Skip to content

Deliver local activity cancels before evicting a run - #1620

Open
DABH wants to merge 3 commits into
mainfrom
la-cancels-before-evict
Open

DABH wants to merge 3 commits into
mainfrom
la-cancels-before-evict

Conversation

@DABH

@DABH DABH commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

What was changed

A run's eviction activation is withheld until every queued cancel for its in-flight local activities has been returned from an activity poll. Cancels are queued once per attempt, an undispatched local activity resolves as cancelled, and an attempt failing after its cancel was requested is not retried locally, matching the server's RETRY_STATE_CANCEL_REQUESTED. CancelAllInRun no longer returns immediate resolutions, as it is only sunk for terminating or evicting runs

Why

_check_more_activations sinks CancelAllInRun, which only queues cancels, then produces the eviction activation. Once lang completes it, InvalidateRun drops the run's LAs from the outstanding set and next_pending discards the queued cancel as untracked, so lang keeps running the LA while Core reports shutdown complete. This was the hang I was originally looking at in temporalio/sdk-python#1837.

Testing

New la_cancel_delivered_before_eviction_activation fails without the fix; six more cover withheld-eviction results, undispatched and backing-off LAs, and no retry after cancel. If any look low-value just lmk.

When a run was evicted, Core queued a cancel for each of its in-flight
local activities and produced the eviction activation in the same pass.
If lang completed the eviction before its activity poll reached the
cancel, invalidating the run made the LA manager drop the cancel as
untracked, so lang kept running the local activity while Core reported
local activity shutdown complete (temporalio/sdk-python#1837).

The eviction activation is now withheld while any of the run's in-flight
local activities has a queued, not yet polled cancel, and the LA manager
wakes the run once the last one is handed to lang. Cancels are queued
once per attempt, a local activity still waiting for dispatch resolves as
cancelled instead of starting, and an attempt that fails after its cancel
was requested is not retried locally.

Local activity results that arrive while the eviction is withheld are
discarded, as they already were once the eviction activation was
outstanding, so the cancellation the eviction caused never reaches
workflow code as an activation. CancelAllInRun yields no immediate
resolutions for the same reason: it is only sunk for runs that are
terminating or evicting, and applying them surfaced spurious
cancellations for backing-off or never-dispatched local activities.
@DABH
DABH force-pushed the la-cancels-before-evict branch from 051b82d to b25d552 Compare October 1, 2026 08:23
@DABH
DABH requested a balanced review from Copilot October 1, 2026 14:41
@DABH
DABH marked this pull request as ready for review October 1, 2026 14:42
@DABH
DABH requested a review from a team as a code owner October 1, 2026 14:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The cancellation and eviction handoff spans concurrent activity and workflow streams and warrants final human validation.

Review effort: Balanced
Findings: None

What changed in this PR

Fixes a shutdown hang by ensuring local-activity cancellations are dispatched before workflow eviction removes their tracking state.

Changes:

  • Tracks cancellation delivery and blocks eviction until queued cancellations are polled.
  • Prevents retries and spurious resolutions after cancellation or eviction.
  • Adds regression coverage and user-facing changelog entries.
File Description
crates/​sdk-core/​src/​worker/​workflow/​workflow_stream.rs Handles cancellation-delivery notifications.
crates/​sdk-core/​src/​worker/​workflow/​mod.rs Prioritizes local-activity notifications.
crates/​sdk-core/​src/​worker/​workflow/​managed_run.rs Withholds eviction while cancellations remain queued.
crates/​sdk-core/​src/​worker/​mod.rs Connects the notification channel.
crates/​sdk-core/​src/​worker/​activities/​local_activities.rs Tracks cancellation state and suppresses retries.
crates/​sdk-core/​src/​core_tests/​workflow_tasks.rs Adds eviction and cancellation regressions.
crates/​sdk-core/​CHANGELOG.md Documents Core behavior changes.
CHANGELOG.md Documents Rust SDK behavior changes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +37 to +49
* Local activities still running when their workflow run is evicted (cache full, workflow
completion, worker shutdown, task failures) now have their cancellation delivered to the
activity poller before the run's eviction activation is issued, so a local activity can no
longer be left running without ever receiving its cancellation. Evictions no longer
deliver duplicate cancellation tasks for the same attempt, a local activity attempt that fails
after cancellation due to run eviction is no longer retried locally,
and a local activity cancelled before the activity poller picked it up now resolves as cancelled
immediately instead of starting. Workflow code is no longer activated with cancellations or
results of local activities that only came about because their run was being evicted or had
completed (previously a local activity backing off between retries at that point surfaced a
cancellation the workflow never requested). A worker with local activities enabled must keep
polling activity tasks until the poll reports shutdown: evictions of runs with in-flight local
activities wait for that poll.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of detail for a changelog entry—it is nearly twice as long as any existing entry. Do users need all of the duplicate-cancel, retry, undispatched-activity, and backing-off edge cases here, or can this be trimmed to the essential user-facing behavior and operational impact?

This branch has not been deployed

No deployments
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.

4 participants