IL-0436 freee · 2025-08-27 (게시일)databaseaws
Aurora MySQL 에서 INSERT 가 supremum 락에 막혀 커넥션이 고갈되고 freee 웹 서비스 전체가 다운되었다
freee 의 한 서비스에서 Aurora MySQL 접속 실패가 잦아지다가 웹 페이지를 열 수 없는 상태로 번졌다. Writer 인스턴스의 CPU 와 메모리는 정상이었지만 BlockedTransactions 와 DatabaseConnections 가 급증해 커넥션이 상한에 닿았다. Database Insights 에서 특정 테이블의 INSERT 가 행 락 대기에 막힌 것을 찾았고, SHOW ENGINE INNODB STATUS 에서 롱 트랜잭션이 Primary Index 의 supremum 에 배타 락을 잡고 있는 것을 확인했다. supremum 은 마지막 레코드 뒤의 갭을 나타내는 가상의 상한값이다. auto_increment 기본 키를 쓰는 테이블의 INSERT 는 이 supremum 에 insert intention lock 을 잡아야 하므로, 모든 INSERT 가 막혔다. 많은 스레드가 이 락이 풀리기를 기다리며 쌓여 커넥션이 고갈되고 서비스가 멈췄다.
문제의 롱 트랜잭션에서 그 테이블에 락을 잡는 쿼리는 INSERT 뿐이었고, 그 밖에는 다른 테이블의 SELECT 와 UPDATE, SAVEPOINT 와 ROLLBACK TO SAVEPOINT 가 있었다. DBRE 팀은 트랜잭션 시작, 아무 읽기 쿼리, SAVEPOINT, INSERT, ROLLBACK TO SAVEPOINT 순서로 supremum 락을 재현했다. INSERT 는 레코드를 넣은 뒤 그 레코드에 암묵적 배타 락을 잡는다. ROLLBACK 은 undo 로 레코드를 지우지만 락은 풀지 않고, 지운 레코드의 락을 다음 갭으로 넘긴다. 그래서 마지막에 추가한 레코드의 락은 supremum 으로 넘어갔다. MySQL 8.0 코드에서도 클러스터드 인덱스에 들어간 행을 undo 할 때 lock_rec_inherit_to_gap() 이 명시적으로 불렸다.
Rails 의 Active Record 에서 ActiveRecord::Base.transaction 에 requires_new: true 를 주면 내부에서 SAVEPOINT 를 쓰고, #create_or_find_by 와 #find_or_create_by 도 내부에서 이를 부른다. 사내에서도 이렇게 SAVEPOINT 롤백을 모르고 쓰는 곳이 여럿 있었다. supremum 락만으로는 문제가 되지 않았지만, 롱 트랜잭션과 겹치면서 행 하나의 롤백이 웹 서비스 전체를 다운시켰다.
- 심각도
- –
- 영향 범위
- A whole freee web service was down: its web pages could not be opened
- 영향 사용자
- –
- 지역
- –
- 감지까지
- –
- 복구까지
- –
증상
Aurora MySQL 에 접속 실패가 잦아지다가 결국 웹 페이지를 열 수 없게 되어 서비스 전체가 다운되었다. 웹 애플리케이션은 DB 가 응답하지 않아 타임아웃이 잇따랐고 새 DB 접속을 맺지 못했다.
Aurora MySQL Writer 인스턴스의 CPUUtilization 과 FreeableMemory 에는 이상이 없어 고부하 상태로 볼 수 없었다. 반면 락 때문에 기다리는 트랜잭션 수인 BlockedTransactions 가 급증했고, DatabaseConnections 가 급증해 상한에 닿았다.
Database Insights 에서 SQL 문을 보니 장애 시각 앞뒤로 특정 테이블의 INSERT 가 wait/synch/cond/innodb/row_lock_wait 를 일으켜 레코드 추가가 막혀 있었다. 기본 키가 AUTO_INCREMENT 값이라, 새 레코드의 INSERT 를 막는 갭 락이 잡히는 일은 보통 생각하기 어려웠다.
SHOW ENGINE INNODB STATUS 의 로그에는 Primary Index 의 supremum 에 배타 락을 잡은 롱 트랜잭션이 있었다. 그 락은 index PRIMARY of table `XXXX`.`XXXX` trx id XXXXXXXXXXXX lock_mode X 줄과 0: len 8; hex XXXXXXXXXXXXXXXX; asc supremum;; 줄로 찍혀 있었다.
원인
- 촉발
- 롱 트랜잭션 안에서 SAVEPOINT 를 만들고 대상 테이블에 INSERT 한 뒤 ROLLBACK TO SAVEPOINT 를 실행했고, 이 롤백이 그 테이블의 supremum 에 배타 락을 남겼다.
감지
–
조치
–
후속 조치
- 원칙적으로 SAVEPOINT 를 쓰지 않는다. 부분 롤백이 필요하면 트랜잭션 자체를 나누고, 꼭 트랜잭션을 중첩해야 하면 애플리케이션 쪽에 타임아웃을 둔다.
- 트랜잭션 격리 수준은 세션 단위로도 정할 수 있다. 그래서 팬텀 행을 허용할 수 있는 경우에는 갭 락을 잡지 않는 READ COMMITTED 를 쓰는 것도 고려하도록 개발자를 교육하고 안내한다.
- 롱 트랜잭션을 감시하고, 회사 전체에서 롱 트랜잭션을 없애는 자발적인 활동을 이어 간다.
교훈
supremum 락 하나로는 문제가 되지 않지만 롱 트랜잭션과 겹치면 서비스 정지로 이어진다. ORM 프레임워크는 중첩 트랜잭션을 만들려고 내부에서 SAVEPOINT 를 쓰는 일이 많아 개발자가 모르고 쓰게 된다. 롱 트랜잭션이 잡은 락은 영향 범위가 좁아 막히는 작업이 적으면 지나치기 쉽지만, 이번 일로 그 잠재 위험이 드러났다.
출처
관련 사례
IL-0451Aurora MySQL 에서 인덱스를 INVISIBLE 로 바꿀 때마다 Reader 3대의 버퍼풀이 무효화돼 CPU 와 ReadIOPS 가 튀었다Daangn · 2026같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0281PostHog US 리전 Logs 제품의 ClickHouse 클러스터가 S3 에 둔 로그 데이터를 거의 다 지워 3일보다 오래된 로그가 사라졌다PostHog · 2026같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0150DynamoDB DNS 자동화의 경쟁 조건이 us-east-1 리전 엔드포인트의 DNS 레코드를 비워 DynamoDB 와 EC2 인스턴스 시작이 실패했고, 그 여파로 EKS, ECS, Fargate 의 컨테이너 시작과 클러스터 스케일링이 14시간 넘게 막혔다Amazon Web Services · 2025같은 원인 · 공통 데이터베이스
IL-0527OpenStreetMap.org 의 복제 diff 생성이 invalid memory alloc request size 오류로 16시간 멈춤OpenStreetMap Foundation · 2025같은 원인 · 공통 데이터베이스
IL-0329Postgres 17 업그레이드 뒤 다시 켠 PGAudit 가 마이그레이션 중 락을 쥔 채 멈춰, 서비스 전반이 느려지고 데이터베이스가 2분 동안 내려갔다incident.io · 2025같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0024무거운 API 요청이 MongoDB 에 과부하를 걸고 취소되지 않은 요청이 쌓여 Hugging Face Hub 가 사흘 동안 세 번 멈췄다Hugging Face · 2024같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0385큰 MySQL 테이블의 스키마 마이그레이션 마지막 단계에서 읽기 복제본 다수가 semaphore deadlock 에 걸려 GitHub 핵심 서비스가 2시간 50분 저하됐다GitHub · 2021같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0196Redis Operator 로 레플리카를 늘리면서 메모리 한도를 낮추자 데이터를 가진 유일한 Redis 파드가 재시작됐고, Sentinel 이 빈 레플리카를 마스터로 올려 데이터가 모두 지워졌다.Palark · 2020같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0388Redis 고가용 클러스터의 primary 노드가 내려간 뒤 Discord API 가 느려지고 길드 일부가 오프라인으로 보이며 접속이 실패했다Discord · 2017같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0436
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0436/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.