FastAPI에서 비동기 DB 커넥션 풀 관리하기

문제 상황

사내 API 서버를 FastAPI로 전환하면서 PostgreSQL 연결에 asyncpg를 사용했다. 초기에는 문제가 없었지만 트래픽이 증가하자 TooManyConnectionsError가 발생하기 시작했다.

기존 코드는 요청마다 커넥션을 생성하고 있었다.

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    conn = await asyncpg.connect(DATABASE_URL)
    user = await conn.fetchrow("SELECT * FROM users WHERE id = $1", user_id)
    await conn.close()
    return user

해결 방법

커넥션 풀을 애플리케이션 시작 시 생성하고 재사용하도록 변경했다.

from contextlib import asynccontextmanager

pool = None

@app.on_event("startup")
async def startup():
    global pool
    pool = await asyncpg.create_pool(
        DATABASE_URL,
        min_size=10,
        max_size=20,
        command_timeout=60
    )

@app.on_event("shutdown")
async def shutdown():
    await pool.close()

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    async with pool.acquire() as conn:
        user = await conn.fetchrow(
            "SELECT * FROM users WHERE id = $1", 
            user_id
        )
    return user

결과

  • 동시 요청 500개 기준 응답 시간이 평균 1.2초에서 0.3초로 개선
  • DB 커넥션 에러 완전히 해결
  • min_size, max_size는 워크로드에 맞춰 조정이 필요했다

커넥션 풀 사이즈는 모니터링하면서 점진적으로 조정했다. pgBouncer 도입도 고려했지만 현재는 애플리케이션 레벨 풀링으로 충분했다.

FastAPI에서 비동기 DB 커넥션 풀 관리하기