Commit 0d4a844d3b3 for nodejs
commit 0d4a844d3b30cad73d8d9a72e0019e8711decc3e
Author: James M Snell <jasnell@gmail.com>
Date: Sun Aug 30 17:26:48 2026 +0000
stream: make stream/iter toWritable apply backpressure
Signed-off-by: James M Snell <jasnell@gmail.com>
Assisted-by: Opencode
PR-URL: https://github.com/nodejs/node/pull/66079
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
diff --git a/doc/api/stream_iter.md b/doc/api/stream_iter.md
index 60d2aa39737..caf39715dca 100644
--- a/doc/api/stream_iter.md
+++ b/doc/api/stream_iter.md
@@ -1817,9 +1817,9 @@ non-Error reason is wrapped in an `ERR_FALSY_VALUE_REJECTION` or
`ERR_OPERATION_FAILED` error before it is passed to the callback. The error's
`reason` property contains the original value.
-The Writable's `highWaterMark` is set to `Number.MAX_SAFE_INTEGER` to
-effectively disable its internal buffering, allowing the underlying Writer
-to manage backpressure directly.
+The Writable uses the default classic stream `highWaterMark`. Classic stream
+backpressure bounds writes waiting to reach the underlying Writer, while the
+Writer controls completion of the active `_write()` or `_writev()` operation.
```mjs
import { push, toWritable } from 'node:stream/iter';
diff --git a/lib/internal/streams/iter/classic.js b/lib/internal/streams/iter/classic.js
index 4ca384cae9a..d1faf2cdb88 100644
--- a/lib/internal/streams/iter/classic.js
+++ b/lib/internal/streams/iter/classic.js
@@ -14,7 +14,6 @@
const {
ArrayPrototypePush,
FunctionPrototypeCall,
- NumberMAX_SAFE_INTEGER,
Promise,
PromisePrototypeThen,
PromiseReject,
@@ -937,11 +936,6 @@ function toWritable(writer) {
const writableOptions = {
__proto__: null,
- // Use MAX_SAFE_INTEGER to effectively disable the Writable's
- // internal buffering. The underlying stream/iter Writer has its
- // own backpressure handling; we want _write to be called
- // immediately so the Writer can manage flow control directly.
- highWaterMark: NumberMAX_SAFE_INTEGER,
write: _write,
final: _final,
destroy: _destroy,
diff --git a/test/parallel/test-stream-iter-writable-from.js b/test/parallel/test-stream-iter-writable-from.js
index 5cf7371caa5..5757f3126e7 100644
--- a/test/parallel/test-stream-iter-writable-from.js
+++ b/test/parallel/test-stream-iter-writable-from.js
@@ -6,6 +6,7 @@
const common = require('../common');
const assert = require('assert');
+const { once } = require('events');
const { setImmediate, setTimeout } = require('timers/promises');
const {
push,
@@ -603,18 +604,33 @@ async function testDestroyWithoutFail() {
}
// =============================================================================
-// Custom highWaterMark option
+// Classic Writable backpressure
// =============================================================================
-function testHighWaterMarkIsMaxSafeInt() {
+function testUsesBoundedHighWaterMark() {
const writer = {
write(chunk) { return Promise.resolve(); },
};
- // HWM is set to MAX_SAFE_INTEGER to disable Writable's internal
- // buffering. The underlying Writer manages backpressure directly.
const writable = toWritable(writer);
- assert.strictEqual(writable.writableHighWaterMark, Number.MAX_SAFE_INTEGER);
+ assert.ok(writable.writableHighWaterMark > 0);
+ assert.ok(writable.writableHighWaterMark < Number.MAX_SAFE_INTEGER);
+}
+
+async function testAppliesClassicBackpressure() {
+ let resolveWrite;
+ const writable = toWritable({
+ write: common.mustCall(() => new Promise((resolve) => {
+ resolveWrite = resolve;
+ })),
+ });
+ const chunk = Buffer.alloc(writable.writableHighWaterMark);
+
+ assert.strictEqual(writable.write(chunk), false);
+ const finished = once(writable, 'finish');
+ resolveWrite();
+ writable.end();
+ await finished;
}
// =============================================================================
@@ -700,10 +716,11 @@ async function testEndThrowsSyncPropagation() {
testInvalidWriterThrows();
testNoWritevWithoutWriterWritev();
-testHighWaterMarkIsMaxSafeInt();
+testUsesBoundedHighWaterMark();
Promise.all([
testBasicWrite(),
+ testAppliesClassicBackpressure(),
testFalsyWriterRejectionBecomesClassicError(),
testClassicWrapperReusePreservesErrorIdentity(),
testWriteDelegatesToWriter(),