Ensure cancellation of a standard ReadableStream pump propagates to its underlying source - #6896
Ensure cancellation of a standard ReadableStream pump propagates to its underlying source#6896mhart wants to merge 1 commit into
ReadableStream pump propagates to its underlying source#6896Conversation
Merging this PR will degrade performance by 8.78%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | request[RegExpBenchmark] |
4.1 ms | 4.5 ms | -8.78% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing mhart:mhart/propagate-stream-cancel (752d9e6) with main (97a94ed)
Footnotes
-
129 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
|
This looks like the same bug as issue #6832, which has had a fix open in PR #6833 since June 20 — same root cause, and this PR handles a couple of things better than ours does (more below). One difference seems worth raising before it lands, though, plus some tests you are welcome to take.
For a JS-backed The race does not show up in a single-request repro: on an idle isolate the cancel task is queued before the drain and usually wins, and a synchronous To be fair about the baseline: the pre-regression What is reproducible either way: running the five regression tests from #6833 against this branch, four pass — the drop-mid-stream cancel, clean completion, the rejecting-cancel case, and the already-errored teardown — and the drain-lifetime one fails, with the drain completing while the cancel is still pending on a gate. The existing streams tests all pass. Each of the five is verified to fail without the corresponding piece of the fix, and they run under Two things this PR does better than ours: setting Minor, take or leave: the pre-regression |
Canceling
pumpTo()destroys the pump coroutine and releases its reader without invoking the source'scancel()callback. A pendingpull()could therefore remain blocked indefinitely, delaying response and request teardown.Reproduction
Run the worker, read only the first chunk, and close the client connection:
Before this change, the
stream-cancellog was absent and the pendingpull()could keep teardown active until the timer resolved.Changes
DrainingReaderthroughIoOwn.reader.cancel()through the activeIoContext.pull()promise.Testing
I only ran this on macOS:
bazel test --config=macos --nocache_test_results --test_output=all \ //src/workerd/api:streams/standard-test@