FastAPI에서 비동기 DB 커넥션 풀 관리하기
문제 상황
FastAPI로 구축한 API 서버에서 PostgreSQL 연결에 asyncpg를 사용 중이었다. 부하 테스트 중 동시 요청이 50개를 넘어가면서 asyncpg.exceptions.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
해결 방법
커넥션 풀을 애플리케이션 시작 시 생성하고, 의존성 주입으로 관리하도록 변경했다.
import asyncpg
from fastapi import FastAPI, Depends
app = FastAPI()
db_pool = None
@app.on_event("startup")
async def startup():
global db_pool
db_pool = await asyncpg.create_pool(
DATABASE_URL,
min_size=10,
max_size=20,
command_timeout=60
)
@app.on_event("shutdown")
async def shutdown():
await db_pool.close()
async def get_db():
async with db_pool.acquire() as connection:
yield connection
@app.get("/users/{user_id}")
async def get_user(user_id: int, conn = Depends(get_db)):
user = await conn.fetchrow("SELECT * FROM users WHERE id = $1", user_id)
return user
결과
- 동시 요청 200개까지 안정적으로 처리
- 평균 응답 시간 120ms → 45ms로 감소
- CPU 사용률도 40% 정도 낮아짐
커넥션 재사용으로 오버헤드가 크게 줄었다. min_size, max_size는 서버 스펙과 예상 트래픽에 맞게 조정이 필요하다. 우리 케이스는 4코어 서버에서 10~20이 적절했다.