Skip to content

Propagate signal cancellation through the native run context - #148

Merged
Cianidos merged 5 commits into
mainfrom
feat/issue-135-signal-cancellation
Aug 21, 2026
Merged

Propagate signal cancellation through the native run context#148
Cianidos merged 5 commits into
mainfrom
feat/issue-135-signal-cancellation

Conversation

@Cianidos

@Cianidos Cianidos commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Closes #135

What

The native command path installed no signal-derived cancellation context: pkg/common/shutdown.NewQuitSignal had no production caller, so SIGINT/SIGTERM never reached Cobra, bench.Run, workload setup/iterations, driver queries/transactions, or insert drains.

This change creates one cancellation context at the command boundary and passes it through every operation as one tree.

Approach

  • cmd/stroppy/commands/root.go wires a signal-derived context in Execute(). It uses shutdown.NotifyContext and runs Cobra via rootCmd.ExecuteContext.
  • pkg/common/shutdown gains NotifyContext(parent, force) replacing the disconnected NewQuitSignal. The first SIGINT/SIGTERM cancels the context (graceful teardown); a second signal after cancellation invokes force(ForcedExitCode) as a bounded escape hatch. stop() releases the OS handler and cancels the context.
  • cmd/stroppy/commands/run/run.go passes cmd.Context() through runGoWorkload into bench.Run, instead of context.Background().

The context already threads through bench.Rundriver.Dispatch → each driver's NewDriver/WaitForDB, and through workload Setup/Iterate/Teardown into query/tx APIs and common.RunParallelBatch load workers, so the command boundary was the only missing link.

Exit statuses

  • 130 — graceful cancellation after the first SIGINT/SIGTERM (128 + SIGINT).
  • 1 — forced exit after a second signal during teardown.

Documented in stroppy run --help (Signals section) and the changelog.

Tests

  • pkg/common/shutdown/signal_test.go — cancel-then-force loop, stop releases the handler, and real SIGINT/SIGTERM delivery.
  • pkg/driver/sqldriver/cancel_test.go — cancellation during the driver readiness wait (WaitForDB) and during a blocked query (RunQuery).
  • pkg/bench/cancel_test.go — cancellation reaching workload Setup (schema/load) and stopping a fixed-duration (constant-vus) scenario without leaked workers (timeout-guarded).

All fake drivers / contexts, no live DB.

Acceptance criteria

  • SIGINT and SIGTERM cancel the root command context.
  • The same context or its children reach bench.Run, workload methods, query/transaction APIs, driver readiness waits, and all load workers.
  • First interrupt performs graceful cancellation and teardown (exit 130).
  • A second interrupt provides a bounded forced-exit path (exit 1).
  • Signal handlers are stopped/released after command completion.
  • Tests cover cancellation during readiness wait, schema/load, fixed-duration execution, and a blocked query.
  • CLI returns a documented cancellation exit status without hanging or leaking workers/connections.

Summary by CodeRabbit

  • New Features

    • Added graceful cancellation when SIGINT or SIGTERM is received.
    • A second signal forces immediate termination if shutdown is delayed.
    • Added clear exit statuses for graceful cancellation, forced termination, and other errors.
    • Workload teardown now runs reliably after cancellation, with a 30-second timeout.
  • Documentation

    • Updated command help and the unreleased changelog with signal-handling behavior and exit statuses.
  • Bug Fixes

    • Improved cancellation propagation for workload setup, database waits, queries, and running scenarios.

Wire a signal-derived context at the command boundary (SIGINT/SIGTERM)
into bench.Run, workload setup/iteration/teardown, driver readiness
waits, and load workers as one cancellation tree.

- First signal cancels the root context for graceful teardown.
- A second signal forces immediate exit via a bounded escape hatch.
- Exit statuses: 130 on graceful cancellation, 1 on forced exit.
- Replace the unused NewQuitSignal helper with shutdown.NotifyContext.

Refs #135
@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 055dad04-f4e5-4861-a6f3-d5076517ea6b

📥 Commits

