CAP-002 Phase 2 (Prerequisite #5) — cross-process run-abort signal channel over Redis pub/sub.
Why this exists: when a user clicks "Abort" on a sharded run, the abort
route in backend/src/routes/runs.js only knows about the local replica's
workerAbortControllers. Shard workers running on other replicas would
keep executing until natural completion, leaving orphan jobs and burning
compute. This module fans the abort signal out to every replica so each
one can cancel any matching in-flight job via its local
workerAbortControllers map.
Channel contract
sentri:run-abort — JSON-encoded { runId: string, origin: string }.
Published by the abort route; subscribed by every shard worker at boot.
Self-echo suppression
Each Node process generates a fresh origin id at module load (UUID-shape
hex string, same pattern as routes/sse.js:_instanceId). The publisher
stamps it on every message; the subscriber drops messages whose origin
matches its own. This prevents the same-process round-trip — the local
fast-path in routes/runs.js already aborted the controller before
publishing, so re-firing it via the channel would be wasteful (and could
race against the cleanup that the route performed synchronously).
Degraded mode (no Redis)
When REDIS_URL is unset or isRedisAvailable() returns false:
publishRunAbort()returnsfalseand is a clean no-op.subscribeToRunAborts()returnsfalse; the worker keeps its in-processworkerAbortControllersmap as the only abort path.
Single-replica deployments therefore behave bit-for-bit identically to
pre-Phase-2 today — no Redis, no cross-process abort, but local aborts
still work because the abort route always calls
workerAbortControllers.get(runId)?.controller.abort() before delegating
to this channel. This module is purely additional cross-replica reach.
Test coverage
Gated on REDIS_URL (see backend/tests/run-abort-pubsub.test.js) — a
single-process unit test can't exercise the cross-process semantics; we
spin up two real subscriber clients against a real Redis instance and
assert (a) both receive the message, (b) the publisher self-suppresses.
- Source:
Members
(static, constant) RUN_ABORT_CHANNEL
Redis channel name. Single channel for all runs — payload carries the runId.
- Source:
(static, constant) RUN_ABORT_ORIGIN :string
Per-process origin id used for self-echo suppression. Generated once at
module load. The shape (16-byte hex) matches routes/sse.js's
_instanceId pattern so future operators reading process telemetry see a
consistent identifier format across both pub/sub channels.
Type:
- string
- Source:
Methods
(static) __resetForTest() → {void}
Test-only: clear the registered handlers and reset subscribed state.
Used by run-abort-pubsub.test.js to isolate test cases. Never call
this from production code — it leaves the redisSub client subscribed
but with no handlers registered, which is wasteful (cheap, but pointless).
- Source:
Returns:
- Type
- void
(static) publishRunAbort(runId) → {Promise.<boolean>}
Publish a run-abort signal to every replica subscribed to the channel.
Same-replica subscribers self-suppress via the origin stamp — the
caller is responsible for the local-fast-path abort before publishing.
Parameters:
| Name | Type | Description |
|---|---|---|
runId |
string |
- Source:
Returns:
Resolves to true when the message was
published, false when Redis is unavailable (degraded single-replica
mode — caller should rely on the local workerAbortControllers only).
- Type
- Promise.<boolean>
(static) subscribeToRunAborts(opts) → {boolean}
Subscribe this process to the run-abort channel. Idempotent — calling twice is safe; only the first call performs the SUBSCRIBE. Every registered handler is invoked with the runId whenever a cross-replica abort arrives (own-origin messages are filtered out before dispatch).
Workers call this at boot from startWorker(). The handler typically
looks up workerAbortControllers.get(runId) and aborts the local
controller — exactly the same code path the in-process abort route uses.
Parameters:
| Name | Type | Description | ||||||
|---|---|---|---|---|---|---|---|---|
opts |
Object |
Properties
|
- Source:
Returns:
true if subscribed (or already was), false when
Redis is unavailable (caller should keep using the local-only path).
- Type
- boolean