-
Notifications
You must be signed in to change notification settings - Fork 30.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FS promises: handle long files reading #19643
Conversation
lib/fs/promises.js
Outdated
totalRead += bytesRead; | ||
if (bytesRead > 0) | ||
chunks.push(buffer.slice(0, bytesRead)); | ||
} while (totalRead < size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
size
cannot be used as a reliable indicator of when the loop should exit since the file size might change while readFileHandle()
is running. Maybe what we could do is using bytesRead === chunkSize
as the exit condition.
Thanks for fixing this! In hindsight, the logic errors seem fairly obvious. One comment. |
49bf54c
to
9a9ea11
Compare
lib/fs/promises.js
Outdated
if (totalRead > 0) | ||
chunks.push(buffer.slice(0, totalRead)); | ||
} while (totalRead === chunkSize); | ||
const chunks = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This indentation looks off. There's no way this passed make test
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, I forgot to re-run the tests after TimothyGu change request. My bad!
9a9ea11
to
a450a5e
Compare
lib/fs/promises.js
Outdated
await read(filehandle, buf, 0, chunkSize, totalRead); | ||
totalRead += bytesRead; | ||
if (bytesRead !== chunkSize) | ||
endOfFile = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: replace these two lines with endOfFile = bytesRead !== chunkSize;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't that very less efficient to set the variable at each iteration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About 5% slower according to the benchmarks I just ran, so "very less" is a bit overstating, still it's not nothing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, I assumed it would be faster due to lack of branching (at least it would be in a language like C). Oh well.
How large does it have to be? From the logic itself it seems as if a file larger than 16384 bytes is enough, or am I missing something? |
@TimothyGu Indeed, 16385 bytes would be enough to check that issue. Should I create such a file in the fixatures folder? |
@aduh95 Yeah that'd work. Or you could find a file in the source tree that's already that big (like |
a450a5e
to
614d2f5
Compare
const { readFile } = require('fs/promises'); | ||
const fixtures = require('../common/fixtures'); | ||
|
||
const fn = fixtures.path('../../AUTHORS'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to defeat the purpose of fixtures
. Maybe just create a file with
const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
const fn = path.join(tmpdir.path, 'large-file');
// Write a file and then read it again using `readFile`
Or, create a file with a known hash in fixtures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, this way I don't need the crypto
module anymore for the test
614d2f5
to
9d04eb8
Compare
lib/fs/promises.js
Outdated
let totalRead = 0; | ||
let endOfFile; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initialize this to false
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to keep one variable the same type (undefined vs. boolean), when it is used as a boolean anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough
9d04eb8
to
bbe41ed
Compare
// Creating large buffer with random content | ||
const buffer = Buffer.from( | ||
Array.apply(null, { length: 16834 * 2 }) | ||
.map(Number.call, Math.random) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: would just .map(Math.random)
work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed
bbe41ed
to
f083126
Compare
common.crashOnUnhandledRejection(); | ||
|
||
// Creating large buffer with random content | ||
const buffer = Buffer.from( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can generate this buffer with crypto.randomBytes(16834 * 2)
actually, but the current method LGTM anyway.
I am a bit confused about the CI result... |
Lots of red in the CI results that appear to be build bot failures... trying again https://ci.nodejs.org/job/node-test-pull-request/14293/ |
One last CI run: https://ci.nodejs.org/job/node-test-pull-request/14315/ |
CI passes (after a rerun on AIX). Landed in 11819c7. |
PR-URL: #19643 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
PR-URL: #19643 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This is based on a semver-major as far as I know, so I marked them as do not land on the other release lines. |
Fixes #19443
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesI am not sure about the testing, should I include a very large file on which we can test? On my local machine, I test using the scripts described in the issue.