{"id":"IL-0211","org":"Mailchimp","date":"2019-02-04","docs":[],"title":{"en":"A Mandrill PostgreSQL shard shut down at transaction ID wraparound and email sending was impaired for about 40 hours","ko":"Mandrill 의 PostgreSQL 샤드 하나가 트랜잭션 ID 랩어라운드를 막으려고 쓰기를 멈춰 약 40시간 동안 메일 발송이 줄었다"},"domain":["database"],"impact":{"scope":"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","users":"Mandrill users; affected users were refunded for purchases made between 1 January and 13 February 2019","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Mailchimp named the factors that slowed its response: knowledge of and access to Mandrill systems and monitoring rested with a few people, Mandrill logging needed a locally writable disk, logs of malformed API requests were hard to separate from real application errors, job health was hard to see, and the disk space problem made partial deployments for triage risky. What helped was trained incident commanders with clear roles (incident commanders in 3-hour rotations, scribes, technical liaisons and points of contact between teams), periodic summaries with a list of recent and open decisions, and taking time in a video meeting to reassess, which led to the truncation. Running in the cloud let the team expand storage quickly, and the team was lucky that the largest table holding up the vacuum held data it could delete.","ko":"Mailchimp 는 대응을 늦춘 요인으로 다섯 가지를 들었다. Mandrill 시스템과 모니터링을 알고 접근할 수 있는 사람이 몇 명뿐이었다. Mandrill 의 로그 기록은 로컬에 쓸 수 있는 디스크에 기댔다. 잘못된 API 요청 로그와 실제 애플리케이션 오류를 구분하기 어려웠다. 작업 상태가 잘 보이지 않았다. 디스크 공간 문제로 원인 분석을 돕는 부분 배포가 위험했다.\n\n잘된 점으로는 인시던트 지휘관을 중심으로 한 긴밀한 조율, 3시간 교대와 인수인계, 기록 담당과 기술 연락 담당 같은 역할 지정, 주기적인 요약과 결정 목록을 들었다. 화상 회의로 다시 모여 상황을 따져 본 것이 테이블을 비우는 해법으로 이어졌다. 대기 메일을 48시간 뒤에 지우는 기본 동작이 다가오자 더 과감한 방법을 검토하게 됐다. Mandrill 이 클라우드에서 돌아 스토리지를 빨리 늘릴 수 있었던 것은 운이었다. 지울 수 있는 데이터가 있었고 그 가운데 VACUUM 을 붙잡던 가장 큰 테이블이 있었던 것도 운이었다."},"related":[],"sources":[{"url":"https://mailchimp.com/what-we-learned-from-the-recent-mandrill-outage/","kind":"blog","archived":"https://web.archive.org/web/20260821124021/https://mailchimp.com/what-we-learned-from-the-recent-mandrill-outage/"}],"summary":{"en":"Every write in PostgreSQL gets a transaction ID (XID) from a 32-bit counter that is compared modulo 2^32, so old XIDs must be cleared by the autovacuum process before about 2 billion newer transactions make them wrap around and look like the future. Mandrill keeps its data in a set of PostgreSQL databases that act as one key-value store sharded by key. The hashing algorithm favoured shard4, which took a higher write load than the other shards. In November 2018 Mandrill engineers had seen XIDs climb to about half the limit at peak load, judged that wraparound was not an immediate threat and put extra monitoring on the backlog. On shard4 autovacuum likely fell behind under the higher load, or it may have failed; Mailchimp could not tell which. At 05:35 UTC on 4 February the XID reached the limit and the database went into safety shutdown mode.\n\nThe first exception was lost among unrelated exceptions, and engineers started investigating at 11:58 UTC, when retries of failed writes had already raised the load on shard4. The wraparound was found at 12:56 UTC and a VACUUM in standalone mode was started at 14:16 UTC. Its runtime was estimated in days or weeks, so a dump and restore without the large Search and Url tables was started alongside it, also too slowly. Failed jobs queued on the app servers' disks, so storage volumes were replaced across the app servers, and the job-queue tables were moved from shard4 to other shards, which helped little.\n\nAt 16:30 UTC on 5 February responders met to look at more radical options. Having established that the app could run without the Search and Url tables, and verified that dropping them would free their XIDs, the team truncated the tables on the locked database at 19:12 UTC. The vacuum finished about an hour later, sending from the backlog began at 21:36 UTC and the outage was announced as resolved at 22:09 UTC. Alerting for XID wraparound was in place by 22:40 UTC, and by 01:00 UTC on 6 February the queues were drained. Mailchimp refunded affected users for Mandrill purchases made between 1 January and 13 February 2019 and credited Mandrill blocks for February.","ko":"Mandrill 은 키로 샤딩한 여러 PostgreSQL 데이터베이스를 하나의 키-값 저장소처럼 쓴다. PostgreSQL 은 쓰기마다 32비트 트랜잭션 ID 인 XID 를 매기고, 오래된 XID 를 autovacuum 으로 정리하지 않으면 약 20억 건 뒤에 번호가 한 바퀴 돌아 과거 트랜잭션이 미래처럼 보이게 된다. 2018년 11월 Mandrill 팀은 최대 부하 때 XID 가 한계의 절반쯤까지 오르는 것을 보고 랩어라운드 가능성을 알아챘지만, 당장 위협은 아니라고 보고 모니터링 추가를 백로그에 넣었다. 해시 알고리즘이 shard4 에 부하를 몰아 이 샤드의 autovacuum 이 밀렸거나 실패했고, 어느 쪽인지는 확인하지 못했다. 2019년 2월 4일 05:35 UTC 에 XID 가 상한에 닿아 shard4 가 안전 종료 모드로 들어갔다.\n\n첫 예외는 다른 예외에 묻혀 늦게 발견됐고, 11:58 UTC 에 조사를 시작해 12:56 UTC 에 랩어라운드를 찾았다. 14:16 UTC 에 단일 사용자 모드에서 VACUUM 을 시작했지만 설정을 바꿔도 며칠에서 최대 40일이 걸린다는 추정이 나왔다. 팀은 VACUUM 과 함께 TB 단위인 Search 와 Url 테이블을 뺀 덤프와 복원을 시작했지만 이것도 매우 느렸다. 쓰기가 막히자 실패한 작업과 로그가 앱 서버 디스크를 채워 스토리지 볼륨을 갈아 끼웠다. 작업 대기열 테이블을 다른 샤드로 옮겼지만 효과는 작았다.\n\n2월 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 결제를 영향받은 사용자에게 환불했다."},"trigger":{"en":"At 05:35 UTC on 4 February the transaction ID counter of shard4, the busiest Mandrill PostgreSQL shard, reached its upper limit and PostgreSQL shut the database down to protect against wraparound.","ko":"Mandrill 의 키 해시 알고리즘이 shard4 에 쓰기를 더 많이 몰아 이 샤드의 autovacuum 이 밀렸거나 실패했고, 2019년 2월 4일 05:35 UTC 에 XID 가 상한에 닿아 데이터베이스가 안전 종료 모드로 들어갔다."},"severity":"unknown","symptoms":["writes-rejected","throughput-degraded","uneven-load","node-disk-full"],"timeline":{"detected_after":"6h 23m","resolved_after":"40h 34m"},"date_kind":"incident","detection":{"en":"The first exception was logged overnight but was buried by unrelated exceptions in the centralized error reporting. Mandrill engineers opened an investigation at 11:58 UTC and, while looking for the source of the rising load on shard4, found the wraparound at 12:56 UTC.","ko":"첫 예외는 일요일 밤에 로그에 남았지만 다른 시스템의 관계없는 예외에 묻혀 중앙 오류 보고에서 늦게 발견됐다. 11:58 UTC 에 Mandrill 엔지니어가 조사를 시작했고, shard4 의 부하 급증을 쫓다가 12:56 UTC 에 랩어라운드를 찾았다."},"components":["database","autovacuum"],"root_cause":"capacity","remediation":[{"text":{"en":"Put shard4 into standalone mode and started VACUUM at 14:16 UTC on 4 February","ko":"14:16 UTC 에 shard4 를 단일 사용자 모드로 띄우고 VACUUM 을 시작했다."}},{"text":{"en":"Stopped and restarted the vacuum with settings that made it run faster","ko":"VACUUM 이 며칠 걸릴 것으로 보여 설정을 바꿔 속도를 높인 뒤 다시 시작했다."}},{"text":{"en":"Started a dump and restore of the database without the Search and Url tables while the vacuum ran","ko":"VACUUM 이 도는 동안 데이터베이스 덤프와 복원을 시작했고, 속도를 높이려고 TB 단위인 Search 와 Url 테이블을 뺐다."}},{"text":{"en":"Replaced storage volumes on the Mandrill app servers whose disks were filling with queued jobs and logs","ko":"실패한 작업과 로그로 디스크가 모자라자 Mandrill 앱 서버들의 스토리지 볼륨을 교체했다."}},{"text":{"en":"Moved the job-queue tables from shard4 to other live shards","ko":"shard4 에 있던 작업 대기열 테이블을 살아 있는 다른 샤드로 옮겨 작업이 처리되게 하려 했다."}},{"text":{"en":"Truncated the Search and Url tables on the locked database at 19:12 UTC on 5 February, after which the vacuum finished in about an hour","ko":"잠긴 데이터베이스에서 테이블을 비우면 그 XID 가 풀린다는 것을 확인한 뒤 2월 5일 19:12 UTC 에 Search 와 Url 테이블을 비웠고, VACUUM 이 약 한 시간 뒤에 끝났다."}},{"text":{"en":"Started sending from the backlog at 21:36 UTC","ko":"21:36 UTC 에 밀린 메일 발송을 시작했다."}},{"text":{"en":"Added alerting for XID wraparound by 22:40 UTC","ko":"22:40 UTC 에 XID 랩어라운드 알림을 새로 걸었다."}}],"action_items":[],"contributing":["early-signal-ignored","no-alert-on-limit","alert-noise","retry-storm","monitoring-blind-spot"],"recurrence_of":null,"symptoms_text":{"en":"From 05:35 UTC on 4 February 2019 Mandrill, Mailchimp's transactional email service, sent only 80% of queued emails, because one of its PostgreSQL shards, shard4, stopped accepting writes. When Mandrill engineers opened an investigation at 11:58 UTC, they saw a sharply elevated load on shard4.\n\nThe first exception had been logged overnight, but unrelated exceptions from other systems buried it in the centralized error reporting. The rising load on shard4 came from retries of the failed writes, and while responders looked for its source, they found at 12:56 UTC that shard4 had reached the upper limit of its transaction IDs (XID) and gone into safety shutdown mode.\n\nThe failure spread to the Mandrill app servers. Jobs that had to write rows to shard4 failed and were queued on the app servers' local disks, and those jobs and the logs of their failures filled the disks until every app server was low on space. Logs of malformed API requests were hard to tell apart from real application errors, job health was hard to see, and the low disk space made partial code deployments for triage risky.\n\nThe standard recovery was too slow. VACUUM in standalone mode, started at 14:16 UTC, was estimated to need many days even after it was restarted with settings that sped it up, with a highest estimate of 40 days. A dump and restore of the database without the Search and Url tables, which were terabytes in size while most tables were megabytes, was faster but still far too slow. Moving the job-queue tables kept on shard4 to other shards helped little, because those jobs still had to write other data to shard4. Mandrill deletes queued mail older than 48 hours by default, so time was running out for the backlog.","ko":"2019년 2월 4일 05:35 UTC 부터 Mandrill 이 대기열에 쌓인 메일의 80% 만 보냈다. 첫 예외는 일요일 밤에 로그에 남았지만 다른 시스템의 관계없는 예외에 묻혀 중앙 오류 보고에서 눈에 띄지 않았다.\n\n11:58 UTC 에 Mandrill 엔지니어가 조사를 시작했을 때 shard4 의 부하가 크게 올라 있었다. 실패한 쓰기를 다시 시도하는 요청이 shard4 로 몰린 탓이었고, 대응 팀은 처음에 치솟는 부하의 출처를 찾았다. 한 시간 뒤인 12:56 UTC 에 트랜잭션 ID 랩어라운드를 발견했다.\n\nshard4 는 트랜잭션 ID 인 XID 가 상한에 닿아 안전 종료 모드로 들어가 쓰기를 받지 않았다. shard4 에 행을 써야 하는 작업이 실패해 Mandrill 앱 서버의 디스크에 대기열로 쌓였고, 이 작업들과 실패를 적은 로그 때문에 모든 앱 서버의 디스크 공간이 모자라기 시작했다. 잘못된 API 요청 로그와 실제 애플리케이션 오류를 구분하기 어려웠고, 작업 상태도 잘 보이지 않았다. 디스크 공간이 모자라 원인 분석을 돕는 부분 배포도 위험했다.\n\nshard4 는 일부 작업 대기열을 추적하는 운영 테이블도 담고 있었다. 이 테이블을 살아 있는 다른 샤드로 옮겼지만, 대부분의 작업이 결국 shard4 에 다른 데이터를 써야 했으므로 별 도움이 되지 않았다.\n\n단일 사용자 모드에서 시작한 VACUUM 은 며칠이 걸릴 것으로 보였다. 설정을 바꿔 다시 시작해도 며칠에서 몇 주가 걸린다는 추정이 나왔고, 가장 긴 추정은 40일이었다. VACUUM 이 도는 동안 TB 단위인 Search 와 Url 테이블을 뺀 덤프와 복원을 시작했지만 이것도 너무 느렸다. Mandrill 은 기본으로 48시간이 지난 대기 메일을 지우므로 밀린 메일에 남은 시간이 줄고 있었다."},"updated_at":"2026-11-14T05:53:41.853926Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0211","org_slug":"mailchimp"}