From ee901fcd2ca82d7a7dad48170c64df782d3e040a Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 22 Mar 2026 05:48:05 -0500 Subject: [PATCH] refac --- backend/open_webui/socket/utils.py | 19 ++-- .../components/layout/Sidebar/UserMenu.svelte | 86 +++++++++---------- 2 files changed, 57 insertions(+), 48 deletions(-) diff --git a/backend/open_webui/socket/utils.py b/backend/open_webui/socket/utils.py index 682d779ccd..16b0cc3855 100644 --- a/backend/open_webui/socket/utils.py +++ b/backend/open_webui/socket/utils.py @@ -82,13 +82,22 @@ class RedisDict: return [(k, json.loads(v)) for k, v in self.redis.hgetall(self.name).items()] def set(self, mapping: dict): - pipe = self.redis.pipeline() + if not mapping: + self.redis.delete(self.name) + return - pipe.delete(self.name) - if mapping: - pipe.hset(self.name, mapping={k: json.dumps(v) for k, v in mapping.items()}) + # Fetch existing keys before writing so we know which ones to remove. + # HKEYS is cheap — it transfers only short key strings, not large JSON values. + existing_keys = set(self.redis.hkeys(self.name)) + new_keys = set(mapping.keys()) + keys_to_remove = existing_keys - new_keys - pipe.execute() + # HSET first (add/update all new values), then HDEL (remove stale keys). + # We never DELETE the whole hash — this eliminates the race window + # where concurrent readers would see an empty models dict. + self.redis.hset(self.name, mapping={k: json.dumps(v) for k, v in mapping.items()}) + if keys_to_remove: + self.redis.hdel(self.name, *keys_to_remove) def get(self, key, default=None): try: diff --git a/src/lib/components/layout/Sidebar/UserMenu.svelte b/src/lib/components/layout/Sidebar/UserMenu.svelte index cc250e5458..293c3a4f3d 100644 --- a/src/lib/components/layout/Sidebar/UserMenu.svelte +++ b/src/lib/components/layout/Sidebar/UserMenu.svelte @@ -237,49 +237,49 @@ {#if role === 'admin'} { - if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) { - return; - } - e.preventDefault(); - show = false; - goto('/playground'); - if ($mobile) { - await tick(); - showSidebar.set(false); - } - }} - > -
- -
-
{$i18n.t('Playground')}
-
- { - if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) { - return; - } - e.preventDefault(); - show = false; - goto('/admin'); - if ($mobile) { - await tick(); - showSidebar.set(false); - } - }} - > -
- -
-
{$i18n.t('Admin Panel')}
-
+ href="/playground" + draggable="false" + class="flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition cursor-pointer select-none" + on:click={async (e) => { + if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) { + return; + } + e.preventDefault(); + show = false; + goto('/playground'); + if ($mobile) { + await tick(); + showSidebar.set(false); + } + }} + > +
+ +
+
{$i18n.t('Playground')}
+ + { + if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) { + return; + } + e.preventDefault(); + show = false; + goto('/admin'); + if ($mobile) { + await tick(); + showSidebar.set(false); + } + }} + > +
+ +
+
{$i18n.t('Admin Panel')}
+
{/if} {#if help}