[Open WebUI] 메모리 리셋 API에서 커넥션 풀 고갈을 방지하는 치명적 버그 수정
PR 링크: open-webui/open-webui#20580 상태: Merged | 변경: +9 / -2
들어가며
Open WebUI의 POST /reset 엔드포인트에서 Depends(get_session)으로 DB 세션을 주입받은 후, 사용자의 모든 메모리에 대해 asyncio.gather()로 병렬 임베딩 API 호출을 수행하고 있었습니다. 100개의 메모리를 가진 사용자의 경우, 하나의 DB 커넥션이 100개의 임베딩 호출(각각 1-5초)이 완료될 때까지 수 분간 점유됩니다. 한 명의 사용자가 이 엔드포인트를 호출하면 커넥션 풀이 완전히 고갈되어 QueuePool timeout 에러가 전체 애플리케이션에서 발생할 수 있습니다.
핵심 코드 분석
Before: 세션이 임베딩 호출 전체를 점유
@router.post("/reset")
async def reset_memory_from_vector_db(
request: Request,
user=Depends(get_verified_user),
db: Session = Depends(get_session), # 커넥션을 점유한 채로
):
memories = Memories.get_memories_by_user_id(user.id, db=db)
# 100개의 병렬 임베딩 호출 동안 db 세션(커넥션)이 계속 열려있음
vectors = await asyncio.gather(
*[generate_embedding(m.content) for m in memories]
)
After: 세션 의존성 제거, 자체 관리 세션 사용
@router.post("/reset")
async def reset_memory_from_vector_db(
request: Request,
user=Depends(get_verified_user),
# Depends(get_session) 제거!
):
"""CRITICAL: 의도적으로 Depends(get_session)을 사용하지 않음.
이 엔드포인트는 asyncio.gather()로 모든 메모리의 임베딩을
동시에 생성합니다. 세션을 들고 있으면 커넥션 풀이 고갈됩니다."""
memories = Memories.get_memories_by_user_id(user.id)
# get_memories_by_user_id가 자체적으로 짧은 세션을 열고 즉시 반환
vectors = await asyncio.gather(
*[generate_embedding(m.content) for m in memories]
)
왜 이게 좋은가
- 치명적 버그 수정: 단 한 명의 사용자가
/reset을 호출하면 전체 애플리케이션의 DB 접근이 마비될 수 있었습니다. - 최소 변경으로 최대 효과:
db: Session = Depends(get_session)파라미터를 제거하고db=db를 빼는 것만으로 해결됩니다. - 방어적 문서화: 왜 세션 의존성을 사용하지 않는지 docstring에 명확히 기록하여 향후 누군가가 다시 추가하는 것을 방지합니다.
- 설계 교훈: DB 커넥션의 수명은 실제 DB 작업과 일치시켜야 합니다. 외부 API 호출 동안 커넥션을 보유하면 안 됩니다.
참고 자료
관련 포스트
PR Analysis 의 다른글
- 이전글 [Open WebUI] 텔레메트리에서 효율적인 COUNT 쿼리로 커넥션 풀 고갈 방지
- 현재글 : [Open WebUI] 메모리 리셋 API에서 커넥션 풀 고갈을 방지하는 치명적 버그 수정
- 다음글 [pytorch] CI: fbgemm/torchrec 핀 버전 업데이트 및 빌드 로직 리팩토링
댓글