본문으로 건너뛰기

[Open WebUI] 매 인증 요청마다 실행되는 last_active 업데이트를 단일 UPDATE 쿼리로 최적화

PR 링크: open-webui/open-webui#23215 상태: Merged | 변경: +3 / -8

들어가며

Open WebUI에서 update_last_active_by_id는 인증된 모든 요청에서 호출됩니다. 기존 구현은 사용자 행을 SELECT로 가져온 후, 필드를 수정하고, commit 후 refresh하여 전체 행을 다시 읽은 뒤 Pydantic 모델로 변환해 반환했습니다. 그런데 이 반환값을 사용하는 호출자가 없었습니다. 이 PR은 이를 단일 UPDATE 쿼리로 교체합니다.

핵심 코드 분석

Before: SELECT + commit + refresh + Pydantic 변환

def update_last_active_by_id(self, id: str, db=None) -> Optional[UserModel]:
    try:
        with get_db_context(db) as db:
            user = db.query(User).filter_by(id=id).first()  # SELECT
            if not user:
                return None
            user.last_active_at = int(time.time())
            db.commit()
            db.refresh(user)  # 전체 행 다시 SELECT
            return UserModel.model_validate(user)  # Pydantic 직렬화
    except Exception:
        return None

After: 단일 UPDATE 쿼리

def update_last_active_by_id(self, id: str, db=None) -> None:
    try:
        with get_db_context(db) as db:
            db.query(User).filter_by(id=id).update(
                {'last_active_at': int(time.time())}
            )
            db.commit()
    except Exception:
        pass

왜 이게 좋은가

  1. DB 라운드트립 감소: SELECT + UPDATE + SELECT(refresh) 3회에서 UPDATE 1회로 줄어든다. 매 인증 요청마다 실행되므로 효과가 누적된다.
  2. 불필요한 Pydantic 직렬화 제거: 반환값을 사용하는 호출자가 없으므로, UserModel 변환 비용을 완전히 제거했다.
  3. throttle 데코레이터와 결합: 이 메서드에는 이미 throttle 데코레이터가 적용되어 있어, 실행 빈도는 제한되지만 실행될 때의 비용을 최소화하는 것이 중요하다.

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글