-
Notifications
You must be signed in to change notification settings - Fork 5k
fetch/S3: replace ResumableSink with proper JSSinks #36087
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Jarred-Sumner
merged 164 commits into
main
from
farm/40eaa140/fetch-node-readable-body-spin
Jul 31, 2026
Merged
Changes from all commits
Commits
Show all changes
164 commits
Select commit
Hold shift + click to select a range
f1fa832
fetch: yield the async-iterable pump per write when feeding an ArrayB…
robobun 61faf95
[autofix.ci] apply automated fixes
autofix-ci[bot] 4c9c022
fetch: hand a DirectPending body's pull() straight to the ResumableSink
robobun e63fb41
test: inherit stderr instead of piping unused
robobun 67a25c5
ResumableSink: match JSDirectStreamController's end/close/error contract
robobun ae14d0e
ResumableSink: controller.error() with no argument aborts
robobun b201c14
ci: retrigger
robobun b95068b
Merge branch 'main' into farm/40eaa140/fetch-node-readable-body-spin
Jarred-Sumner 5563870
fetch: drive async-iterable request bodies into the ResumableSink nat…
robobun 6157425
async-iterable pump: recover the op from the bound pull instead of m_…
robobun 653d7c2
Merge remote-tracking branch 'origin/main' into farm/40eaa140/fetch-n…
robobun a0fa8a1
revert src/ to main for ResumableSink→JSSink consolidation
robobun 0622880
Register FetchRequestBodySink JSSink in codegen + Sink.h + StartTag
robobun 2f5078d
NetworkSink: report Writable::Backpressure + signal.ready on drain
robobun 3bf80d4
fetch: route sink cancel/detach through cancel_request_body_sink
robobun a64ede4
Delete C++ ResumableSink pump (JSResumableSinkPumpOperation et al)
robobun 9d6031b
multipart: replace ResumableSinkBackpressure with local UploadBackpre…
robobun c88d7b8
webcore/fetch: add FetchRequestBodySink JSSink type
robobun 2cc910c
webcore: unregister resumable_sink module + re-exports
robobun ce04e51
FetchTasklet: swap sink to FetchRequestBodySink; drain/write off JSSink
robobun 85d2f16
Register fetch/S3 request-stream .then() handlers in thenable dispatch
robobun 485e4e0
s3: upload_stream uses NetworkSink + assign_to_stream
robobun 8addbf6
FetchRequestBodySink: delegate write to write_request_data
robobun f8fa2be
Declare FetchRequestBodySink ABI in headers.h; drop ResumableSink stubs
robobun 05e05e7
fetch request-body sink: single write_end_request via pump-promise path
robobun 11d2c74
fetch: drop signal.clear in ignore_remaining; resolve flush on cancel
robobun ee22dd5
s3 upload_stream: use AnyPromise safe accessors + set_handled
robobun 35f6c15
FetchRequestBodySink: gate backpressure on JS-side pending_bytes
robobun 452b00f
FetchRequestBodySink: backpressure on every write, not at HWM
robobun b8acab7
FetchRequestBodySink: convert latin1/utf16 directly into the stream b…
robobun f2c8477
s3 upload_stream: capture end_promise before assign_to_stream; settle…
robobun e6d4f33
drop dead code: cancel_from_native, FetchRequestBodySink::{new,global…
robobun 728441c
address review: utf16 length with replacement; s3 continue_stream bef…
robobun 57fade4
s3: drop carried-over TODO comments in moved lines
robobun 1b51f1a
s3: keep js_err alive across the Failure branch's JSC calls
robobun f7de8ae
s3: drop task.ended guard in handle_reject_stream
robobun e64e795
s3: trim handle_reject_stream comment
robobun fec25c2
s3: ref the MultiPartUpload across the single-file PUT
robobun 3c9c618
address review nits: drop test timeout; gate NetworkSink end_promise …
robobun 863eb83
FileSink: signal→source JsCell<SourceHandle>; to_result backpressure …
robobun d768829
streams: add SourceHandle<T: JsSinkAbi> tagged source enum
robobun 0e14582
readStreamIntoSink: backpressure via m_onPull instead of flush(true)
robobun 360d455
NetworkSink: signal→source SourceHandle, drop flush_promise; s3 callers
robobun 90b13a3
Add SinkHandle enum; swap ByteStream pipe→sink with backpressure
robobun 1ffd3b9
HTTPServerWritable: swap signal:Signal -> source:SourceHandle<Self>
robobun 335efb7
JSSink::assign_to_stream: wrap bits into SourceHandle; drop jsvalue_ptr
robobun de7eb66
test: add native pipe backpressure/duplex/s3 tests; un-todo spawn cancel
robobun 4bbf301
RequestContext: write_chunk/end_chunk + SinkHandle::http_response hookup
robobun 6b46a40
FetchRequestBodySink: signal→source SourceHandle, drop flush_promise
robobun fb320ea
Add is_backed_up() to Posix/WindowsStreamingWriter
robobun b938b1b
ArrayBufferSink: swap signal field to SourceHandle<Self>
robobun c36aa30
Subprocess/ShellWritable use SourceHandle; drop SignalHandler impls
robobun b22ebd3
ValueBufferer: write_chunk->Writable + SinkHandle hookup; drop PipeHa…
robobun 85ac139
fetch/s3: native ByteStream→sink bypass of assign_to_stream
robobun ab8ebff
Delete Signal/SignalVTable/SignalHandler/SinkSignal/Pipe/PipeHandler/…
robobun 80b8e2c
NetworkSink: restore flush_promise for .writer() path
robobun 16cd2e3
ByteStream: forward upstream Err to sink.end when back-pressured
robobun 55e0c08
rsis: settle m_result when sink closes during backpressure wait
robobun 76e6bce
FileSink: gate Backpressure to pump sources; resume on POSIX drain
robobun 1e3ba61
is_backed_up(): track EAGAIN, not buffered bytes (match #33399)
robobun f559d76
Body.rs: fix ValueBufferer SinkHandle SAFETY comment attribution
robobun 6336d00
subprocess: migrate FileSink signal→SourceHandle::Subprocess checks
robobun 7294221
RequestContext: raw-ptr dispatch in on_writable_byte_stream; drop dea…
robobun 3c5ad02
JSSink::assign_to_stream: pre-seed JSController to catch sync detach
robobun e22db5f
test: fix s3 stream arg, fetch RSS bound, serve backpressure observable
robobun 7b6042d
JSSink::detach: unpipe ByteStream; FetchTasklet: bytes.sink.is_some()
robobun 925745b
FetchRequestBodySink: restore flush_promise for async-iterable pump
robobun c5cb0f2
SourceHandle: treat JSController(0) placeholder as no-op in close/ready
robobun 7cd300b
fetch: drive write_end_request on native ByteStream sink completion
robobun f375b8d
test: spawn stdin + Bun.write fed by native ByteStream (req/resp.body)
robobun 99372d0
NetworkSink: keep flush_promise for writer().flush(); settle on S3 done
robobun ecde998
FetchRequestBodySink: delete dead flush_promise/pending_bytes fields
robobun 5efc802
FileSink::assign_to_stream: add native ByteStream fast-path
robobun 8479003
FetchRequestBodySink: restore flush_promise for async-iterable pump
robobun 50be62c
test: correct Bun.write(Bun.file,Response) comment re: FileSink path
robobun 4383813
FileSink: assert ByteStream sink unset before fast-path hookup
robobun 9bc59a7
Blob: pre-check Source::Bytes before FileSink assign_to_stream
robobun 534baaf
NetworkSink: drop source!=None flush short-circuit; keep flush_promise
robobun ca6df91
Blob: revert Source::Bytes pre-check; native path resolved 0 early
robobun d5a4b5a
trim comment-cop flagged comments
robobun 213dd64
trim comment-cop flagged comments (all 40)
robobun 41eac3e
SinkHandle::end(ValueBufferer): route Err through write thunk; drop d…
robobun 940ee1f
SinkHandle::end: propagate full StreamError to sinks
robobun 3a21562
FileReader: native SinkHandle fast-path for fetch request body
robobun efb4406
test: spawn+fetch native SourceHandle/SinkHandle end-to-end coverage
robobun 07a9582
HTTPServerWritable: fire source.ready() on drain so readStreamIntoSin…
robobun e915bca
readDirectStream: leave m_onPull empty so onReady cannot re-enter pull
robobun 21badb2
Detach native source on end_from_stream; keep S3 wrapper ref across f…
robobun b60016a
FileSink: keep JSController on Pending path; resume ByteStream from o…
robobun f8d1aa2
FileSink: snapshot SourceHandle before ready()/close() to avoid JsCel…
robobun 90302f8
S3UploadStreamWrapper: release pump ref when S3 fails on native ByteS…
robobun 83015f4
FileSink::on_write: call writer.end() on done+Drained outside pending…
robobun 4d62746
trim comment-cop flagged on_write comment
robobun ff21b71
multipart: re-check queue after on_writable; ByteStream: surface pre-…
robobun 235563e
trim comment-cop flagged comments
robobun 785e879
test: skip spawn stderr backpressure test on Windows
robobun 38481f7
Address review nits: NetworkSink write contract, ByteStream sink guar…
robobun 2b63270
Trim review-bot-flagged comments
robobun aa59ec1
test: bound parked poll loop with 30s deadline
robobun fd8b824
FileSink: fall through to assign_to_stream when ByteStream already ha…
robobun f78b9b2
streams: pause upstream socket when native sink backpressures; write(…
robobun 93cd4d0
streams: write() returns Promise on backpressure for HTTPServerWritab…
robobun 8230dbf
Remove stray test registry artifacts
robobun 3494ea7
FetchRequestBodySink: park JS pump on every scheduled write
robobun 5dacd83
NewSource: replace 5 fn-ptr fields with producer:SourceHandle
robobun 9ea865d
SinkHandle: collapse Http*/H3Response into ServerResponse(AnyRequestC…
robobun 62dff10
FetchTasklet: re-sig stream callbacks; use SourceHandle producer
robobun 149e5e9
RequestContext: re-sig write_chunk/end_chunk/on_request_body_stream_d…
robobun 7fabfdd
streams: degenericize SourceHandle, add SinkKind + 3 producer variants
robobun 1df1d05
AnyRequestContext: add write_chunk/end_chunk/on_request_body_stream_d…
robobun 389fc3d
Add SINK_KIND to JsSinkAbi; degenericize assign_to_stream/detach
robobun 3eb13d8
s3: hoist S3DownloadStreamWrapper, wire to SourceHandle::S3DownloadBody
robobun 8cab178
streams: add UpstreamSource trait + impls; collapse SourceHandle arms
robobun f003ff4
ByteStream: route drain/cancel/attach signals through producer
robobun ec520b4
PendingValue: replace 3 stream fn-ptrs with producer:SourceHandle
robobun c292f99
webcore: add DownstreamSink trait; SinkHandle dispatches via it
robobun a4353f5
De-parameterize SourceHandle<X> fields + source() impls
robobun 544e087
server: replace on_stream_drained fn-ptr with producer:ServerRequestBody
robobun 503c27d
SourceHandle: type Subprocess/ShellWritable ptrs at call-sites
robobun af4ae58
Dead-code sweep: drop detach_abi dup, unused JsSinkAbi externs, inlin…
robobun 55602a3
FetchRequestBodySink: degenericize source; allow dead_code in sink ex…
robobun 69dd9f7
RequestContext: rename clear_*_drain_handler -> detach_request_body_p…
robobun 2969578
streams: restore SAFETY comments + allow ptr-deref on UpstreamSource …
robobun bf5fde6
RequestContext: allow not_unsafe_ptr_arg_deref on write_chunk/end_chunk
robobun 860eaf9
Sink: split decl_js_sink_externs per-subset, drop dead_code allow
robobun 00203c2
s3: clear sink.source before clean-EOF end; settle sink.pending on re…
robobun 776ed0f
Merge remote-tracking branch 'origin/main' into farm/40eaa140/fetch-n…
robobun 6a10dca
s3: drop pre-existing TODO comment surfaced by de-indent
robobun 3fd09b0
[autofix.ci] apply automated fixes
autofix-ci[bot] 1d7b29c
UpstreamSource/DownstreamSink: take &mut self, centralize unsafe dere…
robobun c28f981
SourceHandle/SinkHandle: BackRef<T> variants; Box::leak in fast-paths…
robobun 3dc2bec
Drop SinkKind: unify controller onReady/onClose via JSReadableSinkCon…
robobun 45d1b3f
RequestContext: restore raw-ptr dispatch for re-entrant write_chunk/e…
robobun 687c7a8
webcore: drop dead DownstreamSink trait
robobun bffe087
FileSink: return Backpressure for FileReader source on pending write
robobun b8d9780
[autofix.ci] apply automated fixes
autofix-ci[bot] a8ca0cc
test(spawn): await cancel() via Promise.withResolvers instead of polling
robobun f781147
test: drop native-path tests that also pass via JS pump on main
robobun 0f106b7
test(spawn): drop fetch-body-as-stdin bounded-memory test
robobun 1ea7dc5
headers.h: drop dead per-sink __detachPtr/onClose/onReady CPP_DECLs
robobun ee20d5f
headers.h: drop remaining H3ResponseSink dead decls
robobun 8210cae
test(spawn): fetch response.body → child stdin bounded-memory regress…
Jarred-Sumner f1c0f54
test(spawn): run FileSink leak iterations concurrently (debug+ASAN ti…
robobun c0a5be4
Merge remote-tracking branch 'origin/main' into farm/40eaa140/fetch-n…
Jarred-Sumner 024bc9f
test: cover JS-pull spawn stdin, locked-after-attach, serve proxy bac…
Jarred-Sumner 11418cd
test(spawn): await child.exited + close source in bounds-memory fixtu…
robobun b9025ef
Backpressure regression tests; normalize resourceUsage().maxRSS units
Jarred-Sumner 37f6cd6
Merge branch 'farm/40eaa140/fetch-node-readable-body-spin' of https:/…
Jarred-Sumner aec4a56
boringssl: remove duplicate is_safe_alt_name from merge
robobun d6d45d1
test(serve): measure stall-phase RSS, not whole-process maxRSS, in pr…
robobun c50455e
test(s3): drop vacuous RSS bound from ByteStream -> NetworkSink uploa…
robobun d9d0046
test(spawn): parallelize stdin FileSink leak iterations and drop expl…
robobun 440f8c3
Remove orphaned setHandlers builtin name
robobun 688a574
test: move runFixtureMaxRSS/emptyProcessMaxRSS helpers into harness.ts
robobun b07816c
test(fetch): wait for body bytes at the target before asserting the s…
Jarred-Sumner 3cd26a7
Merge branch 'farm/40eaa140/fetch-node-readable-body-spin' of https:/…
Jarred-Sumner b58eb63
Revert "test(serve): measure stall-phase RSS, not whole-process maxRS…
Jarred-Sumner 45d0d3b
Merge branch 'farm/40eaa140/fetch-node-readable-body-spin' of https:/…
Jarred-Sumner 2ebb0fc
test: halve the bounded-memory fixtures' payload to 128 MB
Jarred-Sumner f3d5e30
Body: do not report a Locked body's content-length as allocated memory
robobun 2ebc01e
[autofix.ci] apply automated fixes
autofix-ci[bot] 7cc45af
Trim long comments flagged by comment-cop
robobun d6f092d
bench: add fetch streaming/backpressure harness
Jarred-Sumner 6b34885
Merge branch 'farm/40eaa140/fetch-node-readable-body-spin' of https:/…
Jarred-Sumner File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| // Streaming/backpressure benchmark for fetch bodies. Runs under bun or node. | ||
| // | ||
| // [MB=64] [STALL=0] [CONCURRENCY=1] bun bench/fetch/streaming-backpressure.mjs <scenario> | ||
| // | ||
| // scenarios: fetch-to-fetch | readable-body | download-proxy | ||
| // prints one JSON line: elapsedMs, peakRssMB (sampled), cpuMs, per-run totals. | ||
| // All servers are in-process net/http servers on loopback, so the numbers are | ||
| // the runtime's own overhead moving MB-per-stream x CONCURRENCY streams. | ||
| // STALL>0 pauses the consumer that long before draining (rate-mismatch case). | ||
| import http from "node:http"; | ||
| import net from "node:net"; | ||
| import { spawn } from "node:child_process"; | ||
|
Check warning on line 12 in bench/fetch/streaming-backpressure.mjs
|
||
| import { Readable } from "node:stream"; | ||
| import { pipeline } from "node:stream/promises"; | ||
|
|
||
| const scenario = process.argv[2]; | ||
| const MB = Number(process.env.MB ?? 64), STALL = Number(process.env.STALL ?? 0), C = Number(process.env.CONCURRENCY ?? 1); | ||
| const CHUNK = Buffer.alloc(64 * 1024, 0x47), COUNT = MB * 16, TOTAL = CHUNK.length * COUNT; | ||
| const rt = typeof Bun !== "undefined" ? "bun " + Bun.version_with_sha : "node " + process.version; | ||
|
|
||
| let peak = 0; | ||
| const rssNow = () => (process.memoryUsage.rss ? process.memoryUsage.rss() : process.memoryUsage().rss); | ||
| const sampler = setInterval(() => { const r = rssNow(); if (r > peak) peak = r; }, 10); | ||
| const t0 = Date.now(), cpu0 = process.cpuUsage(); | ||
| setTimeout(() => { console.log(JSON.stringify({ scenario, MB, STALL, C, runtime: rt, TIMEOUT: true, peakRssMB: Math.round(peak / 1048576) })); process.exit(1); }, 300000).unref(); | ||
|
|
||
| async function makeSource() { | ||
| const srv = net.createServer(sock => { | ||
| sock.write("HTTP/1.1 200 OK\r\ncontent-length: " + TOTAL + "\r\nconnection: close\r\n\r\n"); | ||
| let n = 0; | ||
| const pump = () => { while (n < COUNT) { n++; if (!sock.write(CHUNK)) return sock.once("drain", pump); } sock.end(); }; | ||
| pump(); | ||
| sock.on("error", () => {}); | ||
| }); | ||
| await new Promise(r => srv.listen(0, "127.0.0.1", r)); | ||
| return { url: `http://127.0.0.1:${srv.address().port}/`, close: () => srv.close() }; | ||
| } | ||
| async function makeSlowSink() { | ||
| let done; const drained = new Promise(r => (done = r)); | ||
| const srv = net.createServer(sock => { | ||
| let got = 0; if (STALL) { sock.pause(); setTimeout(() => sock.resume(), STALL); } | ||
| sock.on("data", d => { got += d.length; if (got >= TOTAL) done(got); }); | ||
| sock.on("error", () => {}); | ||
| }); | ||
| await new Promise(r => srv.listen(0, "127.0.0.1", r)); | ||
| return { url: `http://127.0.0.1:${srv.address().port}/`, drained, close: () => srv.close() }; | ||
| } | ||
|
|
||
| async function fetchToFetch() { | ||
| const source = await makeSource(), sink = await makeSlowSink(); | ||
| const up = await fetch(source.url); | ||
| fetch(sink.url, { method: "POST", body: up.body, duplex: "half" }).catch(() => {}); | ||
| await sink.drained; source.close(); sink.close(); | ||
| } | ||
| async function readableBody() { | ||
| const sink = await makeSlowSink(); | ||
| const body = Readable.from((function* () { for (let i = 0; i < COUNT; i++) yield CHUNK; })()); | ||
| fetch(sink.url, { method: "POST", body, duplex: "half" }).catch(() => {}); | ||
| await sink.drained; sink.close(); | ||
| } | ||
| async function downloadProxy() { | ||
| const source = await makeSource(); | ||
| const server = http.createServer(async (req, res) => { | ||
| const up = await fetch(source.url); | ||
| res.writeHead(200); | ||
| await pipeline(Readable.fromWeb(up.body), res).catch(() => {}); | ||
| }); | ||
| await new Promise(r => server.listen(0, "127.0.0.1", r)); | ||
| await new Promise(resolve => { | ||
| let got = 0; | ||
| const c = net.connect(server.address().port, "127.0.0.1", () => { if (STALL) { c.pause(); setTimeout(() => c.resume(), STALL); } c.write("GET / HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n"); }); | ||
| c.on("data", d => { got += d.length; if (got >= TOTAL) { c.destroy(); resolve(); } }); | ||
| c.on("error", () => {}); | ||
| }); | ||
| server.close(); source.close(); | ||
| } | ||
| const table = { "fetch-to-fetch": fetchToFetch, "readable-body": readableBody, "download-proxy": downloadProxy }; | ||
| const fn = table[scenario]; | ||
| if (!fn) { console.error("scenarios:", Object.keys(table).join(" ")); process.exit(2); } | ||
| await Promise.all(Array.from({ length: C }, (_, i) => fn(i))); | ||
| clearInterval(sampler); | ||
| const cpu = process.cpuUsage(cpu0); | ||
| console.log(JSON.stringify({ scenario, MB, STALL, C, totalGB: +(TOTAL * C / 2**30).toFixed(2), runtime: rt, elapsedMs: Date.now() - t0, peakRssMB: Math.round(peak / 1048576), cpuMs: Math.round((cpu.user + cpu.system) / 1000) })); | ||
| process.exit(0); | ||
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡
spawnis imported fromnode:child_processat line 12 but never used — the three scenarios (fetch-to-fetch,readable-body,download-proxy) use onlynet,http,Readable, andpipeline. The PR description's benchmark table lists aspawn-pipescenario that isn't in the file's scenario table, so this is a leftover from an earlier iteration. Per REVIEW.md "Delete dead code in the same PR that makes it dead" — remove the import.Extended reasoning...
What the finding is
bench/fetch/streaming-backpressure.mjsis a new file added by this PR. Line 12 reads:but
spawnis never referenced anywhere in the 84-line file body. The file's scenario table at line 78 defines exactly three scenarios —fetch-to-fetch,readable-body, anddownload-proxy— implemented byfetchToFetch(),readableBody(), anddownloadProxy()respectively. Those functions use onlynet.createServer/net.connect,http.createServer,Readable.from/Readable.fromWeb,pipeline, andfetch. There is no call tospawn(anywhere in the file.Why it's here
The PR description's Benchmark section lists six scenarios including
spawn-pipe (fetch.body -> spawn stdin),file-upload, andjs-stream-upload, but the checked-in bench file's scenario table contains only three of them. The commit message on the tip of the branch is "bench: add fetch streaming/backpressure harness", so this file was pared down between when the PR-description numbers were captured and when the harness was committed. Thespawnimport survived the trim; the scenario that used it did not.Why existing checks don't catch it
Nothing lints
bench/files for unused imports — they are neither shipped runtime code (not undersrc/) nor tests (not undertest/). ESM does not warn on an unused named import binding, and the file runs identically with or without the line (node:child_processis a built-in module with no side-effecting top-level code).Step-by-step proof
bench/fetch/streaming-backpressure.mjsas a new file (new file mode 100644), so its full contents are visible in the diff hunk.import { spawn } from "node:child_process";.spawn: the only occurrence is the import statement itself. The three scenario functions (lines 49-76) contain no reference tospawn, and the scenario dispatch table (line 78:const table = { "fetch-to-fetch": fetchToFetch, "readable-body": readableBody, "download-proxy": downloadProxy };) has nospawn-pipeentry.spawn-pipe (fetch.body -> spawn stdin), confirming a spawn-based scenario existed in an earlier iteration and was removed from the file but not from the imports.Impact
None at runtime. This is a bench harness, not shipped code and not part of the test suite; an unused ESM binding to a built-in module has no observable effect on the benchmark's output, timing, or memory numbers. The only cost is noise: a reader looking for the
spawn-pipescenario referenced in the PR description will find the import, grep for its use, and find nothing.How to fix
Delete line 12:
import http from "node:http"; import net from "node:net"; -import { spawn } from "node:child_process"; import { Readable } from "node:stream"; import { pipeline } from "node:stream/promises";(Optionally, either restore the
spawn-pipescenario to the file or drop it from the PR-description benchmark table so the two agree — but that's a separate PR-description-mismatch nit.)