← 전체 글로 돌아가기

API

PostgreSQL 커넥션 풀이 먼저 고갈된 API 장애를 추적한 과정

CPU와 메모리는 여유로운데 요청이 멈췄던 장애에서 커넥션 풀 대기 시간을 먼저 확인한 기록이다

서버 자원은 정상인데 요청이 늦었다

목록 API가 평소 수백 밀리초 안에 끝나던 날, 몇 분 동안 10초를 넘겼다. CPU와 메모리만 보고 애플리케이션이 느려졌다고 판단했지만 쿼리 자체는 빨랐다. 로그에는 쿼리 시작보다 앞서 긴 공백이 있었다.

쿼리 시간과 대기 시간을 분리했다

커넥션을 얻기 전후 시간을 기록하니 풀에서 기다리는 시간이 길었다.

const started = performance.now()
const client = await pool.connect()
logger.info({ waitMs: performance.now() - started }, 'db connection acquired')
try {
  return await client.query('select id, title from posts order by id desc limit 20')
} finally { client.release() }

원인은 예외 경로에서 release가 빠진 함수였다. 권한 오류 요청을 반복했을 때만 풀이 줄어 정상 응답 테스트로는 발견하지 못했다.

임시 조치와 근본 수정

프로세스를 재시작해 대기 중인 사용자를 풀었지만 풀 크기만 키우지는 않았다. DB 최대 연결 수와 함께 보지 않으면 장애를 늦출 뿐이다. 모든 대여 코드를 try/finally로 감싸고 오류 테스트에서 반환 여부를 확인했다.

select state, count(*) from pg_stat_activity
where datname = current_database() group by state;

재발 방지 메모

  • 대여 지점마다 finally가 있는가
  • 무한 대기가 아닌 타임아웃이 있는가
  • 애플리케이션 풀 합계가 DB 허용치 안인가
  • 오류 뒤 연결 수가 회복되는가

DB가 빠르다는 판단에는 연결을 얻기까지의 시간도 포함해야 원인을 좁힐 수 있다.