Skip to content

fix: address code review issues for robust cancel#54

Merged
bodymindarts merged 6 commits intotask/robust-cancel-v2-019cf300from
task/fix-cancel-top5-019cf365
Mar 16, 2026
Merged

fix: address code review issues for robust cancel#54
bodymindarts merged 6 commits intotask/robust-cancel-v2-019cf300from
task/fix-cancel-top5-019cf365

Conversation

@bodymindarts
Copy link
Copy Markdown
Member

Summary

Addresses 5 code review issues from PR #52 plus a rename:

  • Idempotent cancel_job: Job::cancel_job() now checks cancelled() before pushing events, preventing duplicate ExecutionCancelled/JobCancelled events from the dispatcher + monitor race
  • Atomic pending cancel: The DELETE of a pending execution row and event recording now happen in a single transaction, preventing partial state on crash
  • Integration tests: Two new tests — cooperative cancel (runner detects via select! on cancellation_requested()) and force-abort (runner blocks forever, 1s cancel timeout triggers abort)
  • Shared finalization: Extracted finalize_cancelled_job() in repo.rs, called from both cancel_and_complete_job (dispatcher) and force_cancel_job (poller)
  • Compile-time queries: Restored sqlx::query_as! macro in poll_jobs with explicit column type overrides
  • Rename: RunningJobRegistryCancellationTokens — the struct only maps job IDs to cancellation tokens, not general running-job info

Test plan

  • cargo fmt passes
  • cargo clippy --all-targets -- -D warnings passes
  • All 38 tests pass via cargo nextest run
  • New test_cooperative_cancel verifies cooperative cancel path
  • New test_force_abort_cancel verifies force-abort path with short timeout

🤖 Generated with Claude Code

bodymindarts and others added 6 commits March 15, 2026 22:38
When a cancel signal fires, both the dispatcher (cooperative cancel) and
the monitor task (force-abort timeout) can race to finalize the same job.
Both paths call job.cancel_job() which unconditionally pushed
ExecutionCancelled + JobCancelled events, leading to duplicate events.

Now cancel_job() checks if the job is already cancelled before pushing
events, making the operation safely idempotent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, the cancel_job() method for pending jobs performed the
DELETE of the execution row on the raw pool, then opened a new
transaction to record cancellation events. A crash between the DELETE
and the commit would leave the execution row gone but the job entity
never marked as cancelled.

Now both the DELETE and event recording happen inside the same atomic
operation, matching the pattern used by cancel_and_complete_job in
the dispatcher.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Test A (cooperative cancel): Creates a runner that select!s on
cancellation_requested() and returns JobCompletion::Cancelled when
triggered. Verifies the job ends up cancelled and completed.

Test B (force-abort): Creates a runner that blocks forever (ignores
cancellation). Configures a short cancel_timeout (1 second) via
JobPollerConfig. Verifies the monitor task force-aborts and records
cancellation events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The cancel_and_complete_job (dispatcher) and force_cancel_job (poller)
functions were nearly identical: begin_op → find job → DELETE execution
row → cancel_job() → update → commit. If one was updated the other
would diverge.

Extract finalize_cancelled_job() in repo.rs and call it from both
sites.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The sqlx::query_as! macro was replaced with runtime sqlx::query_as when
adding the cancelled_at IS NULL filter, losing compile-time schema
validation.

Restore query_as! with explicit column type overrides (id as "id: JobId")
and pass job_type_strings with `as _` cast for the ANY($4) parameter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The struct is a map from JobId to CancellationToken — it doesn't track
general running-job information (that's JobTracker's role). The new name
better describes its narrow responsibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@bodymindarts bodymindarts merged commit b1cd6ef into task/robust-cancel-v2-019cf300 Mar 16, 2026
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