Skip to content

Commit

Permalink
stream: compact readable small element buffering
Browse files Browse the repository at this point in the history
When buffering lots of small elements the actual memory usage
can unexpectedly and significantly exceed the configured
highWaterMark. Try to compact the buffer when we estimate this
is about to happen.

Refs: nodejs#29310
  • Loading branch information
ronag committed Aug 25, 2019
1 parent b3172f8 commit 42ada26
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 0 deletions.
17 changes: 17 additions & 0 deletions lib/_stream_readable.js
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,15 @@ function addChunk(stream, state, chunk, addToFront) {
else
state.buffer.push(chunk);

if (!state.objectMode && state.length < state.highWaterMark) {
// Compact buffer if it contains lots of small elements.
// We estimate that each buffer element is 16 bytes
// (i.e. 2 properties * 8 bytes each).
const bufferOverhead = state.buffer.length * 16;
if (state.length + bufferOverhead > state.highWaterMark)
compact(state);
}

if (state.needReadable)
emitReadable(stream);
}
Expand Down Expand Up @@ -1131,6 +1140,14 @@ function fromList(n, state) {
return ret;
}

function compact({ buffer, decoder, objectMode, length }) {
if (!objectMode && buffer.length > 1) {
const v = decoder ? buffer.join('') : buffer.concat(length);
buffer.clear();
buffer.push(v);
}
}

function endReadable(stream) {
const state = stream._readableState;

Expand Down
31 changes: 31 additions & 0 deletions test/parallel/test-stream-readable-hwm-0-small.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use strict';

const assert = require('assert');
const { Readable } = require('stream');

{
const r = new Readable({
read: ()=> {},
highWaterMark: 32,
});

r.push('a');
r.push('b');
r.push('c');

// buffer should be compacted.
assert.strictEqual(r._readableState.buffer.length, 1);
}

{
const r = new Readable({
read: ()=> {},
highWaterMark: 50,
});

r.push('a');
r.push('b');

// buffer should not compacted.
assert.strictEqual(r._readableState.buffer.length, 2);
}

0 comments on commit 42ada26

Please sign in to comment.