Repository navigation
fix(pull): close the scheduler and breaker gaps on pull pilots (#2514) - #3222
Merged
Merged
Conversation
Scheduler dispatch-error path (_dispatch_and_record_outcome): a row the backend already handed to the durable queue was read as finalized, so the scheduler published schedule_execution_completed(status=queued), never polled, and lost the real terminal plus retry/validation. Its FAILED write was also unconditional and could land on a row a pull worker had claimed, publishing a false failure that made the run retry-eligible. The FAILED write is now a CAS on (running, claim_token IS NULL); a row it cannot fail is polled to its real terminal through the same background poll the accepted path uses. Dispatch breaker on pilots: the half-open probe took a push slot, running the turn outside the pilot's pool (#1982). It is now enqueued, and the pull sink records the breaker verdict on its CAS-won branch (success, or auth), so a pilot's breaker trips on pulled auth failures and closes on the probe it pulled. Before this the pull sink recorded no verdict at all. Tests: tests/unit/test_2514_scheduler_pull_gaps.py (15), including the breaker-before-enqueue rule on the pull path. Fixes #2514 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 of 4 tasks
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
Closes the remaining gaps from #2514, plus a breaker gap found while writing its test.
_dispatch_and_record_outcome, issue §3). When the dispatch call failed after the backend had already queued the row, the scheduler readqueuedas finished: it publishedschedule_execution_completed(status=queued), never polled, and lost the real terminal and retry/validation. ItsFAILEDwrite was also unconditional, so it could land on a row a pull worker had claimed and publish a false failure that made the run retry-eligible. TheFAILEDwrite is now a CAS onstatus = running AND claim_token IS NULL. A row it cannot fail is polled to its real terminal through the same background poll the accepted path uses.CircuitOpenbefore the persistent-queue branch, so nothing is enqueued.authcounts, any other failure records nothing.Issue §1 (schedule context on pulled cron turns) is already on
devvia #3114. §2 (retry stacking, poison-parked rows) was closed by #2845 / #2980.Changes
src/scheduler/service.py: CAS-guarded failure write, poll fallback,_spawn_pollhelper shared with the accepted path.src/scheduler/database.py:update_execution_status(require_unclaimed=True)addsAND claim_token IS NULL.src/backend/services/capacity_manager.py:pull_exclusivecomputed before the breaker gate; a pilot's probe skips the slot path and is enqueued.src/backend/services/pull_coordination_service.py:_spawn_breaker_verdicton the CAS-won branch ofapply_task_result.docs/memory/architecture/execution.md,docs/memory/feature-flows/dispatch-circuit-breaker.md.Test Plan
cd tests && pytest unit/test_2514_scheduler_pull_gaps.py -v. 15 pass. They were red on the old code for the expected reasons (no poll / claimed row writtenfailed/ probeadmitted/ no verdict recorded).apply_task_result#2643, Production soak-test of pull/work-stealing coordination (#1081) before default-on #1766, feat: per-agent dispatch circuit breaker to prevent backlog poisoning (RELIABILITY-007) #526): 102 pass after rebase ontodev.devat the same base, run side by side. Branch: 30 failed / 2 errors.dev: 36 failed / same 2 errors. Every branch failure also fails ondev.gtm-synthesizeras a real pull pilot). The scheduler's real handler was run with the backend called for real and its reply discarded, to simulate a dispatch timeout:queuedqueued, no pollsuccessreportedfailed, failure event published (worker later overwrote tosuccess)successrunningrowfailed+ failure event, unchangedopenQUEUED, push slots 0 → 0closed, failures 0authfailuretimeoutfailureFixes #2514
🤖 Generated with Claude Code