Description
Version
v20.0.0-pre
Platform
Linux Mars 5.19.0-35-generic #36~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Feb 17 15:17:25 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
Subsystem
stream
What steps will reproduce the bug?
To reproduce, run the following script with the debugger enabled, i.e. node inspect mytest.mjs
// mytest.mjs
import fs from 'node:fs'
import process from 'node:process'
import { Readable } from 'node:stream'
const reportMemoryUsage = (maxMb, randomWebStream) => {
const { arrayBuffers } = process.memoryUsage()
if (arrayBuffers > maxMb * 1024 * 1024) {
debugger
process.exit(1)
}
}
async function main() {
const randomNodeStream = fs.createReadStream('/dev/urandom', { highWaterMark: 5556 })
const randomWebStream = Readable.toWeb(randomNodeStream)
setInterval(() => reportMemoryUsage(16, randomWebStream), 1e3)
}
main()
- When the debugger stops at the breakpoint, open up the
repl
, and print out the contents ofrandomWebStream
, i.e.console.dir(randomWebStream, { depth: 32 })
. - Inspect the output of the queue in
randomWebStream[Symbol(kState)].controller[Symbol(kState)].queue
.
How often does it reproduce? Is there a required condition?
Always. However, the underlying stream that's being turned into the web stream must not be in the object mode.
What is the expected behavior? Why is that the expected behavior?
Because the underlying stream was not in the object mode and had its highWaterMark
set at 5556 bytes, the expected behavior is for the web stream's underlying queue to also have the total size circa 5556 bytes.
What do you see instead?
In my case I see the queue has the length of 5556 elements, with each element being an instance of Uint8Array of size 5556 bytes by itself - thus producing a grand total of 30869136 bytes, - as opposed to the 5556 bytes which the highWaterMark of the underlying stream randomNodeStream
had.
Additional information
This may be related to #46347.
The source of this behavior is likely this line:
node/lib/internal/webstreams/adapters.js
Lines 420 to 424 in fe514bf
This should probably use the ByteLengthQueuingStrategy
instead, as previously suggested by @debadree25 #46347 (comment)