fix(cloudflare): Enforce flush timeout across Workflow lifecycle - #24483
Open
matthewbjones wants to merge 3 commits into
Open
matthewbjones wants to merge 3 commits into
matthewbjones wants to merge 3 commits into
Conversation
Cloudflare's flush deadline could expire while transport fetches remained active, Workflow steps could inherit the previous step's flush point and schedule redundant eager drains, and flush-lock finalization could wait outside the configured timeout. Abort requests when a transport drain expires, reset the flush point at each Workflow step boundary, and share one deadline across flush-lock, pending-span, and transport phases. Add focused regression coverage for all three paths and update timer-based tests to await the user task before asserting teardown. Fixes getsentry#24482 Co-authored-by: OpenAI Codex <codex@openai.com>
matthewbjones
force-pushed
the
feat/cloudflare-enforce-flush-timeout
branch
from
September 17, 2026 22:04
7ad0606 to
643d751
Compare
matthewbjones
marked this pull request as ready for review
September 17, 2026 22:13
matthewbjones
requested review from
isaacs and
mydea
and removed request for
a team
September 17, 2026 22:13
Share one deadline across the flush lock, pending spans, client processing, and transport drain so pre-drain timeouts cannot strand buffered envelopes. Add regression coverage for lock and span starvation and for the complete flush deadline. Co-authored-by: OpenAI Codex <codex@openai.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit acd76c4. Configure here.
Use the Cloudflare test suite's established fake-timer cleanup and explicit timer advancement patterns for flush deadline assertions. Co-authored-by: OpenAI Codex <codex@openai.com>
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.

Cloudflare was reporting that Sentry-owned
waitUntil()tasks remained pending after instrumented Workflows had completed. The configuredflush(2000)timeout did not fully bound the work associated with a flush.Three separate lifecycle gaps contributed to the warning.
Transport requests survived a timed-out drain
IsolatedPromiseBuffer.drain(timeout)returnedfalsewhen its timer expired, but the request producers it had started continued running. A slow or stalledfetchtherefore remained attached to the invocation after Sentry reported that its flush had timed out.Each drain now owns an
AbortController. When that drain expires, it aborts only the requests started by that drain.makeCloudflareTransportcombines the drain signal with any caller-providedfetchOptions.signal, removes both listeners when the request settles, and keeps overlapping drains isolated so one timeout cannot abort another drain's requests.Workflow steps inherited the previous step's flush point
Cached Cloudflare clients reuse the Workflow run's isolation scope across step RPC invocations. The first boundary flush marked that scope's
flushPointReachedstate astrue, and later steps inherited it. Envelopes created by those later steps were consequently treated as post-invocation telemetry and registered additional eagerwaitUntil(transport.flush(2000))drains even though every step already performs its own boundary flush.The wrapped step callback now resets
flushPointReachedbefore it captures telemetry. This makes each Workflow step begin before its own flush point, while retaining eager delivery for telemetry that is genuinely created after that step's boundary flush.Flush-lock finalization was outside the timeout
CloudflareClient.flush(timeout)awaitedflushLock.finalize()before applyingtimeoutto pending spans and the transport. A userwaitUntiltask that never settled could therefore keep the Sentry flush pending indefinitely. The pending-span and transport phases could also each receive the original timeout instead of the time remaining from one overall deadline.flush()now computes one deadline and passes the remaining budget through flush-lock finalization, pending-span completion, and the transport drain. It returnsfalsewithout beginning a later phase when the budget has already been exhausted.Validation
Each failure has focused red/green regression coverage:
flush(timeout)to returnfalsewithin the deadline.The production reproduction uses 100 trivial
step.do()calls and an HTTP transport that delays every response for 120 seconds. The transport fix alone removed the warnings withcacheClient: false, but warnings remained on the current cached-client path. Disabling eager envelope delivery isolated the remaining behavior to the inherited Workflow flush point. With eager delivery enabled and both lifecycle fixes applied, 20/20 deployed Workflow instances completed with zero matchingwaitUntil()warnings in Cloudflare Observability.The complete
@sentry/cloudflaresuite passes (955/955), along with package lint, build, and typecheck.yarn lint) & (yarn test).Closes #24482