Skip to content

stream: buffering and highWaterMark #29310

@ronag

Description

@ronag

The highWaterMark allows us to limit the buffering of a writable stream. This is great. If I set it to e.g. 16k I would expect the memory usage of the stream to be ~16kb. However this is not always the case, e.g:

const w = new Writable({
  highWaterMark: 16384,
  write (buf, encoding, cb) {
    process.nextTick(cb)
  }
})

while(w.write('a'))

assert.strictEqual(w.writableLength, 16384)

I would expect the memory usage of the stream to be ~16kB (+ some constant stream size). However, since I'm writing a small amount every time, the memory overhead of buffering takes over and I'm actually closer to ~1MB (I think the overhead of every buffered item is roughly 64 bytes). Maybe worse if a cb with unfortunate closure is passed.

If I setup a http server with a memory limit under the assumption of the highWaterMark and number of request with a reasonable extra. I can still end up significantly exceeding the memory limit depending on how other parts behave.

I'm not actually sure how to resolve this without breaking the public API, e..g we could change writableLength to something like:

state.length + state.bufferedRequestCount * BUFFER_OVERHEAD;

But if users assume that writableLength correlates to the number of written bytes (as my example above) we introduce breakage.

Or we could just update the state.length < state.highWaterMark check to take the overhead into account, but then we will maybe surprise the user by returning false in write() before writableLength introspection says it's necessary.

We have something possibly related with pendingcb as well. However, I need to think more about that.

Metadata

Metadata

Assignees

No one assigned

    Labels

    streamIssues and PRs related to the stream subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions