Skip to content

Commit 42ada26

Browse files
committed
stream: compact readable small element buffering
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
1 parent b3172f8 commit 42ada26

2 files changed

Lines changed: 48 additions & 0 deletions

File tree

lib/_stream_readable.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,15 @@ function addChunk(stream, state, chunk, addToFront) {
320320
else
321321
state.buffer.push(chunk);
322322

323+
if (!state.objectMode && state.length < state.highWaterMark) {
324+
// Compact buffer if it contains lots of small elements.
325+
// We estimate that each buffer element is 16 bytes
326+
// (i.e. 2 properties * 8 bytes each).
327+
const bufferOverhead = state.buffer.length * 16;
328+
if (state.length + bufferOverhead > state.highWaterMark)
329+
compact(state);
330+
}
331+
323332
if (state.needReadable)
324333
emitReadable(stream);
325334
}
@@ -1131,6 +1140,14 @@ function fromList(n, state) {
11311140
return ret;
11321141
}
11331142

1143+
function compact({ buffer, decoder, objectMode, length }) {
1144+
if (!objectMode && buffer.length > 1) {
1145+
const v = decoder ? buffer.join('') : buffer.concat(length);
1146+
buffer.clear();
1147+
buffer.push(v);
1148+
}
1149+
}
1150+
11341151
function endReadable(stream) {
11351152
const state = stream._readableState;
11361153

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
const assert = require('assert');
4+
const { Readable } = require('stream');
5+
6+
{
7+
const r = new Readable({
8+
read: ()=> {},
9+
highWaterMark: 32,
10+
});
11+
12+
r.push('a');
13+
r.push('b');
14+
r.push('c');
15+
16+
// buffer should be compacted.
17+
assert.strictEqual(r._readableState.buffer.length, 1);
18+
}
19+
20+
{
21+
const r = new Readable({
22+
read: ()=> {},
23+
highWaterMark: 50,
24+
});
25+
26+
r.push('a');
27+
r.push('b');
28+
29+
// buffer should not compacted.
30+
assert.strictEqual(r._readableState.buffer.length, 2);
31+
}

0 commit comments

Comments
 (0)