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:
Classic298
2026-03-06 22:56:01 +01:00
committed by GitHub
parent 200fb093b1
commit d93cb3658d
2 changed files with 32 additions and 1 deletions

View File

@@ -308,6 +308,31 @@ class FunctionsTable:
log.exception(f"Error getting function valves by id {id}: {e}")
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(
self, id: str, valves: dict, db: Optional[Session] = None
) -> Optional[FunctionValves]:

View File

@@ -331,11 +331,17 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None)
elif meta.get(key) is None:
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):
try:
function_module = request.app.state.FUNCTIONS.get(action_id)
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 {}))
return getattr(valves, "priority", 0)
except Exception: