IL-0326 MailPace · 2023-08-11database
MailPace 의 관리형 Postgres 데이터베이스 디스크가 가득 차 데이터베이스가 두 시간 넘게 내려갔다
MailPace 는 매일 도는 Rake cron 작업으로 보존 기간 30일이 지난 데이터를 지웠다. 이 작업이 조용히 실패하면서 데이터가 쌓이고 있었다. 팀은 2023-08-11 13:00 BST 정기 점검에서 데이터베이스가 있어야 할 크기의 약 1.5배인 것을 보고, 정리 작업이 killed 메시지로 끝나는 것을 찾았다. 13:15 에 cron 인스턴스 메모리를 1gb 에서 8gb 로 늘리자 작업은 돌았다. 그러나 destroy_all 은 10개가 넘는 테이블에 걸친 응답과 첨부 파일 같은 연결 레코드까지 지웠다. 계정 하나가 하루에 메일을 수천 통 보낼 수 있었으므로 destroy_all 호출 하나하나가 매우 컸다. 데이터베이스가 빠르게 찼고 13:36 에 모든 데이터베이스 트랜잭션이 실패했다. MailPace 는 Write Ahead Log 때문일 것으로 추정했고, 업스트림 제공자가 정확한 경위를 확인해 주기를 기다리고 있다고 적었다. 데이터베이스 요금제도 처음부터 성장 여유가 모자랐다.
Postgres 가 관리형 서비스였으므로 팀은 13:37 에 업스트림 제공자 지원팀에 연락했다. 13:45 에 6시간 전 오프사이트 백업을 새 데이터베이스 인스턴스에 준비하는 복원 절차를 시작했다. 이 절차는 여러 번 시험해 본 것이었고 약 1시간이 걸렸다. 다만 데이터 일부를 잃고 그동안 장애가 이어지는 방법이었다. 14:30 에 새 데이터베이스로 옮기려던 참에, 제공자가 저장 공간이 99% 찼을 때 자동 알림을 받았고 더 큰 데이터베이스로 옮기는 중이라고 알려 왔다. 제공자 쪽 소통 착오로 지원 엔지니어 두 명이 동시에 작업해 이전이 늦어졌다. 15:25 에 이전 중인 데이터베이스가 읽기 전용이 돼 로그인은 됐지만 메일은 보낼 수 없었다. 15:50 에 더 큰 인스턴스로 옮겨 완전히 복구됐다. 상태 페이지 두 개는 장애를 잡지 못했다.
그 뒤 MailPace 는 백업으로 만든 별도 데이터베이스 인스턴스에서 문제를 재현해 메모리 사용량을 보고 수정안을 시험했다. 운영에서는 고친 쿼리로 오래된 데이터를 하루 단위로 직접 지웠다.
- 심각도
- –
- 영향 범위
- All database transactions failed across MailPace's services and emails were not sent; during the migration users could log in but not send emails
- 영향 사용자
- –
- 지역
- –
- 감지까지
- –
- 복구까지
- 2h 14m
증상
2023-08-11 13:36 BST 에 여러 서비스에서 모든 데이터베이스 트랜잭션이 실패한다는 알림이 들어왔다. 데이터베이스를 다시 시작하려 했지만 공간이 없다며 올라오지 않았다. 메일도 나가지 않았다.
그날 13:00 정기 점검에서 보니, 보낸 메일과 API 정보를 저장하는 데이터베이스가 있어야 할 크기의 약 1.5배였고 용량의 약 80% 를 쓰고 있었다. 30일 보존 기간보다 오래된 데이터가 남아 있었다. 로그를 보니 매일 도는 정리 cron 작업이 끝나지 못하고 killed 메시지를 남기며 종료되고 있었다.
killed 는 보통 메모리 부족을 뜻하므로 13:15 에 cron 인스턴스 메모리를 1gb 에서 8gb 로 늘렸다. 작업은 몇 분 동안 돌다가 갑자기 끝났고, 그 직후 장애가 시작됐다. 데이터를 지우는 중이었고 70% 정도만 쓰던 데이터베이스였기 때문에 공간이 바닥났다는 것을 처음에는 믿기 어려웠다.
상태 페이지 두 개 모두 장애를 잡지 못했다. 데이터베이스가 내려갔는데도 https://app.mailpace.com 이 여전히 200 으로 응답하고 있었기 때문으로 추정했다.
15:25 에 이전 중인 데이터베이스가 읽기 전용 모드가 되자 사용자는 로그인할 수 있었지만 메일은 보낼 수 없었다. 팀은 처음에 완전히 복구됐다고 생각했다가 곧 잘못을 알고 Twitter 에 다시 알렸다.
원인
- 촉발
- 13:15 BST 에 cron 인스턴스 메모리를 1gb 에서 8gb 로 늘려 밀려 있던 보존 기간 정리 작업을 돌렸다. destroy_all 이 연결된 레코드까지 한꺼번에 많이 지우면서 데이터베이스 디스크가 빠르게 찼다.
감지
13:00 BST 정기 점검에서 데이터베이스가 평소보다 큰 것을 먼저 봤다. 13:36 에는 여러 서비스에서 모든 데이터베이스 트랜잭션이 실패한다는 알림이 곧바로 들어왔다. 상태 페이지 두 개는 장애를 잡지 못했다. 업스트림 제공자는 저장 공간이 99% 찼을 때 자동으로 알림을 받았다.
조치
- Postgres 가 관리형 서비스였으므로 13:37 BST 에 곧바로 업스트림 제공자 지원팀에 연락했다.
- 13:40 에 Twitter 와 Mastodon 으로 사용자에게 장애를 알렸다. 상태 페이지가 자동으로 바뀔 것으로 생각했지만 바뀌지 않았다.
- 제공자의 답을 기다리는 동안 13:45 에 전체 장애 복원 절차를 시작해 6시간 전 오프사이트 백업을 새 데이터베이스 인스턴스에 준비했다.
- 15:50 에 제공자의 이전이 끝나 더 큰 데이터베이스 인스턴스로 옮겨졌고 메일이 다시 나갔다.
- 백업에서 시험해 고친 쿼리로 운영의 오래된 데이터를 하루 단위로 직접 지웠다. 그동안 메모리, 데이터베이스 크기, 지표를 가까이 지켜봤다.
후속 조치
- 매일 도는 정리 작업을 in_batches(of: 50) 로 바꿔 한 번에 50건씩만 지우게 했다. ruby 메모리 요구량과 postgres 부하가 줄었다.
- 정리 작업을 하루 한 번이 아니라 네 번 돌게 바꿔 한 번 실행의 영향을 줄였다.
- 성장 여유가 훨씬 많은 더 큰 데이터베이스 인스턴스로 옮겼다.
- 모든 cron 작업에 cron 모니터링을 적용했다.
- 상태 페이지가 헬스 체크 엔드포인트 /health 를 보게 바꿨다. 이 엔드포인트는 데이터베이스 접근을 포함해 여러 가지를 확인하고 하나라도 실패하면 500 오류로 응답한다.
- 자동 장애 조치를 위한 핫 레플리카 데이터베이스를 검토한다.
교훈
장애는 여러 원인이 겹쳐 났다. 오래된 데이터를 지우는 cron 작업이 조용히 실패해 데이터가 쌓였다. 이 작업은 평소 하루치 양이었다면 문제없었겠지만 지나치게 단순하게 짜여 있었다. 데이터베이스 요금제는 처음부터 성장 여유가 부족했다. 상태 페이지는 장애를 잡지 못했다. MailPace 는 중요한 서비스마다 제공자를 여럿 두라고 권했다. 또 장애를 감지해 메일을 예비 제공자로 보내는 오픈 소스 아웃바운드 SMTP 프록시 Outgoer 를 만들고 있다고 밝혔다.
출처
참고 문서
관련 사례
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
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-0211Mandrill 의 PostgreSQL 샤드 하나가 트랜잭션 ID 랩어라운드를 막으려고 쓰기를 멈춰 약 40시간 동안 메일 발송이 줄었다Mailchimp · 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-0326
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0326/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.