IL-0211 Mailchimp · 2019-02-04database
Mandrill 의 PostgreSQL 샤드 하나가 트랜잭션 ID 랩어라운드를 막으려고 쓰기를 멈춰 약 40시간 동안 메일 발송이 줄었다
Mandrill 은 키로 샤딩한 여러 PostgreSQL 데이터베이스를 하나의 키-값 저장소처럼 쓴다. PostgreSQL 은 쓰기마다 32비트 트랜잭션 ID 인 XID 를 매기고, 오래된 XID 를 autovacuum 으로 정리하지 않으면 약 20억 건 뒤에 번호가 한 바퀴 돌아 과거 트랜잭션이 미래처럼 보이게 된다. 2018년 11월 Mandrill 팀은 최대 부하 때 XID 가 한계의 절반쯤까지 오르는 것을 보고 랩어라운드 가능성을 알아챘지만, 당장 위협은 아니라고 보고 모니터링 추가를 백로그에 넣었다. 해시 알고리즘이 shard4 에 부하를 몰아 이 샤드의 autovacuum 이 밀렸거나 실패했고, 어느 쪽인지는 확인하지 못했다. 2019년 2월 4일 05:35 UTC 에 XID 가 상한에 닿아 shard4 가 안전 종료 모드로 들어갔다.
첫 예외는 다른 예외에 묻혀 늦게 발견됐고, 11:58 UTC 에 조사를 시작해 12:56 UTC 에 랩어라운드를 찾았다. 14:16 UTC 에 단일 사용자 모드에서 VACUUM 을 시작했지만 설정을 바꿔도 며칠에서 최대 40일이 걸린다는 추정이 나왔다. 팀은 VACUUM 과 함께 TB 단위인 Search 와 Url 테이블을 뺀 덤프와 복원을 시작했지만 이것도 매우 느렸다. 쓰기가 막히자 실패한 작업과 로그가 앱 서버 디스크를 채워 스토리지 볼륨을 갈아 끼웠다. 작업 대기열 테이블을 다른 샤드로 옮겼지만 효과는 작았다.
2월 5일 16:30 UTC 회의에서 Search 와 Url 테이블 없이도 앱이 돈다는 것을 확인했고, 잠긴 데이터베이스에서 두 테이블을 비우면 그 XID 가 풀린다는 것을 검증한 뒤 19:12 UTC 에 테이블을 비웠다. VACUUM 은 약 한 시간 뒤에 끝났다. 21:36 UTC 에 밀린 메일을 보내기 시작했고, 22:09 UTC 에 해결을 알렸으며, 22:40 UTC 에 XID 랩어라운드 알림을 새로 걸었다. 2월 6일 01:00 UTC 에 대기열이 비어 정상으로 돌아왔다. Mailchimp 는 2019년 1월 1일부터 2월 13일까지의 Mandrill 결제를 영향받은 사용자에게 환불했다.
- 심각도
- –
- 영향 범위
- Mandrill email sending: only 80% of queued emails were sent from 05:35 UTC on 4 February until 22:09 UTC on 5 February 2019
- 영향 사용자
- Mandrill users; affected users were refunded for purchases made between 1 January and 13 February 2019
- 지역
- –
- 감지까지
- 6h 23m
- 복구까지
- 40h 34m
증상
2019년 2월 4일 05:35 UTC 부터 Mandrill 이 대기열에 쌓인 메일의 80% 만 보냈다. 첫 예외는 일요일 밤에 로그에 남았지만 다른 시스템의 관계없는 예외에 묻혀 중앙 오류 보고에서 눈에 띄지 않았다.
11:58 UTC 에 Mandrill 엔지니어가 조사를 시작했을 때 shard4 의 부하가 크게 올라 있었다. 실패한 쓰기를 다시 시도하는 요청이 shard4 로 몰린 탓이었고, 대응 팀은 처음에 치솟는 부하의 출처를 찾았다. 한 시간 뒤인 12:56 UTC 에 트랜잭션 ID 랩어라운드를 발견했다.
shard4 는 트랜잭션 ID 인 XID 가 상한에 닿아 안전 종료 모드로 들어가 쓰기를 받지 않았다. shard4 에 행을 써야 하는 작업이 실패해 Mandrill 앱 서버의 디스크에 대기열로 쌓였고, 이 작업들과 실패를 적은 로그 때문에 모든 앱 서버의 디스크 공간이 모자라기 시작했다. 잘못된 API 요청 로그와 실제 애플리케이션 오류를 구분하기 어려웠고, 작업 상태도 잘 보이지 않았다. 디스크 공간이 모자라 원인 분석을 돕는 부분 배포도 위험했다.
shard4 는 일부 작업 대기열을 추적하는 운영 테이블도 담고 있었다. 이 테이블을 살아 있는 다른 샤드로 옮겼지만, 대부분의 작업이 결국 shard4 에 다른 데이터를 써야 했으므로 별 도움이 되지 않았다.
단일 사용자 모드에서 시작한 VACUUM 은 며칠이 걸릴 것으로 보였다. 설정을 바꿔 다시 시작해도 며칠에서 몇 주가 걸린다는 추정이 나왔고, 가장 긴 추정은 40일이었다. VACUUM 이 도는 동안 TB 단위인 Search 와 Url 테이블을 뺀 덤프와 복원을 시작했지만 이것도 너무 느렸다. Mandrill 은 기본으로 48시간이 지난 대기 메일을 지우므로 밀린 메일에 남은 시간이 줄고 있었다.
원인
- 촉발
- Mandrill 의 키 해시 알고리즘이 shard4 에 쓰기를 더 많이 몰아 이 샤드의 autovacuum 이 밀렸거나 실패했고, 2019년 2월 4일 05:35 UTC 에 XID 가 상한에 닿아 데이터베이스가 안전 종료 모드로 들어갔다.
감지
첫 예외는 일요일 밤에 로그에 남았지만 다른 시스템의 관계없는 예외에 묻혀 중앙 오류 보고에서 늦게 발견됐다. 11:58 UTC 에 Mandrill 엔지니어가 조사를 시작했고, shard4 의 부하 급증을 쫓다가 12:56 UTC 에 랩어라운드를 찾았다.
조치
- 14:16 UTC 에 shard4 를 단일 사용자 모드로 띄우고 VACUUM 을 시작했다.
- VACUUM 이 며칠 걸릴 것으로 보여 설정을 바꿔 속도를 높인 뒤 다시 시작했다.
- VACUUM 이 도는 동안 데이터베이스 덤프와 복원을 시작했고, 속도를 높이려고 TB 단위인 Search 와 Url 테이블을 뺐다.
- 실패한 작업과 로그로 디스크가 모자라자 Mandrill 앱 서버들의 스토리지 볼륨을 교체했다.
- shard4 에 있던 작업 대기열 테이블을 살아 있는 다른 샤드로 옮겨 작업이 처리되게 하려 했다.
- 잠긴 데이터베이스에서 테이블을 비우면 그 XID 가 풀린다는 것을 확인한 뒤 2월 5일 19:12 UTC 에 Search 와 Url 테이블을 비웠고, VACUUM 이 약 한 시간 뒤에 끝났다.
- 21:36 UTC 에 밀린 메일 발송을 시작했다.
- 22:40 UTC 에 XID 랩어라운드 알림을 새로 걸었다.
후속 조치
–
교훈
Mailchimp 는 대응을 늦춘 요인으로 다섯 가지를 들었다. Mandrill 시스템과 모니터링을 알고 접근할 수 있는 사람이 몇 명뿐이었다. Mandrill 의 로그 기록은 로컬에 쓸 수 있는 디스크에 기댔다. 잘못된 API 요청 로그와 실제 애플리케이션 오류를 구분하기 어려웠다. 작업 상태가 잘 보이지 않았다. 디스크 공간 문제로 원인 분석을 돕는 부분 배포가 위험했다.
잘된 점으로는 인시던트 지휘관을 중심으로 한 긴밀한 조율, 3시간 교대와 인수인계, 기록 담당과 기술 연락 담당 같은 역할 지정, 주기적인 요약과 결정 목록을 들었다. 화상 회의로 다시 모여 상황을 따져 본 것이 테이블을 비우는 해법으로 이어졌다. 대기 메일을 48시간 뒤에 지우는 기본 동작이 다가오자 더 과감한 방법을 검토하게 됐다. Mandrill 이 클라우드에서 돌아 스토리지를 빨리 늘릴 수 있었던 것은 운이었다. 지울 수 있는 데이터가 있었고 그 가운데 VACUUM 을 붙잡던 가장 큰 테이블이 있었던 것도 운이었다.
출처
관련 사례
같은 원인 · 공통 PostgreSQL vacuum, 데이터베이스
같은 원인 · 공통 PostgreSQL vacuum, 데이터베이스
같은 원인 · 공통 PostgreSQL vacuum, 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0288CDN77 의 ClickHouse 데이터 파이프라인에서 이중화된 인스턴스 세 곳의 ZooKeeperWait 가 수천 초로 치솟아 복제가 한 시간 넘게 느려졌다CDN77 · 2026같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0305Intercom 의 PlanetScale 샤드 하나에서 복제본이 CPU 100% 로 주 데이터베이스를 따라가지 못해 받은편지함에 최대 2.2시간 전의 데이터가 보였다Intercom · 2026같은 원인 · 공통 데이터베이스
IL-0390PostHog US Cloud 의 Persons Postgres 데이터베이스 쓰기가 크게 느려져 이벤트와 persons 수집이 최대 2일 지연됐다PostHog · 2025같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0287Atlas 와 Cloud Manager 의 모니터링을 받치는 내부 데이터베이스 클러스터의 샤드 하나가 과부하되어 지표 수집과 클러스터 작업이 늦어졌다MongoDB · 2025같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0298Wikimedia sessionstore 의 Cassandra 노드가 디스크가 가득 차 잇따라 죽고 쿼럼을 잃어 편집이 실패했다Wikimedia Foundation · 2025같은 원인 · 공통 데이터베이스
IL-0312max_connections 를 올리려고 재시작한 주 PostgreSQL 데이터베이스가 다시 올라오지 않아 Gadget 의 모든 앱 요청이 멈췄다Gadget · 2024같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0110Grafana Agent 파드가 메모리를 많이 쓰던 노드에 Hangfire 파드가 함께 올라가 시스템 OOM 으로 CrashLoopBackOff 를 반복했고, 노드가 응답을 멈추고 NotReady 가 됐다.DFDS · 2024같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0202갑자기 몰린 쿼리와 데이터베이스 쓰기로 Honeycomb 수집 호스트가 메모리 부족으로 죽었고, 쿠버네티스 CrashLoopBackOff 가 용량 회복을 막아 US1 이벤트 수집이 한 시간 넘게 불안정했다Honeycomb · 2023 · sev1같은 원인 · 공통 데이터베이스
IL-0386Google Cloud 로 옮긴 뒤 GKE 노드에서 Postgres 연결 타임아웃과 memcached i/o timeout 이 간헐적으로 튀었다incident.io · 2023같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0206GKE 파드 안의 Consul 에이전트가 메모리 급증으로 거듭 OOM kill 되어 Rails 파드가 데이터베이스 복제본을 찾지 못했고, 모든 쿼리가 primary 로 몰려 GitLab.com 이 느려졌다.GitLab · 2022같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0496전역 복제 Cassandra 클러스터에서 모든 리전의 인스턴스 CPU 가 100% 로 치솟아 쿼리가 타임아웃됐고, 같은 부하가 이틀 저녁 더 찾아왔다Ably · 2020같은 원인 · 공통 데이터베이스
IL-0443Aurora MySQL reader 가 이유 없이 재시작한 뒤 writer 의 insert latency 가 7배쯤 오르고 replication lag 가 하루 넘게 이어졌다Repro · 2019같은 원인 · 공통 데이터베이스
IL-0037imagePullPolicy가 Always인 DaemonSet이 크래시 루프를 돌며 이미지를 계속 받아, NAT 세 대를 같이 쓰던 리전 전체가 이미지 레지스트리에서 429로 막혔다Datadog · 2019같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0064CPU 를 많이 쓰는 파드가 Google Cloud 노드 하나에 몰려 노드가 커널 패닉으로 죽었고, 다시 배치된 파드가 다음 노드를 또 죽이며 웹사이트와 API 가 멈췄다.Moonlight · 2019같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0334RDS MySQL 의 bookmarks 테이블이 2TB 파일 크기 한도에 닿아 INSERT 가 실패하고 Instapaper 가 31시간 동안 멈췄다Instapaper · 2017같은 원인 · 공통 데이터베이스
IL-0333사양을 낮춘 RDS PostgreSQL 인스턴스가 피크 부하를 못 버텨 헬스 체크가 실패하고 Buildkite 대시보드가 몇 시간 동안 내려갔다Buildkite · 2016같은 원인 · 공통 데이터베이스
IL-0282US-East 리전의 DynamoDB 스토리지 서버들이 멤버십 정보를 받지 못해 스스로 요청 처리에서 빠지면서 고객 요청의 약 55% 가 실패했다Amazon Web Services · 2015같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0211
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0211/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.