IL-0301 37signals · 2022-06-27database
Basecamp 3 의 주 데이터베이스가 정시 부하에 락 경합으로 30초 만에 완전히 막혔다
2022년 6월 27일 13:00 UTC, 30초 사이에 Basecamp 3 의 주 데이터베이스가 완전히 막혔다. 이 시각은 Basecamp 3 가 각종 예약 알림을 대량으로 묶어 보내는 때였고, 부하가 큰 쿼리가 몰리면서 락 경합이 연쇄로 번졌다. 막힌 쿼리 수천 개가 기다리는 동안 새 쿼리 수천 개가 새 연결과 새 실행 스레드를 만들며 같은 락에 쌓였다. 서버 하드웨어가 스레드에 짓눌려 데이터베이스는 사실상 아무 일도 하지 못했다. 37signals 는 원인 하나를 집어낼 수 없었다고 밝혔다. Basecamp 3 가 빠르게 성장하면서 주 데이터베이스가 한계에 점점 다가갔고, 이날 처음으로 그 한계를 넘었다는 설명이다. 지난 1년의 지표를 다시 보니 알림을 울리지 않은 채 주 데이터베이스 부하를 서서히 늘린 성능 저하가 여러 건 있었다.
37signals 는 진행 보고 이메일을 늦추는 식으로 일부 기능을 멈췄지만 데이터베이스는 이미 감당할 수 있는 선을 넘어 있었다. 막힌 쿼리 수천 개를 종료하자 트랜잭션 롤백이 같은 락에서 막히면서 정체가 더 커졌다. 대기 중이던 복제본으로 페일오버하자 서비스가 돌아왔지만, 멈췄던 기능을 다시 켜는 동안 다른 요인이 섞인 같은 종류의 락 경합으로 다시 무너졌다. 그다음 Basecamp 3 를 유지보수 모드로 바꾸고 데이터베이스 쿼리 시간에 강제 상한을 걸자 정체가 풀렸다. 쿼리 시간 상한을 안전장치로 둔 채 기능을 하나씩 다시 켰고, 장애 시작 후 거의 세 시간 뒤인 15:49 UTC 에 정상화를 알렸다.
누적 중단 시간은 103분, 곧 1.7시간이었다. 데이터는 잃지 않았고 HEY, Basecamp 2, Basecamp Classic 을 비롯한 다른 제품은 영향을 받지 않았다.
- 심각도
- –
- 영향 범위
- Basecamp 3 was slow, showed errors and was down for 103 minutes in total between 13:00 and 15:49 UTC; HEY, Basecamp 2 and Basecamp Classic were not affected and no data was lost
- 영향 사용자
- Basecamp 3 users
- 지역
- –
- 감지까지
- –
- 복구까지
- 2h 49m
증상
2022년 6월 27일 13:00 UTC 에 Basecamp 3 가 갑자기 아주 느려졌고 곧 오류 페이지를 보였다. 같은 30초 사이에 Basecamp 3 의 주 데이터베이스가 정상에서 완전한 정체 상태로 넘어갔다.
사용자 쪽에서는 일부 기능이 동작하기는 했지만 응답이 한참 걸렸다. Basecamp 2 와 Basecamp Classic 은 정상이었다. 그 뒤 Basecamp 3 의 모든 기능이 멈췄고, 몇 분 뒤 돌아온 듯하다가 다시 느려지며 오류를 보였다. 15:45 UTC 무렵에야 Basecamp 3 가 돌아와 그 상태를 유지했다.
데이터베이스 안에서는 13:00:00 과 13:00:30 UTC 사이에 락 경합이 연쇄로 번졌다. 수천 개의 쿼리가 락을 기다리며 막혀 있는 동안에도 수천 개의 새 쿼리가 계속 들어왔다. 새 쿼리 다수가 새 데이터베이스 연결을 열었고, 연결마다 새 실행 스레드가 생겨 같은 락에서 다시 막혔다. 서버 하드웨어는 수천 개의 실행 스레드에 짓눌렸다. 데이터베이스가 죽은 것은 아니었지만 동시 작업을 조율하는 비용에 묶여 사실상 아무 일도 진행하지 못했다.
37signals 는 먼저 주 데이터베이스의 부하를 덜려고 Basecamp 3 의 일부 기능을 멈췄지만, 데이터베이스는 이미 감당할 수 있는 선을 한참 넘은 뒤였다. 막힌 쿼리 수천 개를 종료하자 상황이 더 나빠졌다. 쿼리를 종료하면 그 트랜잭션 전체를 롤백해야 하는데, 롤백 자체가 비싼 작업이고 같은 락에서 다시 막혔기 때문이다.
대기 중이던 복제본 데이터베이스로 페일오버하자 Basecamp 3 가 돌아왔다. 37signals 는 조심스럽게 정상화를 알리고 멈췄던 기능을 다시 켰는데, 데이터베이스가 다시 무너졌다. 두 번째에는 다른 요인이 섞여 있었지만 결국 다시 높은 동시성에서 생긴 락 경합이었다.
원인
- 촉발
- 13:00 UTC 에 예약 알림을 대량으로 묶어 보내는 시각에 맞춰 부하가 큰 데이터베이스 쿼리가 한꺼번에 몰려들었고, 이 쿼리들이 주 데이터베이스에서 락 경합을 연쇄로 일으켰다.
감지
37signals 는 13:00:00 과 13:00:30 UTC 사이에 Basecamp 3 의 주 데이터베이스가 완전히 정체되는 것을 보고 곧바로 대응을 시작했다.
조치
- 주 데이터베이스의 부하를 덜려고 Basecamp 3 의 일부 기능을 멈췄다. 진행 보고 이메일을 그날 늦게 보내도록 미룬 것이 그 예다.
- 막힌 주 데이터베이스에서, 이런 장애에 대비해 대기 중이던 복제본 데이터베이스로 절차대로 페일오버했다.
- 페일오버 뒤 데이터베이스가 다시 무너지자 Basecamp 3 를 곧바로 유지보수 모드로 바꿨다.
- 데이터베이스 쿼리 시간에 강제 상한을 걸었다.
- 쿼리 시간 상한을 안전장치로 두고 기능을 한 단계씩 다시 켜서 서비스를 모두 되살렸다.
후속 조치
- 데이터베이스 쿼리에 강제 시간 상한을 둔다. 평소 빠르던 쿼리가 일정 수준 넘게 느려지면 기다리며 쌓이게 두지 않고 실패하게 한다.
- 동시 실행 한도를 조정한다. 일정 수준을 넘으면 병렬로 도는 작업이 많을수록 시스템 전체가 느려지기 때문이다.
- 서비스 수준 기대치를 더 엄격하게 잡고, 예약 알림과 보고서를 몇 분 안에 대량으로 보내며 생기던 정시 데이터베이스 부하 급증을 더는 용인하지 않는다. 제품 설계부터 데이터베이스 내부까지 이 부분을 살핀다.
- 일부 예약 작업을 돌리는 병렬 워커 수를 줄여 동시 실행과 락 경합을 낮춘다.
- 일부 예약 작업을 한꺼번에 끝내지 않고 몇 분에 걸쳐 돌려 데이터베이스 부하를 시간에 나눈다.
- 한꺼번에 몰려드는 쿼리 다수를 주 데이터베이스에서 읽기 전용 복제본 데이터베이스로 옮긴다.
- 지난 1년의 데이터베이스 지표에서 찾은 성능 저하 여러 건을 하나씩 고친다. 이 저하는 알림을 울리지 않았지만 주 데이터베이스 부하를 서서히 늘렸다.
- 한꺼번에 몰려드는 다른 작업을 찾아 분산해, 데이터베이스의 특정 지점에 부하가 몰리지 않게 한다.
- 높은 부하와 높은 동시성에서 더 예측 가능하게 동작하도록 데이터베이스를 조정한다.
- 데이터베이스 내부의 락과 트랜잭션 상태를 더 잘 볼 수 있도록 관측성을 높인다.
- 장애 중에 부하가 큰 Basecamp 3 기능을 골라 끌 수 있는 더 쓰기 편한 도구를 만든다.
- 멈춘 데이터베이스에 대응하는 운영 훈련을 잡아 대응을 연습하고 다듬는다.
- 앞으로 몇 년의 Basecamp 성장에 맞춰 확장할 수 있도록 데이터베이스 소프트웨어와 하드웨어 업그레이드를 검토한다.
- Basecamp 3 를 받치는 구성 요소에 서비스 수준 목표, 곧 SLO 를 정해 데이터베이스 성능 변화를 더 일찍, 더 분명하게 알아챈다.
- 분기마다 성능과 부하를 검토해 눈에 띄지 않게 진행되는 성능 변화를 찾는다.
교훈
37signals 는 이번 장애에 원인 하나가 없었다고 밝혔다. 시스템이 한계를 조금만 넘어도 순식간에 안정에서 불안정으로 넘어가는 지점이 있고, 그 지점은 특별한 계기 없이 성장과 함께 서서히 다가온다. 막힌 쿼리를 종료하는 일은 트랜잭션 롤백 때문에 같은 락에서 다시 막혀 정체를 키울 수 있다. 37signals 는 이 시스템을 잘 알면서도 결정적인 단서가 없고 데이터베이스 내부를 깊이 들여다볼 수단이 없어 확신을 갖고 대응하지 못했다고 썼다. 그래서 문제 하나를 고치는 대신 위험을 줄이고 확신을 쌓아 가는 길을 택했다.
출처
관련 사례
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
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-0301
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0301/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.