From d00a02ff23e2bb211a3dbe192944d65a87f9e331 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Tue, 3 Nov 2026 21:06:15 +0100 Subject: [PATCH] perf: faster filter lookups on chat requests when Redis is enabled (#33668) With Redis enabled, each chat request and each title or tag generation looks up which pipeline filters apply, and every lookup parsed the full model list stored in Redis from scratch. Each worker now keeps the parsed list and parses it again only when the model list changes. The lookup returns the same filters as before, at the cost of about 3 MB of memory per worker for 1000 models. Measured per lookup against a local Redis; the larger list size in each pair comes from models with profile images: | Models | Model list size | Before | After | | --- | --- | --- | --- | | 200 | 0.17 MB | 0.42 ms | 0.05 ms | | 200 | 2.2 MB | 1.16 ms | 0.05 ms | | 1000 | 0.83 MB | 2.43 ms | 0.06 ms | | 1000 | 11 MB | 6.21 ms | 0.10 ms | --- backend/open_webui/socket/utils.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/socket/utils.py b/backend/open_webui/socket/utils.py index 1d350d3696..6f332a59a0 100644 --- a/backend/open_webui/socket/utils.py +++ b/backend/open_webui/socket/utils.py @@ -214,12 +214,14 @@ class CachedRedisDict(RedisDict): super().__init__(name, redis_url, redis_sentinels, redis_cluster, cache_set_signature=True) self._cache: dict = {} self._cached_signature: str | None = None + self._cached_values: list | None = None def _refresh_cache(self) -> dict: stored_signature = self.redis.get(self._signature_name) if stored_signature is None or stored_signature != self._cached_signature: self._cache = self.redis.hgetall(self.name) self._cached_signature = stored_signature + self._cached_values = None return self._cache def __getitem__(self, key): @@ -238,7 +240,11 @@ class CachedRedisDict(RedisDict): return list(self._refresh_cache().keys()) def values(self): - return [JSONCodec.loads(v) for v in self._refresh_cache().values()] + cache = self._refresh_cache() + # Decoded once per signature; callers share these dicts read-only. + if self._cached_values is None: + self._cached_values = [JSONCodec.loads(v) for v in cache.values()] + return list(self._cached_values) def items(self): return [(k, JSONCodec.loads(v)) for k, v in self._refresh_cache().items()]