SQLAlchemy
역시 연결 풀을 기본적으로 채택하고 있는데, 그 중 기본으로 제공하는 것은 큐 풀 ( QueuePool
)pool_size
와 max_overflow
를 바탕으로 복수의 연결 풀을 구성해서 운용SQLite
를 제외한 모든 데이터베이스에서 기본값으로 이용함Tip! 추가 내용
SQLAlchemy
0.7 부터SQLite
같은 파일 기반 데이터베이스에서는 기본적으로NullPool
을 채택
- 파일 기반 데이터베이스에는 네트워크 연결이 일어나지 않기 때문에 연결 비용이 적기 때문
NullPool
은 이름에서 알 수 있듯이 연결 풀을 유지하지 않고 풀에 연결이 들어오는 즉시 폐기
- 큐 풀의
pool_size
를0
으로 하는 것과 같다고 착각할 수 있으나, 큐 풀은pool_size
가0
일 때pool_size
가 무한대인 것으로 인식
- 따라서 풀을 만들지 않으려면
NullPool
을 쓰는 것이 적절
1. 큐 풀이 처음부터 연결을 미리 만드는 것은 아님 ( 0개로 시작 )
2. 요청이 들어올 때, 큐 풀에 유효 연결이 없으면 하나 생성
3. 설정된 pool_size
까지는 더 연결이 필요하지 않은 상황이라도 연결을 종료하지 않음
4. 요청이 들어올 때, pool_size
까지 다 찼다 할지라도 유효 연결이 없으면 초과하여 하나 생성
5. 4번 이후부터는 오버플로 상황이기 때문에, 큐 풀은 적극적으로 오버플로를 방지하기 위해 새로 들어오는 연결을 종료하여 pool_size
에 총 연결 수를 맞춤
6. QueuePool
이 관리하는 연결이 pool_size
+ max_overflow
까지 다 찬 상황에서 요청이 들어오면, 일단 기다리게 함 ( 기본값으로는 30초를 기다림 )
7. 30초를 기다려도 반환되는 연결이 없다면 TimeoutError
예외를 발생시킴
QueuePool
관련 위 언급한 2가지 값 ( pool_size
, max_overflow
) 을 바꿔주는 게 좋은데 기본값은 5
, 10
pool_size
max_overflow
pool_size
가 과하게 설정되어 있으면 데이터베이스 입장에서 너무 많은 연결을 점유하고 있으니 비효율적max_overflow
가 데이터베이스나 웹 인스턴스의 한계치보다 너무 빡빡하게 잡혀있으면 조금만 사용자 유입이 늘어도 TimeoutError
를 쉽게 만나거나 서비스 속도 저하를 자주 경험하게 됨SQLAlchemy
쓰는 서비스를 만들어서 개발 잘 하고 배포했는데 프로덕션에서 잠깐 잘 돌더니 TimeoutError
를 내며 죽는 경험Session
이 큐 풀에 연결을 받기 위해 기다리다가 못 참고 TimeoutError
를 내는 것timeout
기본값은 30 이므로 30초 동안 풀의 모든 연결이 점유된 상태에서 아무것도 받지 못한 상태가 된 것Session
에서 제대로 연결을 반환해주지 않아서 발생하는 문제Session
이 연결을 불러다 써놓고 Pool
에 돌려주는 일을 빼먹는 실수가 잦은데 현재 구조상에서 요청이 끝나는 시점에 맞춰 session.close()
를 적절히 호출해주면 됨SQLAlchemy
를 쓰면 Session
활용을 암시적으로 하게 될 때가 많음Session
이 실제로 요청을 보내는 시점에서야 연결을 시도하기 때문에 예상치 못한 기능 변경으로 연결 폭증을 겪는 것