Summary
Backend housekeeping in ScriptTemplateHPCManager.create_compute_services (health checks, server reconciliation, clearing completed jobs) only runs on scale-up cycles. On cycles where the queue is drained, the manager is at capacity, or sizing collapses to target == 0, the maintenance never runs. Surfaced while reviewing #3 (adopting the upstream ComputeManager sizing logic).
Details
After #3, the base ComputeManager.cycle() (alchemiscale compute/manager.py) is the only caller of create_compute_services, and it gates the call:
if total_services < self.settings.max_compute_services and num_tasks > 0:
target = self._compute_jobs_to_create(...)
if target > 0:
self.create_compute_services(data, target) # sole call site
But ScriptTemplateHPCManager.create_compute_services (alchemiscale_hpc/base.py) runs per-cycle housekeeping before the submit loop:
self.batch_api.check_job_health()
self.batch_api.verify_running_jobs(server_job_names)
self.batch_api.clear_successful_jobs()
if self.batch_api.jobs_pending():
return 0
# ... submit up to `target` scripts
There is no other maintenance hook in cycle(). So whenever num_tasks == 0, the manager is at capacity, or target == 0:
check_job_health() does not run
verify_running_jobs() does not reconcile running jobs against server state
clear_successful_jobs() does not clear completed jobs
This matters most while the task queue drains: completed jobs aren't cleared and running jobs aren't reconciled until new tasks arrive and scale-up triggers again.
The health checks have always lived inside create_compute_services, but adopting the #502 base — which added the num_tasks > 0 and target > 0 gating — is what couples maintenance to the scale-up decision. Worth confirming against pre-#502 behavior: if create_compute_services previously ran every OK cycle, this is a behavioral regression at idle.
Test gap
The unit tests test_create_compute_services (for verify_running_jobs / clear_successful_jobs) call create_compute_services(..., num_tasks=0, target=0) and assert the maintenance fires. The base will never call the method in that state, so these green tests assert behavior production never reaches — masking the gap above.
Suggested fix
Split the housekeeping out of create_compute_services into a dedicated maintenance step that the base ComputeManager.cycle() invokes every OK cycle (regardless of target), leaving create_compute_services purely about submitting target scripts. This likely requires a coordinated change to the upstream alchemiscale.compute.manager.ComputeManager base, so it may be cleaner as a follow-up than to fold into #3.
Re-home the two affected unit tests onto the new maintenance hook so they exercise a reachable code path.
Acceptance criteria
Found during review of #3.
Summary
Backend housekeeping in
ScriptTemplateHPCManager.create_compute_services(health checks, server reconciliation, clearing completed jobs) only runs on scale-up cycles. On cycles where the queue is drained, the manager is at capacity, or sizing collapses totarget == 0, the maintenance never runs. Surfaced while reviewing #3 (adopting the upstreamComputeManagersizing logic).Details
After #3, the base
ComputeManager.cycle()(alchemiscalecompute/manager.py) is the only caller ofcreate_compute_services, and it gates the call:But
ScriptTemplateHPCManager.create_compute_services(alchemiscale_hpc/base.py) runs per-cycle housekeeping before the submit loop:There is no other maintenance hook in
cycle(). So whenevernum_tasks == 0, the manager is at capacity, ortarget == 0:check_job_health()does not runverify_running_jobs()does not reconcile running jobs against server stateclear_successful_jobs()does not clear completed jobsThis matters most while the task queue drains: completed jobs aren't cleared and running jobs aren't reconciled until new tasks arrive and scale-up triggers again.
The health checks have always lived inside
create_compute_services, but adopting the #502 base — which added thenum_tasks > 0 and target > 0gating — is what couples maintenance to the scale-up decision. Worth confirming against pre-#502 behavior: ifcreate_compute_servicespreviously ran everyOKcycle, this is a behavioral regression at idle.Test gap
The unit tests
test_create_compute_services(forverify_running_jobs/clear_successful_jobs) callcreate_compute_services(..., num_tasks=0, target=0)and assert the maintenance fires. The base will never call the method in that state, so these green tests assert behavior production never reaches — masking the gap above.Suggested fix
Split the housekeeping out of
create_compute_servicesinto a dedicated maintenance step that the baseComputeManager.cycle()invokes everyOKcycle (regardless oftarget), leavingcreate_compute_servicespurely about submittingtargetscripts. This likely requires a coordinated change to the upstreamalchemiscale.compute.manager.ComputeManagerbase, so it may be cleaner as a follow-up than to fold into #3.Re-home the two affected unit tests onto the new maintenance hook so they exercise a reachable code path.
Acceptance criteria
verify_running_jobs/clear_successful_jobsrun everyOKcycle, independent of scale-upcreate_compute_servicesis responsible only for submittingtargetscriptsFound during review of #3.