node-postgres(pg)로 Express API 짰는데요, 평소엔 멀쩡하다가 트래픽이 조금만 몰리면 이 에러가 나면서 그 뒤 요청들이 전부 먹통이 됩니다.
error: timeout exceeded when trying to acquire a connection
at .../node_modules/pg-pool/index.js:...pool 사이즈(max)를 10에서 30으로 올려봤는데 그냥 터지는 시점만 조금 늦춰질 뿐 결국 똑같이 멈춰요. 재시작하면 잠깐 괜찮아지고요. DB 서버 자체는 CPU도 커넥션 수도 여유가 있습니다. 코드에서 뭘 잘못하고 있는 걸까요?
댓글 4
제일 흔한 범인은 에러 경로에서 release가 빠진 겁니다.
const client = await pool.connect()
const r = await client.query(...) // 여기서 throw나면
client.release() // 이 줄을 못 지나감 → 누수
이렇게 짜면 쿼리가 실패할 때마다 커넥션이 하나씩 샙니다. 반드시 try/finally로 release를 보장하세요.
const client = await pool.connect()
try {
const r = await client.query(...)
return r.rows
} finally {
client.release()
}
finally에 넣으면 성공이든 에러든 무조건 반납됩니다.
추가로 볼 것들. 트랜잭션 경로는 특히 위험해요. BEGIN 해놓고 중간에 에러 나서 ROLLBACK도 release도 못 하고 빠지는 케이스. 트랜잭션 헬퍼를 하나 만들어 finally에서 release, catch에서 ROLLBACK을 강제하세요. 그리고 단순 쿼리 하나만 할 거면 굳이 pool.connect()로 빌리지 말고 pool.query()를 쓰세요. pool.query는 알아서 빌리고 알아서 반납해서 누수 자체가 안 생깁니다. connect()로 직접 빌리는 건 트랜잭션처럼 한 커넥션을 여러 쿼리에 걸쳐 써야 할 때만요.
범인 찾는 법: pool에 totalCount, idleCount, waitingCount가 있어요. 주기적으로 로그로 찍어보면, 누수가 있으면 totalCount는 max에 붙어 있는데 idleCount가 0이고 waitingCount만 계속 쌓이는 그림이 보입니다. 그러면 누수 확정이에요.