본문으로 건너뛰기

[Open WebUI] users.py의 5개 업데이트 메서드에서 중복 SELECT 쿼리 제거

PR 링크: open-webui/open-webui#21011 상태: Merged | 변경: +27 / -21

들어가며

Open WebUI의 users.py에는 사용자 정보를 업데이트하는 5개의 메서드가 있다: update_user_role_by_id, update_user_status_by_id, update_user_profile_image_url_by_id, update_last_active_by_id, update_user_by_id. 이 메서드들 모두 동일한 비효율 패턴을 가지고 있었다. db.query(User).filter_by(id=id).update(...) 후에 다시 db.query(User).filter_by(id=id).first()로 같은 레코드를 조회하는 것이다.

핵심 코드 분석

Before: UPDATE + 별도 SELECT

def update_user_role_by_id(self, id, role, db=None):
    try:
        with get_db_context(db) as db:
            db.query(User).filter_by(id=id).update({"role": role})
            db.commit()
            user = db.query(User).filter_by(id=id).first()  # 불필요한 재조회
            return UserModel.model_validate(user)
    except Exception:
        return None

After: fetch-modify-refresh 패턴

def update_user_role_by_id(self, id, role, db=None):
    try:
        with get_db_context(db) as db:
            user = db.query(User).filter_by(id=id).first()
            if not user:
                return None
            user.role = role
            db.commit()
            db.refresh(user)
            return UserModel.model_validate(user)
    except Exception:
        return None

왜 이게 좋은가

  1. 쿼리 수 절반 감소: 5개 메서드 모두 UPDATE + SELECT에서 SELECT + commit + refresh로 변경되어, 총 쿼리 수가 2에서 1로 줄어든다. db.refresh()는 같은 세션 내에서 객체 상태를 갱신한다.
  2. 존재 확인 추가: 기존 코드는 UPDATE가 0행에 영향을 줘도 에러 없이 None을 반환하는 first() 결과에 의존했다. 새 코드는 명시적으로 if not user: return None으로 존재하지 않는 사용자를 조기에 처리한다.
  3. ORM 방식 일관성: setattr를 사용한 동적 필드 업데이트는 model_dump(exclude_none=True)과 자연스럽게 결합되어, 향후 필드 추가 시에도 코드 변경이 최소화된다.

5개 메서드를 동일한 패턴으로 통일한 것도 유지보수 측면에서 좋은 접근이다.

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글