@@ -186,22 +186,22 @@ The API supports two models:
186186Pull streams have natural backpressure -- the consumer drives the pace, so
187187the source is never read faster than the consumer can process. Push streams
188188need explicit backpressure because the producer and consumer run
189- independently. The ` highWaterMark ` and ` backpressure ` options on ` push() ` ,
189+ independently. The ` budget ` and ` backpressure ` options on ` push() ` ,
190190` broadcast() ` , and ` share() ` control how this works.
191191
192192#### The two-buffer model
193193
194194Push streams use a two-part buffering system. Think of it like a bucket
195- (slots ) being filled through a hose (pending writes), with a float valve
195+ (buffer ) being filled through a hose (pending writes), with a float valve
196196that closes when the bucket is full:
197197
198198``` text
199- highWaterMark (e.g., 3 )
199+ budget (e.g., 16384 )
200200 |
201201 Producer v
202202 | +---------+
203203 v | |
204- [ write() ] ----+ +--->| slots |---> Consumer pulls
204+ [ write() ] ----+ +--->| buffer |---> Consumer pulls
205205 [ write() ] | | | (bucket)| for await (...)
206206 [ write() ] v | +---------+
207207 +--------+ ^
@@ -214,29 +214,29 @@ that closes when the bucket is full:
214214 'strict' mode limits this too!
215215```
216216
217- * ** Slots (the bucket)** -- data ready for the consumer, capped at
218- ` highWaterMark ` . When the consumer pulls, it drains all slots at once
219- into a single batch.
217+ * ** Buffer (the bucket)** -- data ready for the consumer, capped at
218+ ` budget ` bytes . When the consumer pulls, it drains all buffered data
219+ at once into a single batch.
220220
221- * ** Pending writes (the hose)** -- writes waiting for slot space. After
221+ * ** Pending writes (the hose)** -- writes waiting for buffer space. After
222222 the consumer drains, pending writes are promoted into the now-empty
223- slots and their promises settle.
223+ buffer and their promises settle.
224224
225225How each policy uses these buffers:
226226
227- | Policy | Slots limit | Pending writes limit |
228- | --------------- | --------------- | -------------------- |
229- | ` 'strict' ` | ` highWaterMark ` | ` highWaterMark ` |
230- | ` 'block ' ` | ` highWaterMark ` | Unbounded |
231- | ` 'drop-oldest' ` | ` highWaterMark ` | N/A (never waits) |
232- | ` 'drop-newest' ` | ` highWaterMark ` | N/A (never waits) |
227+ | Policy | Buffer limit | Pending writes limit |
228+ | --------------- | ------------ | -------------------- |
229+ | ` 'strict' ` | ` budget ` | ` budget ` |
230+ | ` 'unbounded ' ` | ` budget ` | Unbounded |
231+ | ` 'drop-oldest' ` | ` budget ` | N/A (never waits) |
232+ | ` 'drop-newest' ` | ` budget ` | N/A (never waits) |
233233
234234#### Strict (default)
235235
236236Strict mode catches "fire-and-forget" patterns where the producer calls
237237` write() ` without awaiting, which would cause unbounded memory growth.
238- It limits both the slots buffer and the pending writes queue to
239- ` highWaterMark ` .
238+ It limits both the buffer and the pending writes queue to
239+ ` budget ` bytes .
240240
241241If you properly await each write, you can only ever have one pending
242242write at a time (yours), so you never hit the pending writes limit.
@@ -246,7 +246,7 @@ overflows:
246246``` mjs
247247import { push , text } from ' node:stream/iter' ;
248248
249- const { writer , readable } = push ({ highWaterMark : 16 });
249+ const { writer , readable } = push ({ budget : 16384 });
250250
251251// Consumer must run concurrently -- without it, the first write
252252// that fills the buffer blocks the producer forever.
@@ -265,7 +265,7 @@ console.log(await consuming);
265265const { push , text } = require (' node:stream/iter' );
266266
267267async function run () {
268- const { writer , readable } = push ({ highWaterMark : 16 });
268+ const { writer , readable } = push ({ budget : 16384 });
269269
270270 // Consumer must run concurrently -- without it, the first write
271271 // that fills the buffer blocks the producer forever.
@@ -293,9 +293,9 @@ for (const item of dataset) {
293293// --> throws "Backpressure violation: too many pending writes"
294294```
295295
296- #### Block
296+ #### Unbounded
297297
298- Block mode caps slots at ` highWaterMark ` but places no limit on the
298+ Unbounded mode caps buffered bytes at ` budget ` but places no limit on the
299299pending writes queue. Awaited writes block until the consumer makes room,
300300just like strict mode. The difference is that unawaited writes silently
301301queue forever instead of throwing -- a potential memory leak if the
@@ -309,8 +309,8 @@ properly, or when migrating code from those APIs.
309309import { push , text } from ' node:stream/iter' ;
310310
311311const { writer , readable } = push ({
312- highWaterMark : 16 ,
313- backpressure: ' block ' ,
312+ budget : 16384 ,
313+ backpressure: ' unbounded ' ,
314314});
315315
316316const consuming = text (readable);
@@ -328,8 +328,8 @@ const { push, text } = require('node:stream/iter');
328328
329329async function run () {
330330 const { writer , readable } = push ({
331- highWaterMark : 16 ,
332- backpressure: ' block ' ,
331+ budget : 16384 ,
332+ backpressure: ' unbounded ' ,
333333 });
334334
335335 const consuming = text (readable);
@@ -355,19 +355,19 @@ any scenario where stale data is less valuable than current data.
355355``` mjs
356356import { push } from ' node:stream/iter' ;
357357
358- // Keep only the 5 most recent readings
358+ // Keep only the most recent ~16 KB of readings
359359const { writer , readable } = push ({
360- highWaterMark : 5 ,
360+ budget : 16384 ,
361361 backpressure: ' drop-oldest' ,
362362});
363363```
364364
365365``` cjs
366366const { push } = require (' node:stream/iter' );
367367
368- // Keep only the 5 most recent readings
368+ // Keep only the most recent ~16 KB of readings
369369const { writer , readable } = push ({
370- highWaterMark : 5 ,
370+ budget : 16384 ,
371371 backpressure: ' drop-oldest' ,
372372});
373373```
@@ -382,19 +382,19 @@ shedding load under pressure.
382382``` mjs
383383import { push } from ' node:stream/iter' ;
384384
385- // Accept up to 10 buffered items ; discard anything beyond that
385+ // Accept up to 16 KB of buffered data ; discard anything beyond that
386386const { writer , readable } = push ({
387- highWaterMark : 10 ,
387+ budget : 16384 ,
388388 backpressure: ' drop-newest' ,
389389});
390390```
391391
392392``` cjs
393393const { push } = require (' node:stream/iter' );
394394
395- // Accept up to 10 buffered items ; discard anything beyond that
395+ // Accept up to 16 KB of buffered data ; discard anything beyond that
396396const { writer , readable } = push ({
397- highWaterMark : 10 ,
397+ budget : 16384 ,
398398 backpressure: ' drop-newest' ,
399399});
400400```
@@ -416,14 +416,16 @@ if (writer.endSync() < 0) await writer.end();
416416writer .fail (err); // Always synchronous, no fallback needed
417417```
418418
419- #### ` writer.desiredSize `
419+ #### ` writer.canWrite `
420420
421- * {number |null}
421+ * {boolean |null}
422422
423- The number of buffer slots available before the high water mark is reached.
424- Returns ` null ` if the writer is closed or the consumer has disconnected.
423+ Returns ` true ` if the next write is likely to be accepted (buffered data is
424+ below capacity), ` false ` if backpressure is active, or ` null ` if the writer
425+ is closed or the consumer has disconnected.
425426
426- The value is always non-negative.
427+ This is a hint, not a guarantee: the state can change between the check and
428+ the write. Use [ ` ondrain() ` ] [ ] to wait for capacity rather than polling.
427429
428430#### ` writer.end([options]) `
429431
@@ -766,10 +768,10 @@ added:
766768* ` ...transforms ` {Function|Object} Optional transforms applied to the
767769 readable side.
768770* ` options ` {Object}
769- * ` highWaterMark ` {number} Maximum number of buffered slots before
770- backpressure is applied. Must be >= 1; values below 1 are clamped to 1 .
771- ** Default:** ` 4 ` .
772- * ` backpressure ` {string} Backpressure policy: ` 'strict' ` , ` 'block ' ` ,
771+ * ` budget ` {number} Maximum number of buffered bytes before
772+ backpressure is applied. Must be >= 16384 .
773+ ** Default:** ` 16384 ` .
774+ * ` backpressure ` {string} Backpressure policy: ` 'strict' ` , ` 'unbounded ' ` ,
773775 ` 'drop-oldest' ` , or ` 'drop-newest' ` . ** Default:** ` 'strict' ` .
774776 * ` signal ` {AbortSignal} Abort the stream.
775777* Returns: {Object}
@@ -829,18 +831,18 @@ added:
829831-->
830832
831833* ` options ` {Object}
832- * ` highWaterMark ` {number} Buffer size for both directions.
833- ** Default:** ` 4 ` .
834+ * ` budget ` {number} Buffer size in bytes for both directions.
835+ ** Default:** ` 16384 ` .
834836 * ` backpressure ` {string} Policy for both directions.
835837 ** Default:** ` 'strict' ` .
836838 * ` signal ` {AbortSignal} Cancellation signal for both channels.
837839 * ` a ` {Object} Options specific to the A-to-B direction. Overrides
838840 shared options.
839- * ` highWaterMark ` {number}
841+ * ` budget ` {number}
840842 * ` backpressure ` {string}
841843 * ` b ` {Object} Options specific to the B-to-A direction. Overrides
842844 shared options.
843- * ` highWaterMark ` {number}
845+ * ` budget ` {number}
844846 * ` backpressure ` {string}
845847* Returns: {Array} A pair ` [channelA, channelB] ` of duplex channels.
846848
@@ -1079,9 +1081,10 @@ fulfills with `true` when the writer can accept more data.
10791081``` mjs
10801082import { push , ondrain , text } from ' node:stream/iter' ;
10811083
1082- const { writer , readable } = push ({ highWaterMark: 2 });
1083- writer .writeSync (' a' );
1084- writer .writeSync (' b' );
1084+ const { writer , readable } = push ({ budget: 16384 });
1085+ const chunk = new Uint8Array (8192 ); // 8 KB
1086+ writer .writeSync (chunk);
1087+ writer .writeSync (chunk); // 16 KB total -- buffer full
10851088
10861089// Start consuming so the buffer can actually drain
10871090const consuming = text (readable);
@@ -1099,9 +1102,10 @@ await consuming;
10991102const { push , ondrain , text } = require (' node:stream/iter' );
11001103
11011104async function run () {
1102- const { writer , readable } = push ({ highWaterMark: 2 });
1103- writer .writeSync (' a' );
1104- writer .writeSync (' b' );
1105+ const { writer , readable } = push ({ budget: 16384 });
1106+ const chunk = new Uint8Array (8192 ); // 8 KB
1107+ writer .writeSync (chunk);
1108+ writer .writeSync (chunk); // 16 KB total -- buffer full
11051109
11061110 // Start consuming so the buffer can actually drain
11071111 const consuming = text (readable);
@@ -1214,9 +1218,9 @@ added:
12141218-->
12151219
12161220* ` options ` {Object}
1217- * ` highWaterMark ` {number} Buffer size in slots . Must be >= 1; values
1218- below 1 are clamped to 1. ** Default:** ` 16 ` .
1219- * ` backpressure ` {string} ` 'strict' ` , ` 'block ' ` , ` 'drop-oldest' ` , or
1221+ * ` budget ` {number} Buffer size in bytes . Must be >= 16384.
1222+ ** Default:** ` 65536 ` .
1223+ * ` backpressure ` {string} ` 'strict' ` , ` 'unbounded ' ` , ` 'drop-oldest' ` , or
12201224 ` 'drop-newest' ` . ** Default:** ` 'strict' ` .
12211225 * ` signal ` {AbortSignal}
12221226* Returns: {Object}
@@ -1275,12 +1279,6 @@ async function run() {
12751279run ().catch (console .error );
12761280```
12771281
1278- #### ` broadcast.bufferSize `
1279-
1280- * {number}
1281-
1282- The number of chunks currently buffered.
1283-
12841282#### ` broadcast.cancel([reason]) `
12851283
12861284* ` reason ` {Error}
@@ -1331,9 +1329,9 @@ added:
13311329
13321330* ` source ` {AsyncIterable} The source to share.
13331331* ` options ` {Object}
1334- * ` highWaterMark ` {number} Buffer size. Must be >= 1; values below 1
1335- are clamped to 1. ** Default:** ` 16 ` .
1336- * ` backpressure ` {string} ` 'strict' ` , ` 'block ' ` , ` 'drop-oldest' ` , or
1332+ * ` budget ` {number} Buffer size in bytes . Must be >= 16384.
1333+ ** Default:** ` 65536 ` .
1334+ * ` backpressure ` {string} ` 'strict' ` , ` 'unbounded ' ` , ` 'drop-oldest' ` , or
13371335 ` 'drop-newest' ` . ** Default:** ` 'strict' ` .
13381336* Returns: {Share}
13391337
@@ -1373,12 +1371,6 @@ async function run() {
13731371run ().catch (console .error );
13741372```
13751373
1376- #### ` share.bufferSize `
1377-
1378- * {number}
1379-
1380- The number of chunks currently buffered.
1381-
13821374#### ` share.cancel([reason]) `
13831375
13841376* ` reason ` {Error}
@@ -1426,8 +1418,8 @@ added:
14261418
14271419* ` source ` {Iterable} The sync source to share.
14281420* ` options ` {Object}
1429- * ` highWaterMark ` {number} Must be >= 1; values below 1 are clamped
1430- to 1. ** Default:** ` 16 ` .
1421+ * ` budget ` {number} Must be >= 16384.
1422+ ** Default:** ` 65536 ` .
14311423 * ` backpressure ` {string} ** Default:** ` 'strict' ` .
14321424* Returns: {SyncShare}
14331425
@@ -1528,7 +1520,7 @@ added: v26.1.0
15281520 * ` backpressure ` {string} Backpressure policy. ** Default:** ` 'strict' ` .
15291521 * ` 'strict' ` -- writes are rejected when the buffer is full. Catches
15301522 callers that ignore backpressure.
1531- * ` 'block ' ` -- writes wait for drain when the buffer is full. Recommended
1523+ * ` 'unbounded ' ` -- writes wait for drain when the buffer is full. Recommended
15321524 for use with [ ` pipeTo() ` ] [ ] .
15331525 * ` 'drop-newest' ` -- writes are silently discarded when the buffer is full.
15341526 * ` 'drop-oldest' ` -- ** not supported** . Throws ` ERR_INVALID_ARG_VALUE ` .
@@ -1561,7 +1553,7 @@ const writable = new Writable({
15611553});
15621554
15631555await pipeTo (from (' hello world' ),
1564- fromWritable (writable, { backpressure: ' block ' }));
1556+ fromWritable (writable, { backpressure: ' unbounded ' }));
15651557```
15661558
15671559``` cjs
@@ -1574,7 +1566,7 @@ async function run() {
15741566 });
15751567
15761568 await pipeTo (from (' hello world' ),
1577- fromWritable (writable, { backpressure: ' block ' }));
1569+ fromWritable (writable, { backpressure: ' unbounded ' }));
15781570}
15791571run ();
15801572```
@@ -2097,6 +2089,7 @@ console.log(textSync(stream)); // 'hello world'
20972089[` from ()` ]: #frominput
20982090[` fromSync ()` ]: #fromsyncinput
20992091[` node: zlib/ iter` ]: zlib.md#iterable-compression
2092+ [` ondrain ()` ]: #ondraindrainable
21002093[` pipeTo ()` ]: #pipetosource-transforms-writer-options
21012094[` pull ()` ]: #pullsource-transforms-options
21022095[` pullSync ()` ]: #pullsyncsource-transforms-options
0 commit comments