Incident Library

IL-0497 Buttondown · 2026-03-31database

Postgres 가 설정된 최대 연결 수에 닿아 하루에 두 번 새 요청이 연결을 얻지 못하고 실패했다

2026년 3월 31일 Buttondown 은 약 7분과 약 13분, 두 번 멈췄다. 두 번 모두 원인은 같았다. Postgres 가 받도록 설정된 연결 수의 상한에 닿았고, 그 뒤 새 요청은 연결을 얻지 못하고 실패했다. 쿼리 속도, CPU, 메모리, 복제 지연은 모두 정상이었다. 이 상한은 오래전에 정한 뒤 트래픽 패턴이 바뀌는 동안 한 번도 다시 보지 않은 값이었다.

헬스 체크 엔드포인트는 데이터베이스 연결을 확인하지 않고 늘 200 으로 응답했고, 헬스 체커의 요청은 이미 연결을 가진 워커에 닿아 대부분 성공했다. 그래서 자동 알림은 울리지 않았고, 팀은 사용자 신고와 직접 관찰로 장애를 알았다.

첫 번째 장애에서 팀은 연결 고갈을 확인하고 실행 중인 쿼리를 죽여 연결을 풀었고, 장애는 약 7분 만에 끝났다. 두 번째 장애에서는 연결이 완전히 차서 쿼리를 죽이려고 접속할 수도 없었다. 팀은 실행 중인 쿼리를 강제로 끝내려고 데이터베이스를 재시작했고, 장애는 약 13분 이어졌다.

심각도
–
영향 범위
Buttondown's service: requests that needed a database connection failed, in two outages of about seven and about thirteen minutes
영향 사용자
–
지역
–
감지까지
–
복구까지
–

증상

2026년 3월 31일 Buttondown 이 두 번 멈췄다. 약 7분과 약 13분 동안 새 요청이 데이터베이스 연결을 얻지 못해 실패했고, 팀은 자동 알림이 아니라 사용자 신고와 직접 관찰로 장애를 알았다.

데이터베이스 자체에는 문제가 없었다. 쿼리는 빨랐고 CPU 와 메모리도 문제가 없었으며 복제 지연도 정상 범위였다. 문제는 데이터베이스가 받도록 설정된 연결 수의 상한에 닿은 것이었다.

헬스 체커는 대체로 정상으로 보고했다. 헬스 체크 엔드포인트는 데이터베이스 연결을 확인하지 않고 늘 200 으로 응답했고, 헬스 체커의 요청은 마침 연결을 이미 쥐고 있던 워커에 닿아 대부분 성공했다. 외부 모니터링의 요청도 알림이 울리지 않을 만큼은 통과했다.

두 번째 장애 때는 연결이 완전히 차서 쿼리를 죽이려고 데이터베이스에 접속하는 것조차 할 수 없었다. 첫 번째 장애를 해결한 도구를 쓰려면 바로 그 고갈된 연결이 필요했다.

증상 커넥션 풀 고갈서비스 중단

원인

촉발
–

감지

팀은 자동 시스템이 아니라 사용자 신고와 직접 관찰로 장애를 알았다. 헬스 체커는 대체로 정상으로 보고했고 외부 모니터링도 알림을 내지 않았다.

조치

후속 조치

교훈

관리용 연결을 하나라도 예약해 뒀다면 두 번째 장애에서도 첫 번째처럼 쿼리를 죽일 수 있었다. 두 번째 장애가 첫 번째보다 거의 두 배 길었던 것은 복구에 쓰던 수단 자체를 쓸 수 없었기 때문이었다. 가장 흔한 장애를 잡지 못하는 헬스 체크는 헬스 체크 구실을 하지 못한다. 오래전에 정하고 다시 보지 않은 연결 상한은 트래픽이 바뀌면서 서서히 모자라게 됐다.

출처

관련 사례

내 클러스터에도 있는지 확인하기

kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이 사례와 같은 상태를 점검합니다. 이 사례의 구성 요소: 데이터베이스. 점검 항목의 근거는 이 사례 같은 공개 보고서입니다.

AI 도구와 스크립트용

API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.