From 656a84804321768e769d42a4f13cd2fc1b566953 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Fri, 24 Jul 2026 03:32:01 +0200 Subject: [PATCH] perf: halve Redis round trips on model resolution and socket pools (#27225) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When WEBSOCKET_MANAGER=redis, app.state.MODELS and the socket session/ usage pools are Redis-backed dicts, so every membership test and getitem is a network round trip: - generate_chat_completion checked `model_id not in models` (HEXISTS) and then read `models[model_id]` (HGET) on every chat completion. A single .get() now serves both, with the same not-found error. - The direct-connection branch spread the pool with `{**MODELS, ...}`, which iterates keys() then fetches each value — HKEYS plus one HGET per model. dict(MODELS.items()) issues a single HGETALL instead. - get_user_ids_from_room called SESSION_POOL.get(sid) twice per session (once to filter, once for the value); the usage handler checked membership then fetched the same key. Both now do one lookup. In non-Redis mode these are plain dicts and behavior is identical. Claude-Session: https://claude.ai/code/session_01MHg5zs1VBjvRWQ54qHpfYD Co-authored-by: Claude --- backend/open_webui/socket/main.py | 16 ++++++++-------- backend/open_webui/utils/chat.py | 11 +++++++---- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/backend/open_webui/socket/main.py b/backend/open_webui/socket/main.py index a38acc0c00..9eda1d55c3 100644 --- a/backend/open_webui/socket/main.py +++ b/backend/open_webui/socket/main.py @@ -286,14 +286,14 @@ def get_session_ids_from_room(room): def get_user_ids_from_room(room): active_session_ids = get_session_ids_from_room(room) + # Single pool lookup per session (each .get is a Redis round trip + # when the session pool is Redis-backed). active_user_ids = list( - set( - [ - SESSION_POOL.get(session_id)['id'] - for session_id in active_session_ids - if SESSION_POOL.get(session_id) is not None - ] - ) + { + entry['id'] + for entry in (SESSION_POOL.get(session_id) for session_id in active_session_ids) + if entry is not None + } ) return active_user_ids @@ -357,7 +357,7 @@ async def usage(sid, data): # Store the new usage data and task USAGE_POOL[model_id] = { - **(USAGE_POOL[model_id] if model_id in USAGE_POOL else {}), + **(USAGE_POOL.get(model_id) or {}), sid: {'updated_at': current_time}, } diff --git a/backend/open_webui/utils/chat.py b/backend/open_webui/utils/chat.py index 196689f61d..2850c92b93 100644 --- a/backend/open_webui/utils/chat.py +++ b/backend/open_webui/utils/chat.py @@ -179,8 +179,10 @@ async def generate_chat_completion( # Merge the direct connection model into server models so that # task functions (title, tags, etc.) can resolve a server-side # task model while still having the direct model available. + # dict(...items()) is one HGETALL on a Redis-backed pool; ``{**pool}`` + # would issue HKEYS plus one HGET per model. models = { - **request.app.state.MODELS, + **dict(request.app.state.MODELS.items()), request.state.model['id']: request.state.model, } log.debug(f'direct connection to model: {request.state.model["id"]}') @@ -188,11 +190,12 @@ async def generate_chat_completion( models = request.app.state.MODELS model_id = form_data['model'] - if model_id not in models: + # Single lookup — membership check plus getitem would be two Redis + # round trips on a Redis-backed model pool. + model = models.get(model_id) + if model is None: raise Exception('Model not found') - model = models[model_id] - if getattr(request.state, 'direct', False) and model_id == getattr(request.state, 'model', {}).get('id'): return await generate_direct_chat_completion(request, form_data, user=user, models=models) else: