{"id":"IL-0241","org":"Stripe","date":"2019-07-10","docs":[],"title":{"en":"A database shard could not elect a new primary, and the Stripe API failed most requests in two periods on one day","ko":"데이터베이스 shard 하나가 primary 를 뽑지 못해 Stripe API 요청 대부분이 두 차례 실패했다"},"domain":["database"],"impact":{"scope":"Stripe API: a substantial majority of requests failed from 16:35 to 17:02 UTC and from 21:14 to 22:47 UTC","users":"Stripe users; those with five or more failed POST requests were sent an email","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":null,"related":[],"sources":[{"url":"https://stripe.com/rcas/2019-07-10","kind":"blog","archived":"https://web.archive.org/web/20260515104051/https://stripe.com/rcas/2019-07-10"}],"summary":{"en":"Stripe splits data by kind into database clusters and by quantity into shards, and each shard has several redundant nodes. Three months before the incident Stripe upgraded its databases to a new minor version after testing in a quality assurance environment and a phased rollout from less critical to more critical clusters. The version ran well in production, through many successful failovers, but it brought a fault in the database's failover system that showed only when several nodes were stalled. On 10 July one shard was in that state when its primary failed, and the shard could not elect a new primary. The team found that the fastest fix, forcing a new election, required restarting the database cluster; after all nodes were restarted at 17:00 UTC an election succeeded, and the API recovered at 17:02 UTC, 27 minutes after the start.\n\nLooking for the root cause, the team found a code path in the new minor version of the election protocol that was likely behind the fault, and at 20:42 UTC rolled all shards of the cluster back to the previous version of the protocol, which took four minutes. At 21:14 UTC shards became unavailable again. The rolled-back election protocol interacted badly with a configuration setting recently introduced on the production shards, and the result was CPU starvation on every affected shard. Once the CPU starvation was seen, the team found the cause, updated the production configuration and restarted the cluster's nodes by 22:34 UTC, then ramped traffic back up, services needed for user-initiated API requests first, and the API fully recovered at 22:47 UTC.","ko":"Stripe 는 데이터를 종류별로 여러 데이터베이스 클러스터에, 양에 따라 여러 shard 에 나눠 두고, shard 마다 이중화한 노드를 여럿 둔다. 석 달 전 Stripe 는 데이터베이스를 새 마이너 버전으로 올렸다. 품질 보증 환경에서 시험하고 덜 중요한 클러스터부터 단계적으로 배포했으며, 석 달 동안 장애 조치도 여러 번 성공했다. 하지만 새 버전에는 멈춘 노드가 여럿 있을 때만 드러나는 장애 조치 결함이 있었다. 사고 나흘 전 노드 두 개가 아직 밝혀지지 않은 이유로 멈췄다. 두 노드는 복제 지연 지표를 내보내지 않으면서도 액티브 헬스 체크에는 정상이라고 응답했다.\n\n2019년 7월 10일 16:35 UTC 에 이 shard 의 primary 노드가 고장 났고, shard 는 새 primary 를 뽑지 못했다. primary 가 없는 shard 는 쓰기를 받지 못했고, 여기에 쓰는 애플리케이션이 타임아웃을 내면서 API 의 컴퓨팅 자원이 바닥났다. 자동 모니터링이 1분 안에 선출 실패를 잡았고 팀은 2분 안에 대응을 시작했다. 처음 보는 장애라 원인을 진단하는 데 시간이 걸렸다. 가장 빨리 복구하는 방법은 새 primary 선출을 강제하는 것이었고, 그러려면 클러스터를 재시작해야 했다. 17:00 에 모든 노드를 재시작하자 선출이 성공했고, 시작 27분 뒤인 17:02 에 API 가 완전히 복구됐다.\n\n20:13 에 팀은 새 마이너 버전의 선출 프로토콜에서 결함을 일으켰을 법한 코드 경로를 찾았다. 20:42 에 영향받은 클러스터의 모든 shard 를 이전의 안정된 선출 프로토콜 버전으로 되돌렸고, 배포는 4분 안에 끝났다. 그런데 되돌린 버전이 운영 shard 에 최근 넣은 설정 변경과 맞물려 21:14 부터 영향받은 shard 전부에서 CPU 가 바닥났고, 두 번째 장애가 시작됐다. 팀은 처음에 같은 문제가 다시 난 줄 알고 첫 번째 복구 절차를 따랐다. CPU 고갈을 보고 나서야 원인을 찾았고, CPU 경합 때문에 설정 변경도 느리게 적용됐다. 22:34 에 새 설정을 배포하고 노드를 재시작해 shard 를 되살렸다. 클러스터가 정상인지 확인한 뒤 사용자가 직접 보낸 API 요청에 필요한 서비스부터 트래픽을 다시 올렸고, 22:47 에 API 가 완전히 복구됐다."},"trigger":{"en":"The primary node of a database cluster failed at 16:35 UTC while one shard had several stalled nodes. The second period was set off by rolling the election protocol back to the previous minor version at 20:42 UTC.","ko":"16:35 UTC 에 데이터베이스 클러스터의 primary 노드가 고장 났다. 그 shard 에는 멈춘 노드가 여럿 있어 석 달 전 마이너 버전 업그레이드로 들어온 장애 조치 결함이 드러났다. 두 번째 장애는 선출 프로토콜을 이전 마이너 버전으로 되돌린 변경 때문에 일어났다."},"severity":"unknown","symptoms":["high-error-rate","failover-failed","writes-rejected","intermittent-timeouts","node-high-cpu"],"timeline":{"detected_after":"1m","resolved_after":"6h 12m"},"date_kind":"incident","detection":{"en":"Automated monitoring detected the failed election within a minute, and the team was alerted at 16:36 UTC. The second period was announced at 21:14 UTC by automated alerts that some shards in the cluster were unavailable.","ko":"첫 장애는 자동 모니터링이 1분 안에 선출 실패를 잡았고, 16:36 UTC 에 팀이 알림을 받아 대응을 시작했다. 두 번째 장애는 21:14 UTC 에 클러스터의 shard 몇 개를 사용할 수 없다는 자동 알림으로 알았다."},"components":["database","failover-manager"],"root_cause":"software-bug","remediation":[{"text":{"en":"Restarted all nodes of the database cluster, which forced a successful primary election","ko":"17:00 UTC 에 데이터베이스 클러스터의 모든 노드를 재시작해 새 primary 선출을 성공시켰다. 17:02 에 Stripe API 가 완전히 복구됐다."}},{"text":{"en":"Updated the production configuration and restarted the database cluster's nodes","ko":"22:34 UTC 에 운영 설정을 고쳐 새로 배포하고 데이터베이스 클러스터의 노드를 재시작해 영향받은 shard 를 되살렸다."}},{"text":{"en":"Ramped traffic back up, putting services for user-initiated API requests first","ko":"클러스터가 정상인지 확인한 뒤 사용자가 직접 보낸 API 요청에 필요한 서비스부터 트래픽을 다시 올렸다. 22:47 UTC 에 완전히 복구됐다."}}],"action_items":[{"text":{"en":"Alert when nodes stop reporting their replication lag","ko":"노드가 복제 지연 지표를 보내지 않으면 알리는 모니터링을 더했다."}},{"text":{"en":"Alert when a shard enters a state that could trigger the election fault","ko":"shard 가 데이터베이스 장애 조치 시스템의 이 선출 결함을 일으킬 수 있는 상태에 들어가면 알리는 모니터링을 더했다."}},{"text":{"en":"Work with the database maintainers on a fix for the underlying fault","ko":"이 결함을 고치는 수정을 데이터베이스 메인테이너와 함께 만든다."}},{"text":{"en":"Add circuit breaking on failed operations to particular database clusters","ko":"특정 클러스터로 가는 작업이 실패하면 끊어 내는 서킷 브레이커를 더 넣는다. 이번 사고의 클러스터도 대상이다."}},{"text":{"en":"Use fault isolation to contain the impact of a single failed shard","ko":"shard 하나가 고장 나도 그 영향을 가두는 장애 격리 기법을 더 도입한다."}},{"text":{"en":"Limit the resources used by clients that keep retrying failed requests","ko":"실패한 요청을 거듭 재시도하는 클라이언트가 쓰는 자원을 제한한다."}},{"text":{"en":"Add procedures and tooling that make rapid configuration changes during incident response safer","ko":"장애 대응 중 운영자가 설정을 빠르게 바꿀 때 더 안전하게 할 수 있도록 절차와 도구를 더 만든다."}}],"contributing":["delayed-effect-change","monitoring-blind-spot","blast-radius-too-wide","manual-action-worsened","assumption-unverified","overlapping-failures"],"recurrence_of":null,"symptoms_text":{"en":"On 10 July 2019 the Stripe API failed a substantial majority of requests twice, from 16:35 to 17:02 UTC and from 21:14 to 22:47 UTC. In the first period automated monitoring detected within a minute that a database cluster had failed to elect a new primary.\n\nThe first period began when the primary node of the database cluster failed and one shard could not elect a new primary. Without a primary the shard accepted no writes, and applications that write to it began to time out. Many applications used this shard, the API among them, so the stalled writes starved the API of compute resources. Four days earlier two nodes had stalled for reasons not yet known: they stopped emitting the metrics that report their replication lag but kept answering active health checks as healthy.\n\nAt 21:14 UTC automated alerts reported that some shards of the cluster were unavailable, including the shard from the first period, and the database cluster showed high CPU usage. The symptoms looked the same as before, so the team at first assumed the same fault had recurred on several shards and followed the playbook that had worked earlier. The second period had a different cause, and CPU resource contention also slowed the configuration change that ended it.","ko":"2019년 7월 10일 16:35 UTC 에 데이터베이스 클러스터의 primary 노드가 고장 났고, 1분 뒤 자동 모니터링이 선출 실패를 잡아 알림을 보냈다. Stripe API 요청 대부분이 실패하기 시작했다.\n\nprimary 가 없는 shard 는 쓰기를 받지 못했고, 그 shard 에 쓰는 애플리케이션들이 타임아웃을 냈다. API 를 포함해 많은 애플리케이션이 이 shard 를 쓰고 있었다. 그래서 shard 하나가 멈추자 API 의 컴퓨팅 자원이 바닥났고, API 전체의 심각한 장애로 번졌다. 16:50 에 팀은 클러스터가 primary 를 뽑지 못한다는 것을 확인했다.\n\n이 장애의 조건은 나흘 전에 이미 만들어져 있었다. 그때 노드 두 개가 멈춰 복제 지연 지표를 내보내지 않았지만, 액티브 헬스 체크에는 계속 정상이라고 응답했다.\n\n21:14 UTC 에 클러스터의 shard 몇 개가 사용할 수 없다는 자동 알림이 다시 울렸고, 데이터베이스 클러스터의 CPU 사용률이 높게 나타났다. 첫 장애 때 문제가 된 shard 도 그 안에 있었다.\n\n증상이 첫 장애와 같아 보여서 팀은 같은 문제가 여러 shard 에서 다시 났다고 여기고 첫 장애 때 통했던 복구 절차를 그대로 따랐다. 실제 원인은 달랐다. 되돌린 선출 프로토콜이 최근 넣은 설정과 맞물려 영향받은 shard 전부에서 CPU 가 바닥나 있었다. CPU 경합 때문에 필요한 설정 변경을 적용하는 일도 느려졌다."},"updated_at":"2026-11-14T12:33:48.921227Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0241","org_slug":"stripe"}