[workflows] Dispose RPC results and introspection modifiers - #15708
Open
samstarling wants to merge 5 commits into
Open
samstarling wants to merge 5 commits into
samstarling wants to merge 5 commits into
Conversation
🦋 Changeset detectedLatest commit: 9c13670 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
samstarling
marked this pull request as ready for review
September 18, 2026 10:56
workers-devprod
requested review from
a team and
cjol
and removed request for
a team
September 18, 2026 10:57
Contributor
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
@cloudflare/autoconfig
@cloudflare/build-output-utils
@cloudflare/codemods
@cloudflare/config
@cloudflare/containers-shared
create-cloudflare
@cloudflare/deploy-helpers
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-functions
@cloudflare/pages-shared
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-plugin
@cloudflare/workers-auth
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
commit: |
The cleanup for a callback abandoned by a step timeout runs from an untracked promise. The engine is a Durable Object, which stays active while there is ongoing work or pending I/O, so the cleanup is retained without a waitUntil (a documented no-op for Durable Objects). Add a regression test that releases the abandoned callback from a later step, and record the lifetime reasoning at the call site. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
samstarling
force-pushed
the
sam/fix-workflow-rpc-disposal
branch
from
September 21, 2026 09:08
0a9c07a to
d68cb6f
Compare
cjol
approved these changes
Sep 22, 2026
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Local Workflow execution and introspection can leave RPC resources alive, producing undisposed-result warnings and requests cancelled after their execution context has ended. These changes affect local development and test tooling, not deployed production Workflows.
Dispose step callback results after copying their data, release the temporary modifier used during introspected instance creation, and release each introspector's modifier before aborting its instance. Disposal also handles an unused introspector, repeated calls and failed modifier acquisition. Acquisition errors still surface through
modify(), and genuine disposer errors still propagate after aborting the instance. Live results retain typed-array offsets and backing buffers. Compaction remains limited to storage.When a timeout wins the callback race, retain the callback promise and release its eventual result without delaying retries. Late objects are disposed, unused incoming streams are cancelled, and late rejection or cleanup failure does not replace the original timeout. Successful results remain owned by the existing persistence path.
The fixes live in
workflows-shared, which is bundled by Miniflare and the Vitest plugin. The five original lifecycle regressions fail on the unpatched baseline and pass with the fix. Additional cases cover failed acquisition, disposer errors, serialisation boundaries and timeout cleanup. The timeout regressions hold the first callback open until its retry starts, checking late objects, late streams, callback rejection and cancellation failure. The three new cleanup regressions fail without the timeout fix. All 203 shared Workflow tests pass, along with type, lint and formatting checks.Non-stream step results must satisfy the documented structured-cloneable contract. The existing Durable Object storage write already rejected ordinary function-valued properties. Cloning first also rejects functions attached to arrays instead of letting normalisation silently discard them. Tests cover both cases and confirm that prototype methods do not prevent class-instance data from being cloned. Streams continue through their separate persistence path. The timeout tests observe cancellation on the receiving stream: workerd does not reliably notify the producer across RPC. This change does not address the existing lower-level RPC stream-cancellation diagnostics.
Worked example
Imagine a Workflow containing a step such as this:
Without this change, a test like this could pass while still producing RPC lifecycle errors:
Despite the test disposing its introspector with
await using, the runtime could log this: