Skip to content

copier, applier: a cancel with a cause still reads as a cancellation - #280

Draft
Kiran01bm wants to merge 1 commit into
mainfrom
kiran01bm/cs-pd3-cancel-cause
Draft

Kiran01bm wants to merge 1 commit into
mainfrom
kiran01bm/cs-pd3-cancel-cause

Conversation

@Kiran01bm

Copy link
Copy Markdown
Collaborator

A run a caller stops through context.WithCancelCause is now coded cancelled-by-caller, whatever cause the caller cancelled with.

Why

migrate.copySwapFailureCode reads a stop as the caller's when the error answers errors.Is(err, context.Canceled). The copier returned context.Cause(ctx) from Run, and the catch-up did the same from stopped and holdFlushes, so a caller that cancelled with a cause of its own — an orchestrator saying why it stopped the run — got that cause back bare, with no context.Canceled in the chain. The verdict then coded the stop execution-failed, and an adapter routing on the code treated a deliberate stop as a fault. A plain cancel() was unaffected, since its cause is context.Canceled.

What

  • pkg/copier: Run reports why its workers stopped through stopReason: a worker's own error stands as it is, even when the caller cancelled after it; the caller's ending of ctx is reported through endedWith.
  • pkg/applier: stopped and holdFlushes report the context's end through endedWith.
  • endedWith (one small copy per package) returns ctx.Err() as it stands for a plain cancel or a passed deadline, and for a cause of the caller's own returns the cause wrapped together with ctx.Err() — "<cause> (context canceled)" — so errors.Is answers for the cause and for context.Canceled alike.
  • Tests: copier and applier unit tests for the three readings (plain, deadline, cause; plus a worker error that precedes the cancel); the copier, runner, and copyAndSwap cancellation tests now cancel with a cause and assert both readings, and a new copyAndSwap test asserts the verdict code is cancelled-by-caller — it reports execution-failed without the fix. A new live-run test cancels with a cause during catch-up and resumes to done.
  • Docs: the Run outcome and verdict tables in schemabot-integration.md, and the pkg/applier row in the design doc.

One knock-on inside the runner: captureOutcome drops the fill's error when it is the capture's cancellation (errors.Is(fillErr, context.Canceled)). A catch-up failure that cancels the fill now wraps Canceled, so the fill's "my context was cancelled" error is dropped and the catch-up's failure is reported alone, where before both were reported. The classification of every other runner stop (errCaptureEnded, the saver's stop) is unchanged; the existing live-run tests cover them.

Before / after

An orchestrator runs a copy-and-swap and stops it mid-copy with cancel(ownerStop) where ownerStop = errors.New("owner is ready to cut over"). Before, the verdict read as a fault; after, it reads as the caller's stop, and the error still names the caller's cause. Nothing about what the stop leaves behind changes: the row stands in copying at its last save either way, and the next run resumes from it.

Before                                              After
──────                                              ─────
orchestrator: cancel(ownerStop)                     orchestrator: cancel(ownerStop)
        │                                                   │
        ▼                                                   ▼
copier.Run returns                                  copier.Run returns
  context.Cause(ctx)                                  endedWith(ctx)
  = ownerStop                                         = ownerStop (context canceled)
        │                                                   │
        ▼                                                   ▼
errors.Is(err, context.Canceled) → false            errors.Is(err, context.Canceled) → true
errors.Is(err, ownerStop)        → true             errors.Is(err, ownerStop)        → true
        │                                                   │
        ▼                                                   ▼
verdict: failed, code: execution-failed             verdict: failed, code: cancelled-by-caller
  adapter treats the stop as a fault                  adapter treats the stop as its own

A plain cancel() reads the same on both sides: endedWith returns context.Canceled as it stands, not doubled.

🤖 Generated with Amp (Claude Opus 4.6)

A caller that stopped a run through context.WithCancelCause got its cause
back bare, so the verdict coded the stop execution-failed instead of
cancelled-by-caller. The copier and the catch-up now report the cause
wrapped together with ctx.Err(), so errors.Is answers for both.
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.

1 participant