Skip to content

feat(activity): notify Activities when the Worker starts shutting down - #2264

Open
rohitsudhakar1 wants to merge 1 commit into
temporalio:mainfrom
rohitsudhakar1:feat-1739-worker-shutting-down
Open

feat(activity): notify Activities when the Worker starts shutting down#2264
rohitsudhakar1 wants to merge 1 commit into
temporalio:mainfrom
rohitsudhakar1:feat-1739-worker-shutting-down

Conversation

@rohitsudhakar1

Copy link
Copy Markdown

Closes #1739.

What #1739 asked for

workerShuttingDown: Promise<never> and workerShuttingDownSignal: AbortSignal on the Activity Context, mirroring how cancellation is handled, plus matching top-level exports from activity/index.ts.

What this implements

  • Context.workerShuttingDown — a Promise<never> that rejects with CancelledFailure('WORKER_SHUTDOWN') as soon as the Worker initiates shutdown, and Context.workerShuttingDownSignal — the corresponding AbortSignal. These mirror the existing cancelled / cancellationSignal pair.
  • Top-level exports workerShuttingDown() and workerShuttingDownSignal() from @temporalio/activity, as shortcuts for Context.current().…, consistent with the existing cancelled() / cancellationSignal() helpers.
  • The Worker owns a single AbortController, aborted from the state setter as soon as the Worker leaves RUNNING, so every shutdown path is covered — including error-driven ones — rather than just the happy-path shutdown() call. Each Activity derives its own controller from that shared signal, so user code can register listeners without accumulating them on the Worker's signal for the Worker's whole lifetime.
  • A shutdown notification is deliberately not a cancellation: cancellationSignal and cancellationDetails are left untouched and the Activity remains free to complete normally. Unlike cancellation, an Activity also does not need to heartbeat() to learn about shutdown.
  • MockActivityEnvironment.notifyWorkerShuttingDown(), so Activities that use the new API are testable without standing up a real Worker. This wasn't in the issue text, but the API is untestable in user code without it.

Both Context members are documented as @experimental.

Tests

Four new tests in packages/testing/src/__tests__/test-mockactivityenv.ts:

  1. the notification resolves workerShuttingDown and aborts workerShuttingDownSignal, and the Activity still completes normally afterwards;
  2. it does not cancel the Activity — cancellationSignal stays un-aborted, cancellationDetails() stays undefined, and sleep() still works;
  3. the idiomatic Promise.race([work, workerShuttingDown()]) bail-out pattern;
  4. the notification is idempotent and applies to Activities started after shutdown was initiated.

These provably fail without the change: reverting only the four implementation files and rebuilding fails compilation on every new API reference (TS2339: Property 'workerShuttingDown' does not exist…), so the tests cannot pass against main. With the change, the full test-mockactivityenv suite passes (8/8). eslint, prettier --check and lint:prune are all clean.

Cross-SDK parity

This brings TypeScript in line with the other SDKs, which all expose worker-shutdown notification distinctly from cancellation:

SDK API
Go activity.GetWorkerStopChannel
Python activity.wait_for_worker_shutdown
.NET ActivityExecutionContext.WorkerShutdownToken

The Promise / AbortSignal pairing follows the shape #1739 specified and matches how this SDK already surfaces cancellation, rather than importing another SDK's idiom.

Happy to adjust naming, the @experimental markers, or the choice to fire on any non-RUNNING state transition if maintainers would prefer it scoped only to explicit shutdown().

@rohitsudhakar1
rohitsudhakar1 requested a review from a team as a code owner July 27, 2026 21:50
@CLAassistant

CLAassistant commented Jul 27, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Other SDKs let an Activity know that its Worker has begun shutting down,
before expiration of the shutdown grace period: Go has GetWorkerStopChannel(),
Python has wait_for_worker_shutdown() and .NET has WorkerShutdownToken. The
TypeScript SDK had no equivalent, so a long running Activity only learned about
shutdown once the grace period expired and it got abruptly cancelled, leaving
no opportunity to checkpoint its progress and return.

Add `Context.workerShuttingDown` (a Promise<never>) and
`Context.workerShuttingDownSignal` (an AbortSignal), mirroring the existing
`cancelled` / `cancellationSignal` pair, along with matching top level exports
from `@temporalio/activity`.

The Worker owns a single AbortController, aborted from the `state` setter as
soon as the Worker leaves the RUNNING state, so that every shutdown path is
covered, including error driven ones. Each Activity derives its own controller
from that shared signal, so that user code may register listeners without
accumulating them on the Worker's signal.

A shutdown notification is deliberately not a cancellation: the Activity's
cancellation signal and cancellation details are left untouched, and the
Activity remains free to complete normally.

`MockActivityEnvironment.notifyWorkerShuttingDown()` exposes the notification
so that Activities can be tested against it.

Closes temporalio#1739
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.

[Feature Request] Add ActivityContext.workerShuttingDown

2 participants