커넥션 풀의 연결(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()
# 커넥션 반납
따라서 영속성 관련 함수(레포지토리 메서드)를 데코레이팅 패턴으로 감싸면 깜빡하고 세션을 닫지 않을 위험은 많이 줄어들지만, 한편으론 커넥션 풀이 정말 필요한 때만 사용되지는 않을 수 있다. 위와 같은 코드는 첫 번째 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()
# 커넥션 반납
parse me : 언젠가 이 글에 쓰이면 좋을 것 같은 재료을 보관해 두는 영역입니다.
None
from : 과거의 어떤 원자적 생각이 이 생각을 만들었는지 연결하고 설명합니다.
supplementary : 어떤 새로운 생각이 이 문서에 작성된 생각을 뒷받침하는지 연결합니다.
None
opposite : 어떤 새로운 생각이 이 문서에 작성된 생각과 대조되는지 연결합니다.
None