[GH-ISSUE #21410] bug: Memory leak in SESSION_POOL, USAGE_POOL and YdocManager (with and without Redis) #19467

Closed
opened 2026-04-20 01:56:27 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @ashm-dev on GitHub (Feb 14, 2026).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/21410

Description

The in-memory data structures SESSION_POOL, USAGE_POOL, and YdocManager._updates grow unboundedly, causing steady RAM increase that only resolves on restart.

This affects both modes:

  • Without Redis: data accumulates in Python process RAM
  • With Redis: data accumulates in Redis RAM, because RedisDict uses hset without expire — no TTL is ever set on keys. Storing data in Redis without TTL is problematic: it defeats Redis's ability to manage memory via eviction policies, and effectively turns Redis into an ever-growing store.

Root Causes

1. USAGE_POOL not cleaned on disconnect

File: backend/open_webui/socket/main.py

On disconnect, SESSION_POOL[sid] is deleted, but USAGE_POOL entries for that sid are only cleaned via periodic_usage_pool_cleanup() on timeout. If a socket closes without timing out first, entries persist indefinitely.

@sio.event
async def disconnect(sid):
    if sid in SESSION_POOL:
        del SESSION_POOL[sid]      # ← cleaned
        # USAGE_POOL[model_id][sid] ← NOT cleaned here

2. YdocManager._updates grows without bound

File: backend/open_webui/socket/utils.py

Every document edit appends to a list that is never evicted (neither in-memory nor in Redis):

async def append_to_updates(self, document_id, update):
    if not self._redis:
        if document_id not in self._updates:
            self._updates[document_id] = []
        self._updates[document_id].append(update)  # unbounded

With Redis, rpush is used without expire — same problem.

clear_document() is only called when all users leave a document. If at least one user stays connected, updates accumulate forever.

3. RedisDict stores data without TTL

File: backend/open_webui/socket/utils.py

RedisDict uses hset/hget with no expiration. SESSION_POOL, USAGE_POOL, and MODELS data persists in Redis indefinitely. This is undesirable — data in Redis should have TTL to allow proper memory management and prevent unbounded growth.

Observed Behavior

  • RAM usage grows steadily in small increments during normal usage
  • Only returns to baseline after full application restart
  • With Redis enabled, Python process memory stabilizes but Redis memory grows instead

Expected Behavior

  • USAGE_POOL entries for a sid should be removed on disconnect
  • YdocManager._updates should have a size limit or TTL
  • RedisDict should set TTL on keys (e.g. session data should expire after disconnect + grace period)
  • Redis keys for SESSION_POOL, USAGE_POOL should have reasonable expiration times
Originally created by @ashm-dev on GitHub (Feb 14, 2026). Original GitHub issue: https://github.com/open-webui/open-webui/issues/21410 ## Description The in-memory data structures `SESSION_POOL`, `USAGE_POOL`, and `YdocManager._updates` grow unboundedly, causing steady RAM increase that only resolves on restart. **This affects both modes:** - **Without Redis:** data accumulates in Python process RAM - **With Redis:** data accumulates in Redis RAM, because `RedisDict` uses `hset` without `expire` — no TTL is ever set on keys. Storing data in Redis without TTL is problematic: it defeats Redis's ability to manage memory via eviction policies, and effectively turns Redis into an ever-growing store. ## Root Causes ### 1. USAGE_POOL not cleaned on disconnect **File:** `backend/open_webui/socket/main.py` On disconnect, `SESSION_POOL[sid]` is deleted, but `USAGE_POOL` entries for that `sid` are only cleaned via `periodic_usage_pool_cleanup()` on timeout. If a socket closes without timing out first, entries persist indefinitely. ```python @sio.event async def disconnect(sid): if sid in SESSION_POOL: del SESSION_POOL[sid] # ← cleaned # USAGE_POOL[model_id][sid] ← NOT cleaned here ``` ### 2. YdocManager._updates grows without bound **File:** `backend/open_webui/socket/utils.py` Every document edit appends to a list that is never evicted (neither in-memory nor in Redis): ```python async def append_to_updates(self, document_id, update): if not self._redis: if document_id not in self._updates: self._updates[document_id] = [] self._updates[document_id].append(update) # unbounded ``` With Redis, `rpush` is used without `expire` — same problem. `clear_document()` is only called when all users leave a document. If at least one user stays connected, updates accumulate forever. ### 3. RedisDict stores data without TTL **File:** `backend/open_webui/socket/utils.py` `RedisDict` uses `hset`/`hget` with no expiration. `SESSION_POOL`, `USAGE_POOL`, and `MODELS` data persists in Redis indefinitely. This is undesirable — data in Redis should have TTL to allow proper memory management and prevent unbounded growth. ## Observed Behavior - RAM usage grows steadily in small increments during normal usage - Only returns to baseline after full application restart - With Redis enabled, Python process memory stabilizes but Redis memory grows instead ## Expected Behavior - `USAGE_POOL` entries for a `sid` should be removed on disconnect - `YdocManager._updates` should have a size limit or TTL - `RedisDict` should set TTL on keys (e.g. session data should expire after disconnect + grace period) - Redis keys for `SESSION_POOL`, `USAGE_POOL` should have reasonable expiration times
Author
Owner

@tjbck commented on GitHub (Feb 16, 2026):

Addressed in dev.

<!-- gh-comment-id:3905825465 --> @tjbck commented on GitHub (Feb 16, 2026): Addressed in dev.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#19467