fix: progress-based job drain at acquisition end + JobRunner death detection#597
Open
hongquanli wants to merge 1 commit into
Open
fix: progress-based job drain at acquisition end + JobRunner death detection#597hongquanli wants to merge 1 commit into
hongquanli wants to merge 1 commit into
Conversation
Two failure modes from the 2026-07-12 backpressure investigation (acquisition "stuck with jobs queue full", lost images at acquisition end): 1. _finish_jobs waited on pending saves with a fixed 10s deadline on every acquisition end (natural or aborted). Under backpressure saturation the pending count equals the job limit by construction, so whenever pending x per-job save time exceeded 10s the tail of the acquisition was killed and its images silently lost - even while jobs were completing steadily (observed 2026-07-05: 17 jobs drained in 10s, ~20 pending, remainder abandoned). The drain deadline is now progress-based (drain_runners): every completion resets the stall clock; jobs are abandoned only when nothing completes for stall_timeout_s, and any abandonment counts into _acquisition_error_count so the run ends as completed_with_errors instead of reporting clean success. 2. A dead JobRunner subprocess was undetectable: backpressure counters are only decremented in the subprocess, so its death froze them at the limit and the acquisition crawled at one 30s throttle timeout per frame, presenting as a permanently-full job queue. Dead runners with pending jobs are now detected (find_dead_runners) at the throttle site (abort with a clear error) and during the drain (abandoned immediately instead of waiting out the stall timeout). Adds JobRunner.pending_count(); has_pending delegates to it and is now safe after shutdown(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
_finish_jobsused a fixed 10 s deadline to wait for pending save jobs on every acquisition end (natural completion AND abort), then killed the runners. Under backpressure saturation the pending count equals the job limit by construction, so wheneverpending × per-job save timeexceeded 10 s the tail of the acquisition was abandoned and its images silently lost — even while jobs were completing steadily. The drain deadline is now progress-based: every completed job resets the stall clock, so a full-but-draining queue gets as long as it needs; jobs are abandoned only when nothing completes forstall_timeout_s._acquisition_error_count, so the run ends ascompleted_with_errorsinstead of reporting clean success.Evidence
Observed 2026-07-05 (headless run, simulation): queue pinned at jobs=20/20 (MB=662.7/10000 — the byte budget was at 6.6 %; the job cap was the binding limit), abort requested, 17 jobs drained in the 10 s window at ~0.55 s each, ~20 were pending →
Timed out after 10 [s] waiting for jobs to finish. Pending jobs for SaveImageJob abandoned!!!. Full investigation write-up: AI-docsSquid/to-do/2026-07-12-backpressure-drain-findings.md.Changes
control/core/job_processing.py: new module-leveldrain_runners()(progress-based drain; kills a runner only on stall; abandons a dead runner's jobs immediately),find_dead_runners(),DrainResult; newJobRunner.pending_count()(has_pending()delegates to it and is now safe aftershutdown()instead of raising on the cleared counter).control/core/multi_point_worker.py:_finish_jobsusesdrain_runners(timeout_s→stall_timeout_s), logs abandonment cause per job class, counts abandoned jobs into_acquisition_error_count; new_abort_if_job_runners_dead()checked at the backpressure throttle site; background runner shutdown uses a short fixed join timeout (runners are idle after a clean drain, killed otherwise).Test plan
tests/control/core/test_job_drain.pywritten first and watched fail (headline regression: 6 × 0.4 s jobs drain fully withstall_timeout_s=1.0— the old fixed deadline would have abandoned them; stall abandonment; dead-runner immediate abandonment;find_dead_runnerscases;pending_countlifecycle)pytest tests/control/core— 331 passed, 2 skippedblack --config pyproject.tomlcleanNotes
🤖 Generated with Claude Code