Sandcorp샌드코프 · 웹·AI 제품 개발

새 프로젝트 받는 중 · 갱신 2026.11sandcorp.co.krGitHub

← 팁

성능웹소켓·실시간

Django ASGI에서 DB 연결은 풀로 쓰고, 오래 열린 요청은 바로 돌려준다

ASGI에서는 CONN_MAX_AGE를 올려도 연결이 재사용되지 않는다. psycopg 풀을 켜고, SSE·롱폴링은 DB를 쓴 직후 연결을 풀에 돌려준다.

Django를 ASGI로 돌리면 로그인한 요청마다 10~20ms가 더 붙고, pg_stat_activity의 새 세션이 요청 수만큼 늘어난다. CONN_MAX_AGE를 올려도 그대로다.

Django의 DB 연결은 스레드마다 따로 있다. ASGI에서는 요청의 동기 부분이 매번 다른 스레드에서 돌기 때문에, 연결이 재사용되지 못하고 요청 끝에 닫힌다.

Django 5.1부터는 psycopg 연결 풀을 켤 수 있다. 연결이 스레드가 아니라 풀에 속하게 된다.

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": "app",
        "OPTIONS": {"pool": True},
    }
}

풀을 켠 뒤 PoolTimeout이 나면 SSE·롱폴링처럼 몇 분씩 열린 요청을 의심한다. DB를 한 번 만진 뒤 연결을 응답이 끝날 때까지 쥐고 있는 것이다. DB를 쓴 직후 같은 스레드에서 연결을 닫으면 풀로 돌아간다.

from django.db import connection

def load_snapshot(room_id: int) -> list:
    try:
        return list(Message.objects.filter(room_id=room_id)[:50])
    finally:
        connection.close()  # 풀을 켜면 닫기 = 풀에 돌려주기

함께 볼 것: sync 전용 미들웨어가 async 사슬에 끼면 요청마다 async_to_sync 실행기를 새로 세우고, 그 요청에서 띄운 태스크가 그 실행기를 물려받아 교착할 수 있다. 미들웨어는 sync·async를 둘 다 받게 쓴다. 고치기 전후는 같은 끝점을 20번 불러 중앙값(첫 값은 버림)과 요청 전후 Postgres 세션 수로 비교한다.

문의 →