fix(tern): cancel orphaned pending tasks so they stop blocking new applies#750
Merged
Conversation
…plies A pending task whose apply already reached a terminal state can never start: a terminal apply is not claimable, so no drive will ever pick the task up, and pending means no engine work or checkpoint exists. Left alone such a task blocks every later apply targeting its database as phantom active work, permanently refusing dispatches. The conflict check now cancels these orphans — durably, with an apply-log entry on the owning apply — before consulting the engine, and admits the new apply. Any uncertainty (a non-pending state, a storage failure, or a missing apply row) leaves the task blocking as before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes a dispatch deadlock in the local tern client where a pending task whose parent apply is already terminal can never be driven again, but still causes the “schema change already in progress” conflict gate to block all future applies on that database. The PR adds a self-healing path that cancels only this provably-unstarted orphan shape and proceeds, while continuing to fail-closed on any uncertainty.
Changes:
- Cancel orphaned pending tasks during conflict detection when their parent apply is already in a terminal state.
- Add unit tests covering terminal/active/uncertain apply-lookup cases for orphan resolution.
- Add an integration test asserting the orphan is durably cancelled, logged to apply_logs, and does not touch the engine.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| pkg/tern/local_apply.go | Adds orphaned-pending-task cancellation to the conflict-check path. |
| pkg/tern/local_apply_conflict_test.go | Adds unit tests validating orphan cancellation and fail-closed behavior on uncertainty. |
| pkg/tern/local_orphaned_task_integration_test.go | Adds an integration test to prove durable cancellation + apply-log recording without engine interaction. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ably written A failed cancellation write now keeps the task blocking and restores its in-memory state so a later conflict check retries cleanly, instead of admitting the new apply while storage still records the orphan as active work. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
aparajon
marked this pull request as ready for review
July 17, 2026 22:55
aparajon
requested review from
Kiran01bm,
eeSeeGee,
jayjanssen and
morgo
as code owners
July 17, 2026 22:55
morgo
approved these changes
Jul 17, 2026
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.
What broke
A pending task whose apply already reached a terminal state is orphaned: terminal applies are never claimed again, so no drive will ever start the task. The conflict check that guards new dispatches ("is a schema change already in progress?") saw the non-terminal task and refused — permanently. Every dispatch to that database failed with "schema change already in progress", and nothing in the system could ever clear it.
The fix
The conflict check now recognizes this shape and self-heals. Only a pending task qualifies — pending provably means no engine work or checkpoint ever existed. Anything uncertain keeps blocking.
Stopped and failed-retryable tasks are untouched — they own engine checkpoints and an operator retry budget. Databases already blocked by past orphans unblock automatically on their next dispatch, with the cancellation recorded in the owning apply's durable logs.
Completes the set with #749 (sharded dispatches drive their DDL) and #748 (a drive that can't load its tasks refuses to complete): #749 fixes the cause, #748 makes any recurrence loud, and this PR clears the blockage left behind.
🤖 Generated with Claude Code (Claude Fable 5)