[PR #21596] [MERGED] perf: eliminate 2 redundant full chat deserialization on every message send #49204

Closed
opened 2026-04-30 01:31:52 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/open-webui/open-webui/pull/21596
Author: @Classic298
Created: 2/19/2026
Status: Merged
Merged: 2/21/2026
Merged by: @tjbck

Base: devHead: perf-chat-loading


📝 Commits (2)

  • 2dde12b perf: eliminate 2 redundant full chat deserialization on every message send (#162)
  • abe92a0 Address maintainer feedback: rename method and inline call (#166)

📊 Changes

3 files changed (+42 additions, -6 deletions)

View changed files

📝 backend/open_webui/main.py (+3 -3)
📝 backend/open_webui/models/chats.py (+35 -0)
📝 backend/open_webui/utils/middleware.py (+4 -3)

📄 Description

Problem:
Every message send triggered get_chat_by_id_and_user_id which loads the entire Chat row - including the potentially massive JSON blob containing the full conversation history - even when the caller only needed a simple yes/no ownership check or a single column value.

Two call sites in the message-send hot path were doing this:

  1. main.py ownership verification: loaded the entire chat object including all message history JSON, then checked if chat is None. The JSON blob was immediately discarded - only the existence of the row mattered.
  2. middleware.py folder check: loaded the entire chat object including all message history JSON, then read only chat.folder_id - a plain column on the chat table that requires zero JSON parsing.

Fix:

  • Added chat_exists_by_id_and_user_id(): uses SQL EXISTS subquery which
    returns a boolean without loading any row data. The database can satisfy
    this from the primary key index alone.
  • Added get_chat_folder_id(): queries only the folder_id column via
    db.query(Chat.folder_id), which tells SQLAlchemy to SELECT only that
    single column instead of the entire row.

Both new methods preserve the same error handling semantics (return False/None on exception) and user_id filtering (ownership check) as the original get_chat_by_id_and_user_id.

Impact:

  • Best case (typical): eliminates deserializing 2 full chat JSON blobs per message send. For long conversations (hundreds of messages with tool calls, images, file attachments), this blob can be multiple megabytes.
  • Worst case: no regression - the new queries are strictly cheaper than the old ones (less data transferred, less Python object construction, no Pydantic model_validate overhead).

Contributor License Agreement

By submitting this pull request, I confirm that I have read and fully agree to the Contributor License Agreement (CLA), and I am providing my contributions under its terms.

Note

Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/open-webui/open-webui/pull/21596 **Author:** [@Classic298](https://github.com/Classic298) **Created:** 2/19/2026 **Status:** ✅ Merged **Merged:** 2/21/2026 **Merged by:** [@tjbck](https://github.com/tjbck) **Base:** `dev` ← **Head:** `perf-chat-loading` --- ### 📝 Commits (2) - [`2dde12b`](https://github.com/open-webui/open-webui/commit/2dde12b4d7c30495f62231e0fec5df7c18e7fe44) perf: eliminate 2 redundant full chat deserialization on every message send (#162) - [`abe92a0`](https://github.com/open-webui/open-webui/commit/abe92a08c01311e92d656c1f0ea19d80f3e03e01) Address maintainer feedback: rename method and inline call (#166) ### 📊 Changes **3 files changed** (+42 additions, -6 deletions) <details> <summary>View changed files</summary> 📝 `backend/open_webui/main.py` (+3 -3) 📝 `backend/open_webui/models/chats.py` (+35 -0) 📝 `backend/open_webui/utils/middleware.py` (+4 -3) </details> ### 📄 Description Problem: Every message send triggered get_chat_by_id_and_user_id which loads the entire Chat row - including the potentially massive JSON blob containing the full conversation history - even when the caller only needed a simple yes/no ownership check or a single column value. Two call sites in the message-send hot path were doing this: 1. main.py ownership verification: loaded the entire chat object including all message history JSON, then checked `if chat is None`. The JSON blob was immediately discarded - only the existence of the row mattered. 2. middleware.py folder check: loaded the entire chat object including all message history JSON, then read only `chat.folder_id` - a plain column on the chat table that requires zero JSON parsing. Fix: - Added `chat_exists_by_id_and_user_id()`: uses SQL EXISTS subquery which returns a boolean without loading any row data. The database can satisfy this from the primary key index alone. - Added `get_chat_folder_id()`: queries only the `folder_id` column via `db.query(Chat.folder_id)`, which tells SQLAlchemy to SELECT only that single column instead of the entire row. Both new methods preserve the same error handling semantics (return False/None on exception) and user_id filtering (ownership check) as the original get_chat_by_id_and_user_id. Impact: - Best case (typical): eliminates deserializing 2 full chat JSON blobs per message send. For long conversations (hundreds of messages with tool calls, images, file attachments), this blob can be multiple megabytes. - Worst case: no regression - **the new queries are strictly cheaper than the old ones (less data transferred, less Python object construction, no Pydantic model_validate overhead).** ### Contributor License Agreement <!-- 🚨 DO NOT DELETE THE TEXT BELOW 🚨 Keep the "Contributor License Agreement" confirmation text intact. Deleting it will trigger the CLA-Bot to INVALIDATE your PR. --> By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms. > [!NOTE] > Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-30 01:31:52 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#49204