Repository: nodejs/node
Pull request: stream: add setter & getter for default highWaterMark

Adds stream.(get|set)DefaultHighWaterMark to read or update the default hwm.

<!--
Before submitting a pull request, please read
https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md.

Commit message formatting guidelines:
https://github.com/nodejs/node/blob/HEAD/doc/contributing/pull-requests.md#commit-message-guidelines

For code changes:
1. Include tests for any bug fixes or new features.
2. Update documentation if relevant.
3. Ensure that `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
-->


Changed files:
filename: doc/api/stream.md
patch: @@ -3348,6 +3348,29 @@ reader.read().then(({ value, done }) => {
 });
 ```
 
+### `stream.getDefaultHighWaterMark(objectMode)`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {boolean} objectMode
+* Returns: {integer}
+
+Returns the default highWaterMark used by streams.
+Defaults to `16384` (16 KiB), or `16` for `objectMode`.
+
+### `stream.setDefaultHighWaterMark(objectMode, value)`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {boolean} objectMode
+* {integer} highWaterMark value
+
+Sets the default highWaterMark used by streams.
+
 ## API for stream implementers
 
 <!--type=misc-->

filename: lib/_http_outgoing.js
patch: @@ -80,12 +80,11 @@ let debug = require('internal/util/debuglog').debuglog('http', (fn) => {
   debug = fn;
 });
 
-const HIGH_WATER_MARK = getDefaultHighWaterMark();
-
 const kCorked = Symbol('corked');
 const kUniqueHeaders = Symbol('kUniqueHeaders');
 const kBytesWritten = Symbol('kBytesWritten');
 const kErrored = Symbol('errored');
+const kHighWaterMark = Symbol('kHighWaterMark');
 
 const nop = () => {};
 
@@ -150,6 +149,7 @@ function OutgoingMessage() {
   this._onPendingData = nop;
 
   this[kErrored] = null;
+  this[kHighWaterMark] = getDefaultHighWaterMark();
 }
 ObjectSetPrototypeOf(OutgoingMessage.prototype, Stream.prototype);
 ObjectSetPrototypeOf(OutgoingMessage, Stream);
@@ -196,7 +196,7 @@ ObjectDefineProperty(OutgoingMessage.prototype, 'writableLength', {
 ObjectDefineProperty(OutgoingMessage.prototype, 'writableHighWaterMark', {
   __proto__: null,
   get() {
-    return this.socket ? this.socket.writableHighWaterMark : HIGH_WATER_MARK;
+    return this.socket ? this.socket.writableHighWaterMark : this[kHighWaterMark];
   },
 });
 
@@ -403,7 +403,7 @@ function _writeRaw(data, encoding, callback, size) {
   this.outputData.push({ data, encoding, callback });
   this.outputSize += data.length;
   this._onPendingData(data.length);
-  return this.outputSize < HIGH_WATER_MARK;
+  return this.outputSize < this[kHighWaterMark];
 }
 
 

filename: lib/internal/streams/state.js
patch: @@ -4,16 +4,29 @@ const {
   MathFloor,
   NumberIsInteger,
 } = primordials;
+const { validateInteger } = require('internal/validators');
 
 const { ERR_INVALID_ARG_VALUE } = require('internal/errors').codes;
 
+let defaultHighWaterMarkBytes = 16 * 1024;
+let defaultHighWaterMarkObjectMode = 16;
+
 function highWaterMarkFrom(options, isDuplex, duplexKey) {
   return options.highWaterMark != null ? options.highWaterMark :
     isDuplex ? options[duplexKey] : null;
 }
 
 function getDefaultHighWaterMark(objectMode) {
-  return objectMode ? 16 : 16 * 1024;
+  return objectMode ? defaultHighWaterMarkObjectMode : defaultHighWaterMarkBytes;
+}
+
+function setDefaultHighWaterMark(objectMode, value) {
+  validateInteger(value, 'value', 0);
+  if (objectMode) {
+    defaultHighWaterMarkObjectMode = value;
+  } else {
+    defaultHighWaterMarkBytes = value;
+  }
 }
 
 function getHighWaterMark(state, options, duplexKey, isDuplex) {
@@ -33,4 +46,5 @@ function getHighWaterMark(state, options, duplexKey, isDuplex) {
 module.exports = {
   getHighWaterMark,
   getDefaultHighWaterMark,
+  setDefaultHighWaterMark,
 };

filename: lib/stream.js
patch: @@ -42,6 +42,7 @@ const {
   },
 } = require('internal/errors');
 const compose = require('internal/streams/compose');
+const { setDefaultHighWaterMark, getDefaultHighWaterMark } = require('internal/streams/state');
 const { pipeline } = require('internal/streams/pipeline');
 const { destroyer } = require('internal/streams/destroy');
 const eos = require('internal/streams/end-of-stream');
@@ -105,6 +106,8 @@ Stream.addAbortSignal = addAbortSignal;
 Stream.finished = eos;
 Stream.destroy = destroyer;
 Stream.compose = compose;
+Stream.setDefaultHighWaterMark = setDefaultHighWaterMark;
+Stream.getDefaultHighWaterMark = getDefaultHighWaterMark;
 
 ObjectDefineProperty(Stream, 'promises', {
   __proto__: null,

filename: test/parallel/test-stream-set-default-hwm.js
patch: @@ -0,0 +1,36 @@
+'use strict';
+
+require('../common');
+
+const assert = require('node:assert');
+const {
+  setDefaultHighWaterMark,
+  getDefaultHighWaterMark,
+  Writable,
+  Readable,
+  Transform
+} = require('stream');
+
+assert.notStrictEqual(getDefaultHighWaterMark(false), 32 * 1000);
+setDefaultHighWaterMark(false, 32 * 1000);
+assert.strictEqual(getDefaultHighWaterMark(false), 32 * 1000);
+
+assert.notStrictEqual(getDefaultHighWaterMark(true), 32);
+setDefaultHighWaterMark(true, 32);
+assert.strictEqual(getDefaultHighWaterMark(true), 32);
+
+const w = new Writable({
+  write() {}
+});
+assert.strictEqual(w.writableHighWaterMark, 32 * 1000);
+
+const r = new Readable({
+  read() {}
+});
+assert.strictEqual(r.readableHighWaterMark, 32 * 1000);
+
+const t = new Transform({
+  transform() {}
+});
+assert.strictEqual(t.writableHighWaterMark, 32 * 1000);
+assert.strictEqual(t.readableHighWaterMark, 32 * 1000);