Reviewing files that changed from the base of the PR and between c338c2f and c5163da.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • cmd/stroppy/commands/root.go
  • cmd/stroppy/commands/root_test.go
  • cmd/stroppy/commands/run/run.go
  • pkg/bench/cancel_test.go
  • pkg/bench/runtime.go
  • pkg/common/shutdown/signal.go
  • pkg/common/shutdown/signal_test.go
  • pkg/common/shutdown/singal.go
  • pkg/driver/sqldriver/cancel_test.go
💤 Files with no reviewable changes (1)
  • pkg/common/shutdown/singal.go

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

SIGINT and SIGTERM now cancel command execution and trigger graceful workload teardown. A second signal forces exit with status 2. Cancellation propagates through Go workloads, benchmark execution, workers, and SQL operations. Exit statuses distinguish signal cancellation from other errors.

Changes

Signal cancellation flow

Layer / File(s) Summary
Shutdown context and signal monitoring
pkg/common/shutdown/signal.go, pkg/common/shutdown/signal_test.go
Adds signal-derived contexts, signal-specific exit codes, forced exit handling, signal cleanup, and lifecycle tests.
Command boundary integration
cmd/stroppy/commands/root.go, cmd/stroppy/commands/root_test.go, cmd/stroppy/commands/run/run.go, CHANGELOG.md
Runs commands with the shutdown context, maps cancellation and errors to exit statuses, and documents the signal behavior.
Workload context propagation
cmd/stroppy/commands/run/run.go
Passes the Cobra context through Go workload execution into bench.Run.
Cancellation teardown and driver validation
pkg/bench/runtime.go, pkg/bench/cancel_test.go, pkg/driver/sqldriver/cancel_test.go
Runs teardown once with a detached 30-second context, joins teardown errors, and tests cancellation during setup, workers, teardown, readiness waits, and blocked queries.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to c5163

The change propagates cancellation through command execution and documents graceful and forced interruption behavior; no actionable merge-blocking risk remains beyond normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant OS
  participant RootCommand
  participant RunCommand
  participant bench.Run
  participant WorkloadTeardown
  OS->>RootCommand: SIGINT or SIGTERM
  RootCommand->>RunCommand: Cancel command context
  RunCommand->>bench.Run: Propagate canceled context
  bench.Run->>WorkloadTeardown: Run detached teardown
  WorkloadTeardown-->>bench.Run: Return teardown result
  bench.Run-->>RootCommand: Return combined error
  RootCommand-->>OS: Exit with signal-derived status
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 26 functions across 8 files. (1 skipped: 1 unsupported.) Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: propagating signal cancellation through the native run context.
Linked Issues check ✅ Passed The changes implement the linked issue objectives, including signal propagation, graceful teardown, forced exit, handler release, exit codes, and cancellation tests [#135].
Out of Scope Changes check ✅ Passed The code, tests, helper replacement, and changelog entry all support signal cancellation, cleanup, exit handling, or the linked issue acceptance criteria.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-135-signal-cancellation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

- Run workload Teardown exactly once from a defer under a fresh timeout
  context, so graceful cancellation still performs schema cleanup.
- Prevent the use-after-stop race: drain buffered signals and gate force
  on a stopped flag so a fast double-Ctrl-C cannot turn a clean exit
  into a forced one.
- Derive the graceful exit status from the first signal (130 SIGINT,
  143 SIGTERM) and use 2 as the distinct forced-exit status.
- Fix exit-status read ordering so the signal is queried after the run
  returns.
- Add exit-code mapping and teardown-on-cancel tests; move the changelog
  line to Changed.

Refs #135
Extract magic-number constants (128 signal base, 2 signal buffer size),
name NotifyContext results, annotate the bounded int->int32 signal
conversion, and reflow test blank lines for wsl.
Arbitrate forced exit against shutdown atomically, keep generic cancellations at exit status 1, preserve caller values during teardown, and allow successful Execute callers to run deferred cleanup.
@Cianidos
Cianidos merged commit de6e798 into main Aug 21, 2026
7 checks passed
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.

Propagate signal cancellation through the native run context

1 participant