fix(db): 커넥션 풀 고갈의 범인을 이름으로 남긴다 — leak detection + 풀 상향 - #312
Merged
Conversation
dev 에서 auth/refresh 와 approvals 가 간헐 500 을 내고 FE 세션 로그인이 풀렸다. 조사해 보니 두 엔드포인트의 버그가 아니라 <b>HikariCP 커넥션 풀 고갈</b>이었다. DB 커넥션이 필요한 모든 것이 함께 죽는다 — 스케줄 워커 전부(findExpirable · recoverExpiredLeases · findByStatus · findStalePendingTaskIds …)가 같은 시간대에 실패했다. HikariPool-1 - Connection is not available, request timed out after 30000ms (total=10, active=10, idle=0, waiting=0) 01:52:02 부터 22 분간 끊이지 않고 이어졌고 앱 재기동으로만 풀렸다. <b>순간 폭주면 저절로 풀린다</b> — 반납되지 않은 커넥션이 있었다는 뜻이다. 첫 희생자가 agent-event 스레드였고, 그 시각 SSE 스트림이 비동기 인가에서 거부되고 있었다(#309 에서 고친 것)는 점은 정황이지만, 지금 로그만으로는 어느 코드가 커넥션을 쥐었는지 특정할 수 없다. 특정할 수 없다는 것 자체가 고칠 지점이다. - leak-detection-threshold 60s: 임계를 넘으면 그 커넥션을 <b>빌려간 지점의 스택</b>이 WARN 으로 찍힌다. 반납을 막지는 않으므로 동작은 그대로고, 다음 발생 때는 범인이 이름으로 남는다. - maximum-pool-size 10 → 20: 기본값 10 은 에이전트 동시 실행을 4 로 올린 뒤(#311) 빠듯하다. MySQL 기본 max_connections 는 151 이라 여유가 있다. 다만 이건 완화지 해결이 아니다 — 누수가 있으면 고갈까지 걸리는 시간만 늘린다. - 둘 다 환경변수로 뺐다(DB_LEAK_DETECTION_THRESHOLD_MS · DB_MAX_POOL_SIZE). dev·prod 양쪽에 넣었다. 다음 고갈 때 WARN 스택을 잡아 근본 원인을 따로 고친다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013y8USoCXTsRTATAhy88M93
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
실제 원인: HikariCP 커넥션 풀 고갈
DB 커넥션이 필요한 모든 것이 함께 죽는다. 같은 시간대에 스케줄 워커가 전부 실패했다 —
findExpirable·recoverExpiredLeases·findByStatus·findStalePendingTaskIds…FE 가 본 것들이 전부 이 하나의 증상이었다:
POST /auth/refresh→ 500 (→ 로그인 풀림)GET /projects/{id}/approvals→ 간헐 500누수다, 폭주가 아니다
01:52:02 부터 22 분간 끊이지 않고 이어졌고 앱 재기동으로만 풀렸다(타임스탬프 있는 줄 기준 120 건, 매 분 2 건씩 균일).
순간 폭주면 저절로 풀린다. 반납되지 않은 커넥션이 있었다는 뜻이다.
정황: 첫 희생자가
agent-event스레드였고, 그 시각 SSE 스트림이 비동기 인가에서 거부되고 있었다(#309 에서 고침). 다만 지금 로그만으로는 어느 코드가 커넥션을 쥐었는지 특정할 수 없다.특정할 수 없다는 것 자체가 고칠 지점
leak-detection-threshold: 60s— 임계를 넘으면 그 커넥션을 빌려간 지점의 스택이 WARN 으로 찍힌다. 반납을 막지는 않으므로 동작은 그대로고, 다음 발생 때 범인이 이름으로 남는다.maximum-pool-size: 10 → 20— 기본값 10 은 에이전트 동시 실행을 4 로 올린 뒤(fix(agent): 에이전트 동시 실행을 2 → 4 로 — max5 는 도달할 수 없는 값이었다 #311) 빠듯하다. MySQL 기본max_connections는 151 이라 여유가 있다.다만 이건 완화지 해결이 아니다 — 누수가 있으면 고갈까지 걸리는 시간만 늘린다. 근본 원인은 다음 WARN 스택을 잡아 따로 고친다.
둘 다 환경변수로 뺐다(
DB_LEAK_DETECTION_THRESHOLD_MS·DB_MAX_POOL_SIZE). dev·prod 양쪽 적용.검증
전체 테스트 통과. 다음 고갈 때 WARN 스택이 실제로 찍히는지가 이 PR 의 진짜 검증이다.
🤖 Generated with Claude Code
https://claude.ai/code/session_013y8USoCXTsRTATAhy88M93