Skip to content

fix(tern): cancel orphaned pending tasks so they stop blocking new applies#750

Merged
aparajon merged 2 commits into
mainfrom
armand/orphaned-task-sweep
Jul 17, 2026
Merged

fix(tern): cancel orphaned pending tasks so they stop blocking new applies#750
aparajon merged 2 commits into
mainfrom
armand/orphaned-task-sweep

Conversation

@aparajon

Copy link
Copy Markdown
Collaborator

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.

new dispatch ── conflict check finds a non-terminal task
        │
        ├── task is pending AND its apply is terminal ──► cancel the orphan
        │                                                 (durably, + apply-log
        │                                                 entry) and proceed
        │
        └── anything else ──────────────────────────────► keep blocking
            (apply still active, stopped/retryable         (fail closed)
            task with a checkpoint, storage error,
            missing apply row)

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)

…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>
Copilot AI review requested due to automatic review settings July 17, 2026 22:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread pkg/tern/local_apply.go
…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
aparajon marked this pull request as ready for review July 17, 2026 22:55
@aparajon
aparajon merged commit c06926c into main Jul 17, 2026
32 checks passed
@aparajon
aparajon deleted the armand/orphaned-task-sweep branch July 17, 2026 23:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants