@@ -206,7 +206,6 @@ def _weights_to_task_counts(
206206 def _execute_strategy (
207207 self ,
208208 network_sk : ScopedKey ,
209- strategy_sk : ScopedKey ,
210209 strategy_state : StrategyState
211210 ) -> StrategyState :
212211 """Execute a single strategy and return updated state."""
@@ -273,46 +272,73 @@ def _execute_strategy(
273272 strategy_state .task_scaling ,
274273 )
275274
275+ taskhub_sk = self .n4js .get_taskhub (network_sk )
276+
276277 # Set task counts for each transformation
277278 for transformation_sk , target_count in task_counts .items ():
278- current_tasks = self .n4js .get_transformation_tasks (transformation_sk )
279- actioned_tasks = [
280- task_sk for task_sk in current_tasks
281- if self .n4js .get_task (task_sk ).status in [TaskStatusEnum .waiting , TaskStatusEnum .running ]
282- ]
283- current_count = len (actioned_tasks )
279+
280+ actioned_tasks = self .n4js .get_transformation_actioned_tasks (transformation_sk , taskhub_sk )
281+ actioned_count = len (actioned_tasks )
284282
285- if target_count > current_count :
286- # Create new tasks
287- for _ in range (target_count - current_count ):
288- self .n4js .create_task (transformation_sk )
283+ if target_count > actioned_count :
284+ # Action additional tasks, creating them as necessary
285+ required = target_count - actioned_count
286+ tasks_to_action = []
287+
288+ # Get existing actionable tasks for the transformation
289+ transformation_tasks = self .n4js .get_transformation_tasks (transformation_sk )
290+ actionable_tasks_status = {
291+ task_sk : status for task_sk , status in zip (transformation_tasks , self .n4js .get_task_status (transformation_tasks ))
292+ if status in [TaskStatusEnum .waiting , TaskStatusEnum .running ] and task_sk not in actioned_tasks
293+ }
294+
295+ # Add actionable tasks not already actioned, starting with those that are already running
296+ for task_sk in actionable_tasks_status :
297+ status = actionable_tasks_status [task_sk ]
298+ if status == TaskStatusEnum .running :
299+ tasks_to_action .append (task_sk )
300+ if len (tasks_to_action ) >= required :
301+ break
302+
303+ # If we still need more, add actionable tasks that are waiting
304+ if len (tasks_to_action ) < required :
305+ for task_sk in actionable_tasks_status :
306+ status = actionable_tasks_status [task_sk ]
307+ if status == TaskStatusEnum .waiting :
308+ tasks_to_action .append (task_sk )
309+ if len (tasks_to_action ) >= required :
310+ break
311+
312+ # Create new tasks if needed
313+ if len (tasks_to_action ) < required :
314+ new_tasks = self .n4js .create_tasks ([transformation_sk ] * (required - len (tasks_to_action )))
315+ tasks_to_action .extend (new_tasks )
316+
317+ self .n4js .action_tasks (tasks_to_action , taskhub_sk )
289318
290- elif target_count < current_count and strategy_state .mode == StrategyModeEnum .full :
291- # Cancel excess tasks (prioritize unclaimed ones)
292- excess = current_count - target_count
319+ elif target_count < actioned_count and strategy_state .mode == StrategyModeEnum .full :
320+ # Cancel excess tasks
321+ excess = actioned_count - target_count
293322 tasks_to_cancel = []
323+
324+ actioned_status = self .n4js .get_task_status (actioned_tasks )
294325
295- # First cancel unclaimed tasks
296- for task_sk in actioned_tasks :
297- task = self .n4js .get_task (task_sk )
298- if task .status == TaskStatusEnum .waiting and task .claim is None :
326+ # First cancel waiting tasks
327+ for task_sk , status in zip (actioned_tasks , actioned_status ):
328+ if status == TaskStatusEnum .waiting :
299329 tasks_to_cancel .append (task_sk )
300330 if len (tasks_to_cancel ) >= excess :
301331 break
302332
303- # Then cancel claimed but not running tasks if needed
333+ # Then cancel running tasks if needed
304334 if len (tasks_to_cancel ) < excess :
305- for task_sk in actioned_tasks :
306- if task_sk not in tasks_to_cancel :
307- task = self .n4js .get_task (task_sk )
308- if task .status == TaskStatusEnum .waiting :
309- tasks_to_cancel .append (task_sk )
310- if len (tasks_to_cancel ) >= excess :
311- break
335+ for task_sk , status in zip (actioned_tasks , actioned_status ):
336+ if status == TaskStatusEnum .running :
337+ tasks_to_cancel .append (task_sk )
338+ if len (tasks_to_cancel ) >= excess :
339+ break
312340
313- # Cancel the selected tasks
314- for task_sk in tasks_to_cancel :
315- self .n4js .cancel_task (task_sk )
341+ self .n4js .cancel_tasks (tasks_to_cancel , taskhub_sk )
316342
317343 # Update strategy state
318344 strategy_state .last_iteration = datetime .utcnow ()
@@ -327,10 +353,11 @@ def _execute_strategy(
327353 # Strategy execution failed
328354 logger .exception (f"Strategy execution failed for network { network_sk } " )
329355
356+ strategy_state .last_iteration = datetime .utcnow ()
357+ strategy_state .last_iteration_result_count = current_result_count
330358 strategy_state .status = StrategyStatusEnum .error
331- strategy_state .exception = (type ( e ). __name__ , str (e ))
359+ strategy_state .exception = (e . __class__ . __qualname__ , str (e ))
332360 strategy_state .traceback = traceback .format_exc ()
333- strategy_state .last_iteration = datetime .utcnow ()
334361 strategy_state .iterations += 1
335362
336363 return strategy_state
@@ -353,7 +380,7 @@ def cycle(self):
353380 with ProcessPoolExecutor (max_workers = self .max_workers ) as executor :
354381 # Submit all strategy executions
355382 future_to_network = {
356- executor .submit (self ._execute_strategy , network_sk , strategy_sk , strategy_state ): network_sk
383+ executor .submit (self ._execute_strategy , network_sk , strategy_state ): network_sk
357384 for network_sk , strategy_sk , strategy_state in ready_strategies
358385 }
359386 network_to_future = {value : key for key , value in future_to_network .items ()}
0 commit comments