浏览代码

perf(db): deduplicate update_user_last_active_by_id to reduce conflicts

Signed-off-by: Adam Tao <tcx4c70@gmail.com>
Adam Tao 3 月之前
父节点
当前提交
635cb8e3ff
共有 3 个文件被更改,包括 53 次插入0 次删除
  1. 11 0
      backend/open_webui/env.py
  2. 3 0
      backend/open_webui/models/users.py
  3. 39 0
      backend/open_webui/utils/misc.py

+ 11 - 0
backend/open_webui/env.py

@@ -340,6 +340,17 @@ DATABASE_ENABLE_SQLITE_WAL = (
     os.environ.get("DATABASE_ENABLE_SQLITE_WAL", "False").lower() == "true"
     os.environ.get("DATABASE_ENABLE_SQLITE_WAL", "False").lower() == "true"
 )
 )
 
 
+DATABASE_DEDUPLICATE_INTERVAL = (
+    os.environ.get("DATABASE_DEDUPLICATE_INTERVAL", 0.)
+)
+if DATABASE_DEDUPLICATE_INTERVAL == "":
+    DATABASE_DEDUPLICATE_INTERVAL = 0.0
+else:
+    try:
+        DATABASE_DEDUPLICATE_INTERVAL = float(DATABASE_DEDUPLICATE_INTERVAL)
+    except Exception:
+        DATABASE_DEDUPLICATE_INTERVAL = 0.0
+
 RESET_CONFIG_ON_START = (
 RESET_CONFIG_ON_START = (
     os.environ.get("RESET_CONFIG_ON_START", "False").lower() == "true"
     os.environ.get("RESET_CONFIG_ON_START", "False").lower() == "true"
 )
 )

+ 3 - 0
backend/open_webui/models/users.py

@@ -4,8 +4,10 @@ from typing import Optional
 from open_webui.internal.db import Base, JSONField, get_db
 from open_webui.internal.db import Base, JSONField, get_db
 
 
 
 
+from open_webui.env import DATABASE_DEDUPLICATE_INTERVAL
 from open_webui.models.chats import Chats
 from open_webui.models.chats import Chats
 from open_webui.models.groups import Groups
 from open_webui.models.groups import Groups
+from open_webui.utils.misc import deduplicate
 
 
 
 
 from pydantic import BaseModel, ConfigDict
 from pydantic import BaseModel, ConfigDict
@@ -311,6 +313,7 @@ class UsersTable:
         except Exception:
         except Exception:
             return None
             return None
 
 
+    @deduplicate(DATABASE_DEDUPLICATE_INTERVAL)
     def update_user_last_active_by_id(self, id: str) -> Optional[UserModel]:
     def update_user_last_active_by_id(self, id: str) -> Optional[UserModel]:
         try:
         try:
             with get_db() as db:
             with get_db() as db:

+ 39 - 0
backend/open_webui/utils/misc.py

@@ -1,5 +1,6 @@
 import hashlib
 import hashlib
 import re
 import re
+import threading
 import time
 import time
 import uuid
 import uuid
 import logging
 import logging
@@ -478,3 +479,41 @@ def convert_logit_bias_input_to_json(user_input):
         bias = 100 if bias > 100 else -100 if bias < -100 else bias
         bias = 100 if bias > 100 else -100 if bias < -100 else bias
         logit_bias_json[token] = bias
         logit_bias_json[token] = bias
     return json.dumps(logit_bias_json)
     return json.dumps(logit_bias_json)
+
+
+def freeze(value):
+    """
+    Freeze a value to make it hashable.
+    """
+    if isinstance(value, dict):
+        return frozenset((k, freeze(v)) for k, v in value.items())
+    elif isinstance(value, list):
+        return tuple(freeze(v) for v in value)
+    return value
+
+
+def deduplicate(interval: float = 10.0):
+    """
+    Decorator to prevent a function from being called more than once within a specified duration.
+    If the function is called again within the duration, it returns None. To avoid returning
+    different types, the return type of the function should be Optional[T].
+
+    :param interval: Duration in seconds to wait before allowing the function to be called again.
+    """
+    def decorator(func):
+        last_calls = {}
+        lock = threading.Lock()
+
+        def wrapper(*args, **kwargs):
+            key = (args, freeze(kwargs))
+            now = time.time()
+            if now - last_calls.get(key, 0) < interval:
+                return None
+            with lock:
+                if now - last_calls.get(key, 0) < interval:
+                    return None
+                last_calls[key] = now
+            return func(*args, **kwargs)
+        return wrapper
+
+    return decorator