본문으로 건너뛰기

[Open WebUI] 메시지 전송마다 발생하는 불필요한 채팅 JSON 역직렬화 2회 제거

PR 링크: open-webui/open-webui#21596 상태: Merged | 변경: +42 / -6

들어가며

Open WebUI에서 메시지를 보낼 때마다 get_chat_by_id_and_user_id가 두 번 호출되었습니다. 이 함수는 Chat 테이블의 전체 행을 로드하는데, 여기에는 대화 전체 히스토리가 담긴 거대한 JSON 블록이 포함됩니다. 긴 대화(도구 호출, 이미지, 파일 첨부 포함)에서 이 JSON은 수 MB에 달할 수 있습니다. 하지만 실제로 필요한 정보는 "이 채팅이 존재하는가?"와 "folder_id 값이 무엇인가?" 뿐이었습니다.

핵심 코드 분석

소유권 확인: 전체 로드에서 EXISTS 쿼리로

Before (main.py):

chat = Chats.get_chat_by_id_and_user_id(metadata["chat_id"], user.id)
if chat is None and user.role != "admin":
    raise HTTPException(...)

After:

if not Chats.is_chat_owner(metadata["chat_id"], user.id) and user.role != "admin":
    raise HTTPException(...)

새로 추가된 is_chat_owner 메서드:

def is_chat_owner(self, id: str, user_id: str, db=None) -> bool:
    with get_db_context(db) as db:
        return db.query(
            exists().where(
                and_(Chat.id == id, Chat.user_id == user_id)
            )
        ).scalar()

폴더 조회: 전체 로드에서 단일 컬럼 조회로

Before (middleware.py):

chat = Chats.get_chat_by_id_and_user_id(chat_id, user.id)
if chat and chat.folder_id:
    folder = Folders.get_folder_by_id_and_user_id(chat.folder_id, user.id)

After:

folder_id = Chats.get_chat_folder_id(chat_id, user.id)
if folder_id:
    folder = Folders.get_folder_by_id_and_user_id(folder_id, user.id)

새로 추가된 get_chat_folder_id 메서드:

def get_chat_folder_id(self, id: str, user_id: str, db=None):
    with get_db_context(db) as db:
        result = (
            db.query(Chat.folder_id)
            .filter_by(id=id, user_id=user_id)
            .first()
        )
        return result[0] if result else None

왜 이게 좋은가

  • 메시지 전송마다 수 MB 크기의 JSON blob을 2번 역직렬화하던 것이 완전히 제거됩니다
  • EXISTS 쿼리는 행 데이터를 로드하지 않고 프라이머리 키 인덱스만으로 결과를 반환합니다
  • db.query(Chat.folder_id)는 SQLAlchemy에 SELECT folder_id만 생성하도록 지시합니다
  • Pydantic의 model_validate 오버헤드도 제거됩니다
  • 대화가 길어질수록 효과가 더 커지는 최적화입니다

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글