perf(models): batch-fetch function valves to eliminate N+1 queries (#22301)
* perf(models): batch-fetch function valves to eliminate N+1 queries get_action_priority() called Functions.get_function_valves_by_id() individually for every action on every model — an N+1 query pattern that issued one DB round-trip per (action x model) pair. Add Functions.get_function_valves_by_ids() that fetches all valves in a single WHERE IN query, then look up each action's valves from the pre-fetched dict inside get_action_priority(). No functional change — same priority resolution, same sort order. * Update models.py * Update models.py
This commit is contained in:
@@ -308,6 +308,31 @@ class FunctionsTable:
|
|||||||
log.exception(f"Error getting function valves by id {id}: {e}")
|
log.exception(f"Error getting function valves by id {id}: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_function_valves_by_ids(
|
||||||
|
self, ids: list[str], db: Optional[Session] = None
|
||||||
|
) -> dict[str, dict]:
|
||||||
|
"""
|
||||||
|
Batch fetch valves for multiple functions in a single query.
|
||||||
|
Returns a dict mapping function_id -> valves dict.
|
||||||
|
Functions without valves are mapped to {}.
|
||||||
|
"""
|
||||||
|
if not ids:
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
with get_db_context(db) as db:
|
||||||
|
functions = (
|
||||||
|
db.query(Function.id, Function.valves)
|
||||||
|
.filter(Function.id.in_(ids))
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
f.id: (f.valves if f.valves else {}) for f in functions
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
log.exception(f"Error batch-fetching function valves: {e}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def update_function_valves_by_id(
|
def update_function_valves_by_id(
|
||||||
self, id: str, valves: dict, db: Optional[Session] = None
|
self, id: str, valves: dict, db: Optional[Session] = None
|
||||||
) -> Optional[FunctionValves]:
|
) -> Optional[FunctionValves]:
|
||||||
|
|||||||
@@ -331,11 +331,17 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None)
|
|||||||
elif meta.get(key) is None:
|
elif meta.get(key) is None:
|
||||||
meta[key] = copy.deepcopy(value)
|
meta[key] = copy.deepcopy(value)
|
||||||
|
|
||||||
|
# Batch-fetch all function valves in one query to avoid N+1 DB hits
|
||||||
|
# inside get_action_priority (previously called per action × per model).
|
||||||
|
all_function_valves = Functions.get_function_valves_by_ids(
|
||||||
|
list(all_function_ids)
|
||||||
|
)
|
||||||
|
|
||||||
def get_action_priority(action_id):
|
def get_action_priority(action_id):
|
||||||
try:
|
try:
|
||||||
function_module = request.app.state.FUNCTIONS.get(action_id)
|
function_module = request.app.state.FUNCTIONS.get(action_id)
|
||||||
if function_module and hasattr(function_module, "Valves"):
|
if function_module and hasattr(function_module, "Valves"):
|
||||||
valves_db = Functions.get_function_valves_by_id(action_id)
|
valves_db = all_function_valves.get(action_id)
|
||||||
valves = function_module.Valves(**(valves_db if valves_db else {}))
|
valves = function_module.Valves(**(valves_db if valves_db else {}))
|
||||||
return getattr(valves, "priority", 0)
|
return getattr(valves, "priority", 0)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
Reference in New Issue
Block a user