커넥션 풀의 연결(Connection Checkout)은 쿼리가 실행되는 순간이 아니라, with Session() 또는 async with AsyncSession() 블록에 진입해서 데이터베이스에 실제 요청을 보내거나 응답을 받는 순간(session.execute(), session.select(), …)(ref1)부터 with 블록을 나갈 때까지 계속 점유된다. with Session 컨텍스트가 열리자마자 바로 커넥션 풀을 점유하는 것은 아니지만, 최악의 경우 with 블록의 범위가 곧 커넥션의 점유 범위가 될 수 있다.
def problematic_sync_function():
with Session() as session:
# 1. 커넥션을 빌리고 DB 작업 수행
stmt = select(User).where(User.id == 1)
user = session.query(User).filter_by(id=1).one()
# 2. 이 5초 동안 DB 커넥션과 작업자(Worker)를 모두 점유함!
response = requests.get("https://slow-api.com/process") # 5초 소요
user.external_data = response.json()
session.commit()
# 커넥션 반납
Python
복사
따라서 영속성 관련 함수(레포지토리 메서드)를 데코레이팅 패턴으로 감싸면 깜빡하고 세션을 닫지 않을 위험은 많이 줄어들지만, 한편으론 커넥션 풀이 정말 필요한 때만 사용되지는 않을 수 있다. 위와 같은 코드는 첫 번째 DB 호출 이후 함수가 종료되기 전까지 session.close()가 불가능하다. 이렇게 하나의 영속성 함수 내에서 DB와 상관없는 I/O 바운드 작업이 존재하여 최적화가 필요하다면, 아래와 같이 DB 입출력 부분에만 컨텍스트 매니저를 사용하여 불필요한 부분에 DB 커넥션을 반납하라.
def recommended_sync_function():
user_id = 1
with Session() as session:
user = session.query(User).filter_by(id=user_id).one()
# 커넥션 반납
# 이 동안에는 DB 커넥션을 점유하지 않음
response = requests.get("https://slow-api.com/process") # 5초 소요
external_data = response.json()
with Session() as session:
user = session.query(User).filter_by(id=user_id).one()
user.external_data = external_data
session.commit()
# 커넥션 반납
Python
복사
parse me : 언젠가 이 글에 쓰이면 좋을 것 같은 재료을 보관해 두는 영역입니다.
1.
None
from : 과거의 어떤 원자적 생각이 이 생각을 만들었는지 연결하고 설명합니다.
1.
•
앞의 글에는 데코레이터를 사용하는 패턴이 만능의 모범 사례인 것처럼 소개했다.
2.
•
앞의 글에서는 데코레이터를 사용하는 방법, 메서드 내에서 컨텍스트 매니저를 사용하는 두 가지 방법을 소개했다. 이 글의 내용에서 지적하는 내용을 개선하려면 데코레이터 대신 컨텍스트 매니저를 선택해야 한다.
3.
•
커넥션 풀이 무엇인지는 앞의 글을 참고하라. 참고로, 이 문제는 (당연히) 애플리케이션 레벨 커넥션 풀이 아니라 미들웨어 커넥션 풀만 사용하는 경우에도 동일하게 적용되는 문제다.
4.
•
이 글의 내용을 앞의 프롬프트에 반영했다.
supplementary : 어떤 새로운 생각이 이 문서에 작성된 생각을 뒷받침하는지 연결합니다.
1.
None
opposite : 어떤 새로운 생각이 이 문서에 작성된 생각과 대조되는지 연결합니다.
1.
None
to : 이 문서에 작성된 생각이 어떤 생각으로 발전되거나 이어지는지를 작성하는 영역입니다.
1.
None
ref : 생각에 참고한 자료입니다.