Python에서 DB connection이 오래 열린 채 남으면 pool exhaustion, 느린 요청, hanging job처럼 보일 수 있지만, 실제 핵심은 connection lifetime입니다. cleanup 경로, 긴 transaction, 특정 코드 경로의 release 누락이 보통 더 중요합니다.
그래서 “connection이 안 닫힌다”는 말은 종종 너무 넓습니다. 어떤 경우에는 진짜 leak이고, 어떤 경우에는 leak은 아니지만 너무 오래 잡혀 있어서 pool 입장에서는 leak처럼 보입니다.
이 글은 실전 순서에 집중합니다.
- leaked connection과 long-held connection을 어떻게 구분할지
- cleanup flow, session scope, pool pressure에서 무엇을 먼저 볼지
- request path와 background job이 DB 자원을 어떻게 오래 붙잡는지
짧게 말하면 connection이 진짜 새는지, 아니면 너무 오래 잡히는지 먼저 나누고, 그다음 cleanup과 exception 경로를 보고, 마지막에 pool 압박과 요청/작업 동시성을 비교하는 편이 빠릅니다.
더 넓은 Python 분기부터 다시 보고 싶다면 Python 트러블슈팅 가이드로 가세요.
먼저 connection lifetime부터 보기
가장 빠른 분기는 단순합니다. connection이 새는 건지, 아니면 예상보다 오래 점유되는 건지입니다.
이 차이에 따라 해결 방향이 완전히 달라집니다.
예를 들어 아래 장애를 나눠줍니다.
- 빌린 connection이 돌아오지 않음
- transaction이 애플리케이션 로직 전체를 너무 오래 감쌈
- background job이 session을 조용히 오래 잡고 있음
이 첫 분기 없이 보면 pool size를 먼저 의심하다가 실제 ownership과 cleanup 문제를 놓치기 쉽습니다.
leak과 long-held connection은 왜 다른가
겉으로는 두 패턴이 거의 비슷하게 보일 수 있습니다.
- active connection이 높게 유지됨
- waiting caller가 늘어남
- 요청이 느려짐
- job이 pool 뒤에서 멈춤
하지만 같은 운영 문제는 아닙니다.
더 중요한 질문은 “pool이 exhausted인가?”보다 “왜 connection이 팀 예상보다 오래 unavailable 상태인가?”입니다.
자주 나오는 원인
1. cleanup 경로가 비어 있다
모든 코드 경로에서 connection이나 session이 닫히지 않을 수 있습니다.
이 패턴은 보통 아래에서 자주 나옵니다.
- 성공 경로에서는 닫지만 exception path에서는 닫지 않음
- 특정
return경로가 cleanup을 건너뜀 - helper 함수 안에서 ownership이 숨겨져 caller가 다른 쪽에서 정리했을 거라 가정함
이 경우 pool은 빌려간 자원이 제때 돌아오지 않아 서서히 바닥납니다.
2. transaction이 너무 오래 열린다
connection이 leak은 아니지만, 의도보다 너무 오래 unavailable 상태일 수 있습니다.
예를 들면:
- transaction을 너무 일찍 열음
- transaction을 잡은 채 네트워크나 CPU 작업을 함
- commit/rollback 전에 downstream 코드를 오래 기다림
이 경로는 “DB가 느리다”처럼 보이지만, 실제 더 큰 문제는 애플리케이션이 connection을 오래 붙잡는다는 점입니다.
3. background job이나 worker가 connection을 오래 잡는다
장수 task, worker, script가 pool을 조용히 계속 점유할 수 있습니다.
이 패턴은 보통 아래에서 자주 나옵니다.
- worker가 session을 잘못 재사용함
- job 하나가 DB와 무관한 작업까지 connection을 계속 잡고 있음
- task retry와 reconnect 동작이 숨은 pool pressure를 만듦
그래서 웹 트래픽은 평범해 보여도 실제 connection 압박은 background execution 쪽에서 만들어지는 경우가 많습니다.
실전 점검 순서
connection이 오래 열린 채 남을 때는 아래 순서가 빠릅니다.
- active, idle, waiting 사용량 비교
- 어떤 경로가 connection을 가장 오래 잡는지 확인
- cleanup / exception path 확인
- pool 압박과 job / request 부하 비교
- leak인지, 긴 hold time인지, 동시성 증가인지 판단
이 순서가 중요한 이유는 두 가지 흔한 실수를 막기 때문입니다.
- connection lifetime을 모른 채 pool size부터 키우는 실수
- application hold time을 보지 않고 slow query부터 단정하는 실수
같은 사고에서 task backlog도 보인다면 Python Celery task가 멈춰 있을 때와 같이 보세요.
아주 작은 예시가 보여주는 핵심
conn = engine.connect()
result = conn.execute(query)
# missing conn.close()
빌린 connection을 돌려주지 않으면 pool이 서서히 바닥나고 결국 요청이 뒤에서 기다리기 시작합니다.
중요한 것은 close() 한 줄 자체보다 ownership이 충분히 분명한가입니다. 모든 경로가 누가 release를 책임지는지 알고 있어야 실제로 닫힙니다.
DB 경로마다 물어볼 질문
각 query나 transaction 경로마다 아래를 물어보면 도움이 됩니다.
- 누가 connection이나 session을 획득하는가
- 잡고 있는 동안 무슨 작업을 하는가
- 성공 시 무엇이 release를 보장하는가
- 실패 시 무엇이 release를 보장하는가
이 프레이밍이 좋은 이유는 DB connection 장애가 capacity 문제처럼 보여도 실제로는 lifecycle bug인 경우가 많기 때문입니다.
FAQ
Q. pool exhaustion이면 항상 DB 자체가 느린 건가요?
아닙니다. 애플리케이션이 connection을 너무 오래 잡고 있거나, cleanup을 일부 경로에서 놓치거나, 같은 pool에 더 많은 동시 작업을 밀어 넣고 있을 수도 있습니다.
Q. 운영에서 무엇부터 보는 게 가장 빠른가요?
active, idle, waiting 사용량을 먼저 보고, 어떤 요청이나 job 경로가 connection을 가장 오래 붙잡는지 확인하세요.
Q. background job만으로도 이 문제가 생길 수 있나요?
네. worker와 task runner가 DB 자원을 조용히 오래 점유하면 웹 요청까지 pool starvation처럼 느끼게 만들 수 있습니다.
Read Next
- Python 전체 분기부터 다시 보고 싶다면 Python 트러블슈팅 가이드로 가세요.
- 같은 사고에서 task backlog도 보인다면 Python Celery task가 멈춰 있을 때를 보세요.
- 관측 부족 때문에 원인 파악이 늦다면 Python 로그가 안 찍힐 때도 같이 보세요.
Related Posts
Sources:
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.
먼저 읽어볼 가이드
검색 유입이 많은 핵심 글부터 이어서 보세요.
- 미들웨어 트러블슈팅 가이드: Redis vs RabbitMQ vs Kafka 개발자를 위한 미들웨어 트러블슈팅 허브 글입니다. Redis, RabbitMQ, Kafka 중 어떤 증상부터 먼저 봐야 하는지와 어떤 문제 패턴이 각 시스템에 가까운지 정리합니다.
- Kubernetes CrashLoopBackOff: 먼저 볼 것들 startup failure, probe, config, resource limit 관점에서 CrashLoopBackOff를 어떻게 나눠서 봐야 하는지 정리한 가이드입니다.
- Kafka consumer lag가 계속 늘 때: 트러블슈팅 가이드 Kafka consumer lag가 계속 늘어날 때 무엇부터 봐야 하는지 정리합니다. poll 주기, 처리 속도, rebalance, consumer 설정까지 실전 기준으로 다룹니다.
- Kafka Rebalancing Too Often 가이드 Kafka consumer group에서 rebalance가 너무 자주 일어날 때 membership flapping, poll timing, protocol, assignment churn을 어떤 순서로 봐야 하는지 설명하는 실전 가이드입니다.
- Docker container가 계속 재시작될 때: 먼저 확인할 것들 exit code, command failure, environment mistake, health check 관점에서 Docker restart loop를 푸는 실전 가이드입니다.
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.