IL-0498 Clerk · 2025-09-14databasegcp
Postgres 데이터베이스 부하가 15분마다 치솟아 나흘 동안 요청 실패와 지연 급증이 간헐적으로 이어졌다
2025년 9월 14일 05:30 UTC에 클라우드 사업자가 Clerk 의 Postgres 를 마이너 버전으로 자동 업그레이드했다. 이전 버전의 잠금 관리자는 새 연결을 연결 수의 제곱에 비례하는 시간 복잡도로 허가해, 처리하는 연결이 많을수록 연결 요청 하나하나가 느려졌다. 이 비효율이 뜻하지 않게 새 연결 생성 속도를 늦추는 속도 제한 구실을 했다. 업그레이드 뒤에는 이 병목이 사라져 많은 새 연결이 거의 한꺼번에 허가됐다.
Clerk 는 Cloud Run 컨테이너를 많이 쓰고, 컨테이너마다 연결 풀러의 최대 연결 수명이 15분으로 고정돼 있어 연결이 15분마다 닫히고 새로 만들어졌다. 예전에는 새 연결이 천천히 만들어져 만료가 시간에 걸쳐 흩어졌지만, 업그레이드 뒤에는 만료된 연결이 한꺼번에 다시 만들어졌다. 배포 직후에는 컨테이너 시작 시각이 흩어져 있었지만, 데이터베이스에 백프레셔가 생길 때마다 컨테이너들의 연결 재생성 시각이 점점 맞춰졌다. 결국 15분마다 모든 컨테이너가 연결을 동시에 새로 만들었고, 이 효과가 15분마다 쌓이면서 활성 연결 풀이 고갈돼 고객이 보는 지연 급증으로 이어졌다.
9월 15일 13:08 UTC에 장애가 시작됐다. 팀은 18일까지 쿼리를 최적화하고, 인덱스를 다듬어 전체 재색인을 하고, 데이터베이스 파라미터를 조정했다. 남용 트래픽 차단도 잠시 강화했는데, 이 때문에 정상 요청 일부가 HTTP 429 로 거부됐을 수 있다. 자동 업그레이드를 되돌리는 방안도 알아봤지만 사업자가 데이터베이스 버전을 되돌리려 하지 않았다. 9월 17일에는 수동 마이너 버전 업그레이드와 용량 증설을 했다. 6월 26일 장애 뒤에 한 개선 덕분에 세션은 이번 장애 대부분 동안 동작했다. 장애 중 위험을 줄이려고 건 배포 동결은 오히려 문제를 키웠다. 평소 배포에서는 머신이 차례로 올라오면서 연결 생성 시각이 머신마다 흩어졌는데, 동결로 이 효과가 사라졌기 때문이다.
9월 18일 03:19 UTC에 근본 원인을 찾았고, 연결 재생성 시각이 서로 맞춰지지 않도록 연결 풀링 설정에 지터를 넣었다. 배포 직후 데이터베이스 부하는 장애 전보다 낮아졌고 지연 급증이 사라졌다. 이후 데이터베이스는 장애 중 넣은 성능 개선 덕분에 업그레이드 전보다 나은 상태로 안정됐다.
- 심각도
- –
- 영향 범위
- Clerk customer traffic: intermittent significant request failures and latency spikes; for 3 to 6 minutes on 17 September new sign ins, sign ups and other account management failed, while user sessions stayed active
- 영향 사용자
- –
- 지역
- –
- 감지까지
- 0m
- 복구까지
- 62h 11m
증상
2025년 9월 15일 13:08 UTC에 내부 모니터링이 요청 실패가 크게 늘고 지연이 늘었다고 알렸다. 그 뒤 9월 18일까지 고객 트래픽에서 요청 실패와 지연 급증이 간헐적으로 이어졌다.
그 전날인 9월 14일 05:30 UTC에 클라우드 사업자가 데이터베이스를 마이너 버전으로 자동 업그레이드했다. 직후 내부 모니터링은 데이터베이스 부하가 늘어난 것을 잡았지만 아직 견딜 만한 수준이었고 지연에는 영향이 없었다.
9월 15일 14:09 UTC에 요청 지연은 정상으로 돌아왔지만 데이터베이스 상태는 계속 나빴다. 14:58 UTC부터 15:16 UTC까지 다시 요청 실패와 지연 급증이 있었다. 9월 17일 00:22 UTC에 새 쿼리 최적화를 담은 배포 뒤 데이터베이스 부하 급증이 사라진 듯했지만 01:00 UTC에 다시 나타났다. 이번에는 급증이 대략 15분마다 주기적으로 나타났다. 이 동안 세션 관리는 세션 엔드포인트가 지연을 더 잘 견디고 SDK 가 재시도한 덕분에 동작했다.
9월 17일 07:15 UTC부터 08:10 UTC까지 수동 마이너 버전 업그레이드와 용량 증설을 하던 중 3~6분 동안 새 로그인, 가입, 그 밖의 계정 관리 작업이 실패했다. 사용자 세션은 내내 살아 있었다. 같은 날 15:07 UTC부터 15:14 UTC까지 다시 요청 실패와 지연 급증이 있었고, 15분마다 치솟는 데이터베이스 부하가 또 보였다.
데이터베이스 연결 지표는 60초 간격으로 수집됐지만 연결 재생성은 15분마다 몇 초 안에 일어났다. 대시보드에는 활성 연결이 가끔 치솟는 모습만 보였고, 팀은 급증이 규칙적이지 않고 드문드문하다고 보고 이를 시작 원인이 아니라 밑에 깔린 부하의 증상으로 다뤘다.
같은 시기에 팀은 일부 고객을 노린 대량의 사기 가입을 막고 있었다. 이 공격은 약 10분마다 부하 급증을 만들어 데이터베이스의 15분 주기와 겹쳤고, 팀은 처음에 이 공격을 지연 급증의 주원인으로 의심했다. 10분, 15분, 30분 간격으로 도는 백그라운드 작업과 예약 쿼리도 급증 시각과 우연히 맞아떨어져 조사의 방향을 흐렸다.
지연 급증은 Frontend API 에서 보였지만, 같은 연결 풀링 로직을 쓰는 Backend API 에서는 꾸준히 보이지 않았다. 이 차이 때문에 문제가 시스템 전체의 연결 풀링이 아니라 일부 부하나 쿼리 비효율로 보였다.
원인
- 촉발
- 2025년 9월 14일 클라우드 사업자가 Clerk 의 Postgres 데이터베이스를 마이너 버전으로 자동 업그레이드했다. 이 업그레이드에는 Postgres 가 연결 잠금을 다루는 방식의 큰 내부 변경이 들어 있었다.
감지
9월 14일 업그레이드 직후 내부 모니터링이 데이터베이스 부하 증가를 잡았지만 견딜 만한 수준이었고 지연에는 영향이 없었다. 9월 15일 13:08 UTC에 내부 모니터링이 요청 실패가 크게 늘고 지연이 늘었다고 알렸다.
조치
- 비용이 큰 쿼리 여러 개를 최적화해 데이터베이스의 기본 부하를 영구히 줄였다. 주요 인덱스를 다듬고 전체 재색인을 해 데이터베이스 오버헤드도 줄였다.
- 안정성을 지키려고 남용 트래픽 차단을 잠시 강화했다. 효과는 있었지만 정상 요청 일부가 HTTP 429 로 거부됐을 수 있다.
- 9월 17일 07:15 UTC부터 08:10 UTC까지 수동 마이너 버전 업그레이드를 하고 데이터베이스 용량을 늘렸다. 데이터베이스 상태가 눈에 띄게 나아졌고 지연 급증이 다시 사라진 듯했다.
- 연결 재생성 시각이 서로 맞춰지지 않도록 연결 풀링 전략에 지터를 넣었다. 배포 직후 데이터베이스 부하가 장애 전보다 낮아졌고 지연 급증이 사라졌다.
후속 조치
- 업그레이드와 다운그레이드를 더 통제할 수 있고 지금 사업자보다 성능이 나은 데이터베이스 솔루션을 검토한다.
- 가입과 로그인 인프라를 시스템의 다른 부분에서 떼어 내는 식으로 서비스 격리를 넓힌다.
- 비용이 큰 쿼리 목록을 계속 따라가며 데이터베이스 성능을 더 끌어올린다.
- 애플리케이션 하나를 다른 애플리케이션과 격리하는 테넌트 격리 방법을 검토한다. 이 작업은 샤딩과 멀티테넌트 인프라의 최신 방식을 살펴보는 일이라 다른 개선보다 오래 걸릴 가능성이 크다.
교훈
Clerk 는 돌아보면 원인이 단순해 보이지만 여러 요인이 겹쳐 찾기 어려웠다고 밝혔다. 첫째, 연결 지표를 60초 간격으로 수집해 15분마다 몇 초 안에 일어나는 재생성 주기가 대시보드에 드문드문한 급증으로만 보였다. 둘째, 같은 시기의 사기 가입 공격이 약 10분 주기로 부하를 만들어 원인을 그쪽으로 의심하게 했다. 셋째, 10분, 15분, 30분 간격의 정기 작업과 예약 쿼리가 급증 시각과 맞아떨어져 조사를 흩뜨렸다. 넷째, 같은 연결 풀링 로직을 쓰는 Backend API 에는 급증이 잘 보이지 않았는데, Clerk 는 Backend API 가 관리하는 연결 수가 훨씬 적어 재생성이 맞춰져도 데이터베이스가 감당했기 때문으로 봤다. 다섯째, 장애 중에 건 배포 동결은 평소 배포가 연결 생성 시각을 흩어 주던 효과를 없애 문제를 키웠다. 이 관계가 직관적이지 않아 연결 동기화를 가리키는 가장 분명한 신호가 가려졌다.
출처
관련 사례
같은 원인 · 공통 데이터베이스, 데이터베이스 프록시와 커넥션 풀러
IL-0316밀린 배포가 한꺼번에 몰린 Control Plane 에서 새 버전 PgBouncer 가 CPU 와 메모리를 비정상적으로 많이 써 데이터베이스 연결이 타임아웃됐다Railway · 2025같은 원인 · 공통 데이터베이스, 데이터베이스 프록시와 커넥션 풀러
IL-0308RevenueCat 이 Aurora PostgreSQL 10 에서 14 새 클러스터로 넘어간 직후 쿼리가 극도로 느려져 대시보드와 SDK 엔드포인트가 세 시간 넘게 실패했다RevenueCat · 2022같은 원인 · 공통 데이터베이스, 데이터베이스 프록시와 커넥션 풀러
IL-0572Merpay 회계 이벤트 처리 시스템에서 Cloud Pub/Sub 메시지 1000만 건이 밀려 새 회계 데이터가 하루 넘게 Cloud Spanner에 들어가지 못했다Mercari · 2026같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0444Aurora MySQL 에서 ALTER TABLE 을 실행할 때마다 그 테이블을 reader 로 읽던 커넥션이 끊겨 애플리케이션 오류가 쏟아졌다Repro · 2024같은 원인 · 공통 데이터베이스
IL-0117인그레스 업그레이드가 네트워크 컨트롤 플레인 회귀를 일으켜 데이터 플레인이 잠깐 끊겼고, 한꺼번에 재시작한 서비스들이 백엔드 저장소에 과부하를 일으켜 OpenAI API 오류가 늘었다OpenAI · 2024같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0498
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0498/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.