@@ -46,14 +46,16 @@ def _build_node_simple_sort_clause(sort_option: NodeSimpleSortOption):
4646 return column .desc () if sort_option .value .startswith ("-" ) else column .asc ()
4747
4848
49- async def load_node_attrs (node : Node ):
49+ async def load_node_attrs (node : Node , * , load_usage_logs : bool = True ):
50+ if not load_usage_logs :
51+ return
5052 try :
5153 await node .awaitable_attrs .usage_logs
5254 except AttributeError :
5355 pass
5456
5557
56- async def get_node (db : AsyncSession , name : str ) -> Node | None :
58+ async def get_node (db : AsyncSession , name : str , * , load_usage_logs : bool = True ) -> Node | None :
5759 """
5860 Retrieves a node by its name.
5961
@@ -66,11 +68,11 @@ async def get_node(db: AsyncSession, name: str) -> Node | None:
6668 """
6769 node = (await db .execute (select (Node ).where (Node .name == name ))).unique ().scalar_one_or_none ()
6870 if node :
69- await load_node_attrs (node )
71+ await load_node_attrs (node , load_usage_logs = load_usage_logs )
7072 return node
7173
7274
73- async def get_node_by_id (db : AsyncSession , node_id : int ) -> Node | None :
75+ async def get_node_by_id (db : AsyncSession , node_id : int , * , load_usage_logs : bool = True ) -> Node | None :
7476 """
7577 Retrieves a node by its ID.
7678
@@ -83,13 +85,15 @@ async def get_node_by_id(db: AsyncSession, node_id: int) -> Node | None:
8385 """
8486 node = (await db .execute (select (Node ).where (Node .id == node_id ))).unique ().scalar_one_or_none ()
8587 if node :
86- await load_node_attrs (node )
88+ await load_node_attrs (node , load_usage_logs = load_usage_logs )
8789 return node
8890
8991
9092async def get_nodes (
9193 db : AsyncSession ,
9294 query : NodeListQuery ,
95+ * ,
96+ load_usage_logs : bool = True ,
9397) -> tuple [list [Node ], int ]:
9498 """
9599 Retrieves nodes based on optional status, enabled, id, and search filters.
@@ -143,8 +147,9 @@ async def get_nodes(
143147 # Order by created_at and id for consistent results
144148 stmt = stmt .order_by (Node .created_at .asc (), Node .id .asc ())
145149
146- # Eagerly load usage_logs to avoid N+1 queries (one extra SELECT per node)
147- stmt = stmt .options (selectinload (Node .usage_logs ))
150+ # Eagerly load usage_logs for API lifetime_* fields (skip for jobs/connect)
151+ if load_usage_logs :
152+ stmt = stmt .options (selectinload (Node .usage_logs ))
148153
149154 db_nodes = (await db .execute (stmt )).unique ().scalars ().all ()
150155
@@ -208,7 +213,7 @@ async def get_limited_nodes(db: AsyncSession) -> list[Node]:
208213 Returns:
209214 list[Node]: Nodes that should be limited
210215 """
211- query = select (Node ).options ( selectinload ( Node . usage_logs )). where (
216+ query = select (Node ).where (
212217 and_ (
213218 Node .status .in_ ([NodeStatus .error , NodeStatus .connected , NodeStatus .connecting ]),
214219 Node .is_limited ,
@@ -503,7 +508,6 @@ async def update_node_status(
503508 # If the instance was detached (e.g., used across sessions), re-fetch it
504509 db_node = (await db .execute (select (Node ).where (Node .id == db_node .id ))).scalar_one ()
505510
506- await load_node_attrs (db_node )
507511 return db_node
508512
509513
@@ -799,10 +803,11 @@ async def bulk_reset_node_usage(db: AsyncSession, nodes: list[Node]) -> list[Nod
799803 # Re-fetch all nodes in a single query instead of N individual refreshes
800804 node_ids = [node .id for node in nodes ]
801805 refreshed = (
802- await db .execute (
803- select (Node ).options (selectinload (Node .usage_logs )).where (Node .id .in_ (node_ids ))
804- )
805- ).unique ().scalars ().all ()
806+ (await db .execute (select (Node ).options (selectinload (Node .usage_logs )).where (Node .id .in_ (node_ids ))))
807+ .unique ()
808+ .scalars ()
809+ .all ()
810+ )
806811 # Preserve input order
807812 refreshed_by_id = {n .id : n for n in refreshed }
808813 return [refreshed_by_id [nid ] for nid in node_ids if nid in refreshed_by_id ]
0 commit comments