Browse Source

perf: fix N+1 query issue in tools access control checking

- Pre-fetch user group IDs once per request in get_tools endpoint
- Pass user_group_ids to has_access to avoid repeated group queries
- Optimize access control validation from 1+N to 1+1 query pattern
- Reduce database load when checking multiple tools access permissions

Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
Sihyeon Jang 1 month ago
parent
commit
0503fbd2e3
2 changed files with 9 additions and 4 deletions
  1. 3 1
      backend/open_webui/routers/tools.py
  2. 6 3
      backend/open_webui/utils/access_control.py

+ 3 - 1
backend/open_webui/routers/tools.py

@@ -4,6 +4,7 @@ from typing import Optional
 import time
 import re
 import aiohttp
+from open_webui.models.groups import Groups
 from pydantic import BaseModel, HttpUrl
 from fastapi import APIRouter, Depends, HTTPException, Request, status
 
@@ -71,11 +72,12 @@ async def get_tools(request: Request, user=Depends(get_verified_user)):
         # Admin can see all tools
         return tools
     else:
+        user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user.id)}
         tools = [
             tool
             for tool in tools
             if tool.user_id == user.id
-            or has_access(user.id, "read", tool.access_control)
+            or has_access(user.id, "read", tool.access_control, user_group_ids)
         ]
         return tools
 

+ 6 - 3
backend/open_webui/utils/access_control.py

@@ -1,4 +1,4 @@
-from typing import Optional, Union, List, Dict, Any
+from typing import Optional, Set, Union, List, Dict, Any
 from open_webui.models.users import Users, UserModel
 from open_webui.models.groups import Groups
 
@@ -109,12 +109,15 @@ def has_access(
     user_id: str,
     type: str = "write",
     access_control: Optional[dict] = None,
+    user_group_ids: Optional[Set[str]] = None,
 ) -> bool:
     if access_control is None:
         return type == "read"
 
-    user_groups = Groups.get_groups_by_member_id(user_id)
-    user_group_ids = [group.id for group in user_groups]
+    if user_group_ids is None:
+        user_groups = Groups.get_groups_by_member_id(user_id)
+        user_group_ids = {group.id for group in user_groups}
+
     permission_access = access_control.get(type, {})
     permitted_group_ids = permission_access.get("group_ids", [])
     permitted_user_ids = permission_access.get("user_ids", [])