Skip to content

Commit e98b952

Browse files
committed
fs: respect setDefaultHighWaterMark in ReadStream
ReadStream ignored stream.setDefaultHighWaterMark() and always used 64 KiB. Use the user-set value when there is one, and keep 64 KiB otherwise so Windows does not drop to the 16 KiB stream default. Fixes: #66937
1 parent 6d46467 commit e98b952

4 files changed

Lines changed: 50 additions & 4 deletions

File tree

‎doc/api/fs.md‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,8 @@ added: v16.11.0
265265
* `emitClose` {boolean} **Default:** `true`
266266
* `start` {integer}
267267
* `end` {integer} **Default:** `Infinity`
268-
* `highWaterMark` {integer} **Default:** `64 * 1024`
268+
* `highWaterMark` {integer} **Default:** `64 * 1024`, or the value set with
269+
[`stream.setDefaultHighWaterMark()`][]
269270
* `signal` {AbortSignal|undefined} **Default:** `undefined`
270271
* Returns: {fs.ReadStream}
271272
@@ -781,7 +782,8 @@ added: v18.11.0
781782
* `emitClose` {boolean} **Default:** `true`
782783
* `start` {integer}
783784
* `end` {integer} **Default:** `Infinity`
784-
* `highWaterMark` {integer} **Default:** `64 * 1024`
785+
* `highWaterMark` {integer} **Default:** `64 * 1024`, or the value set with
786+
[`stream.setDefaultHighWaterMark()`][]
785787
* Returns: {readline.InterfaceConstructor}
786788
787789
Convenience method to create a `readline` interface and stream over the file.
@@ -3104,7 +3106,8 @@ changes:
31043106
* `emitClose` {boolean} **Default:** `true`
31053107
* `start` {integer}
31063108
* `end` {integer} **Default:** `Infinity`
3107-
* `highWaterMark` {integer} **Default:** `64 * 1024`
3109+
* `highWaterMark` {integer} **Default:** `64 * 1024`, or the value set with
3110+
[`stream.setDefaultHighWaterMark()`][]
31083111
* `fs` {Object|null} **Default:** `null`
31093112
* `signal` {AbortSignal|null} **Default:** `null`
31103113
* `windowsHandle` {bigint} A raw Win32 `HANDLE` value to read from, in place
@@ -9787,6 +9790,7 @@ the file contents.
97879790
[`path.matchesGlob()`]: path.md#pathmatchesglobpath-pattern
97889791
[`statfs.bsize`]: #statfsbsize
97899792
[`stream.getDefaultHighWaterMark()`]: stream.md#streamgetdefaulthighwatermarkobjectmode
9793+
[`stream.setDefaultHighWaterMark()`]: stream.md#streamsetdefaulthighwatermarkobjectmode-value
97909794
[`stream/iter pipeTo()`]: stream_iter.md#pipetosource-transforms-writer-options
97919795
[`stream/iter pull()`]: stream_iter.md#pullsource-transforms-options
97929796
[`stream/iter pullSync()`]: stream_iter.md#pullsyncsource-transforms

‎lib/internal/fs/streams.js‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const {
3333
validateThisInternalField,
3434
} = require('internal/validators');
3535
const { errorOrDestroy } = require('internal/streams/destroy');
36+
const { getUserDefaultHighWaterMark } = require('internal/streams/state');
3637
const fs = require('fs');
3738
const { kRef, kUnref, FileHandle } = require('internal/fs/promises');
3839
const { Buffer } = require('buffer');
@@ -193,7 +194,7 @@ function ReadStream(path, options) {
193194
// A little bit bigger buffer and water marks by default
194195
options = copyObject(getOptions(options, kEmptyObject));
195196
if (options.highWaterMark === undefined)
196-
options.highWaterMark = 64 * 1024;
197+
options.highWaterMark = getUserDefaultHighWaterMark() ?? 64 * 1024;
197198

198199
if (options.autoDestroy === undefined) {
199200
options.autoDestroy = false;

‎lib/internal/streams/state.js‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const { ERR_INVALID_ARG_VALUE } = require('internal/errors').codes;
1111
// TODO (fix): For some reason Windows CI fails with bigger hwm.
1212
let defaultHighWaterMarkBytes = process.platform === 'win32' ? 16 * 1024 : 64 * 1024;
1313
let defaultHighWaterMarkObjectMode = 16;
14+
let defaultHighWaterMarkBytesSet = false;
1415

1516
function highWaterMarkFrom(options, isDuplex, duplexKey) {
1617
return options.highWaterMark != null ? options.highWaterMark :
@@ -27,9 +28,14 @@ function setDefaultHighWaterMark(objectMode, value) {
2728
defaultHighWaterMarkObjectMode = value;
2829
} else {
2930
defaultHighWaterMarkBytes = value;
31+
defaultHighWaterMarkBytesSet = true;
3032
}
3133
}
3234

35+
function getUserDefaultHighWaterMark() {
36+
return defaultHighWaterMarkBytesSet ? defaultHighWaterMarkBytes : undefined;
37+
}
38+
3339
function getHighWaterMark(state, options, duplexKey, isDuplex) {
3440
const hwm = highWaterMarkFrom(options, isDuplex, duplexKey);
3541
if (hwm != null) {
@@ -47,5 +53,6 @@ function getHighWaterMark(state, options, duplexKey, isDuplex) {
4753
module.exports = {
4854
getHighWaterMark,
4955
getDefaultHighWaterMark,
56+
getUserDefaultHighWaterMark,
5057
setDefaultHighWaterMark,
5158
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
5+
const assert = require('node:assert');
6+
const fs = require('node:fs');
7+
const { setDefaultHighWaterMark } = require('node:stream');
8+
9+
{
10+
const stream = fs.createReadStream(__filename);
11+
assert.strictEqual(stream.readableHighWaterMark, 64 * 1024);
12+
stream.destroy();
13+
}
14+
15+
setDefaultHighWaterMark(false, 1024 * 1024);
16+
17+
{
18+
const stream = fs.createReadStream(__filename);
19+
assert.strictEqual(stream.readableHighWaterMark, 1024 * 1024);
20+
stream.destroy();
21+
}
22+
23+
{
24+
const stream = fs.createReadStream(__filename, { highWaterMark: 1024 });
25+
assert.strictEqual(stream.readableHighWaterMark, 1024);
26+
stream.destroy();
27+
}
28+
29+
fs.promises.open(__filename).then(async (fh) => {
30+
const stream = fh.createReadStream({ autoClose: false });
31+
assert.strictEqual(stream.readableHighWaterMark, 1024 * 1024);
32+
stream.destroy();
33+
await fh.close();
34+
}).then(common.mustCall());

0 commit comments

Comments
 (0)