Skip to content

Cancel Flow

Cancellation commits to the database first and signals NATS second, and that order is the whole design. A compare-and-swap on runs.version marks the run terminal, sets its cancellation cause and ended_at, and releases the scheduler claim. Only then is a message published to the CANCEL stream, as a fast path for pull-mode workers.

If that publish fails the run stays cancelled. Executors reconcile from the database, so the signal is an optimisation, not a prerequisite. A push executor never reads CANCEL at all — PushExecutor.isRunCancelled calls store.GetRun before each step, so a cancelled run stops at the next step boundary.

Ironflow cancel flow

The publish carries a message ID (cancel-{runID}), so a repeated cancel does not duplicate the signal. Cancelling an already-terminal run is refused outright rather than treated as a no-op. The same path also runs cancel_on cleanup, records an audit event with the (truncated) reason, and publishes the lifecycle event to PUBSUB subscribers.