-
Notifications
You must be signed in to change notification settings - Fork 30k
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#opendir does not return all files in subdirectories when bufferSize is less than the directory size #48820
Comments
It looks like the problem here is caused by original: Lines 172 to 181 in 12a93ce
revised: Lines 172 to 185 in 11cedfe
Tagging @Ethan-Arrowood as you seem to have been here most recently. |
Yeah this was identified recently - i have a PR fixing it differently: #48698 WDYT? |
I tried your branch out and it does not fix my issue. I think the problem is because opendir and readdir don't share the same code paths. On the upside, your branch pointed me to where I can change a test to illustrate the issue. I will update my PR branch with a change to |
Okay cool. I'll focus on finishing up the change necessary for |
This shows the test change that triggers my issue 9483314 |
Any update on this? The issue is still reproducible in v20.8.0 and my proposed fix above still seems reasonable? |
Interesting. I assumed #49603 would fix your issue. Feel free to send a PR with another fix though and we can get it landed asap! |
@mdouglass I saw you have a draft open that would just need an update? |
I haven't touched it as I was still awaiting feedback on the questions I raised in the original PR.
|
Besides a bug, this may be a lack of documentation. juanarbol@b35181f#diff-5a0fa708afa6a2202dc64e5038e16c8e249cb1c3ec23c87885acc2a662a1ec00L48 It used to read up to 32 folders, now, it can read as https://github.com/juanarbol/node/blob/main/lib/internal/fs/dir.js#L52 BufferSize will be 32 by default, and overwritten by the provided args. |
This bug specifically happens with import { mkdir, open, opendir, rmdir } from "node:fs/promises";
await mkdir("issue48820");
await mkdir("issue48820/1");
await mkdir("issue48820/1/1");
await mkdir("issue48820/1/1/1");
await mkdir("issue48820/1/1/2");
await mkdir("issue48820/1/2");
await mkdir("issue48820/2");
const dir = await opendir("issue48820", { bufferSize: 1, recursive: true });
for await (const dirent of dir)
console.log(`${dirent.parentPath}/${dirent.name}`);
// Actual output:
// issue48820/1
// issue48820/1/1
// issue48820/1/1/1
// issue48820/2 In the root directory both items are found. However, in each subdirectory only one is found. @Ethan-Arrowood implemented the |
Okay I think I am understanding the issue. But there is a lack of documentation on |
Okay, I've added an assertion to the opendir recursive test file that proves this bug: diff --git a/test/sequential/test-fs-opendir-recursive.js b/test/sequential/test-fs-opendir-recursive.js
index a7e9b9a318..4328f1f4c1 100644
--- a/test/sequential/test-fs-opendir-recursive.js
+++ b/test/sequential/test-fs-opendir-recursive.js
@@ -132,6 +132,7 @@ function getDirentPath(dirent) {
}
function assertDirents(dirents) {
+ assert.strictEqual(dirents.length, expected.length);
dirents.sort((a, b) => (getDirentPath(a) < getDirentPath(b) ? -1 : 1));
assert.deepStrictEqual(
dirents.map((dirent) => {
@@ -221,3 +222,10 @@ function processDirCb(dir, cb) {
test().then(common.mustCall());
}
+
+// BufferSize
+{
+ const dir = fs.opendirSync(testDir, { bufferSize: 1, recursive: true });
+ processDirSync(dir);
+ dir.closeSync();
+} Will fix here: #55744 |
How the buffering works is that in a non-recursive mode, the first read operation will fill the buffer up to that limit. and then reads will be pulled from that buffer until its empty, and then it will do another read operation, and repeat this process until the entire handle is read. This works fine because its all handled at the C level. That is how it knows where to pick up again when it needs to read 1+ times. But in recursive mode, the implementation is only at the JS level. when a read happens, it is breadth first. Meaning like reading the root This issue has highlighted a fundamental issue with the implementation. Fixing it will require a bit of work to do, but it shouldn't be hard. I'll have a fix soon. Maybe an example? Lets say you have a bufferSize of
Then when the user calls
The next call to The next call to Thus, I need to completely rework the implementation here to handle all of this. |
The bufferSize option was not respected in recursive mode. This PR implements a naive solution to fix this issue as, until a better implementation can be designed. Fixes: nodejs#48820
PR is ready for review 😄 It uses the naive solution @mdouglass shared above. I think this okay so that we get the bug fixed immediately. We can improve the efficiency / implementation as a follow up IMO. |
The bufferSize option was not respected in recursive mode. This PR implements a naive solution to fix this issue until a better implementation can be designed. Fixes: nodejs#48820
The bufferSize option was not respected in recursive mode. This PR implements a naive solution to fix this issue until a better implementation can be designed. Fixes: nodejs#48820
The bufferSize option was not respected in recursive mode. This PR implements a naive solution to fix this issue until a better implementation can be designed. Fixes: nodejs#48820
Thanks @Ethan-Arrowood, this lgtm. |
PR merged 🚀 |
May I suggest that a warning be added to the documentation ? |
That would be a great addition until #55764 is fixed. Would you like to send a PR? |
I'm not very used to doing this but here is a suggestion anyway #55876 |
The bufferSize option was not respected in recursive mode. This PR implements a naive solution to fix this issue until a better implementation can be designed. Fixes: nodejs#48820 PR-URL: nodejs#55744 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
The bufferSize option was not respected in recursive mode. This PR implements a naive solution to fix this issue until a better implementation can be designed. Fixes: nodejs#48820 PR-URL: nodejs#55744 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
Version
Node.js v20.4.0
Platform
Linux rowlf 6.3.11-200.fc38.x86_64 #1 SMP PREEMPT_DYNAMIC Sun Jul 2 13:17:31 UTC 2023 x86_64 GNU/Linux
Subsystem
fs
What steps will reproduce the bug?
If I call fs#opendir in recursive mode on a directory structure where a subdirectory has more than
bufferSize
files in it, than the complete results will not be returned (each subdirectory gets truncated tobufferSize
entries.I have attached a code sample that demonstrates the bug. It requires a directory structure with at least 64 files in a subdirectory of a directory named
to-read
:I created the above on my machine with
for i in $(seq 1 64); do; touch $i; done
repro-opendir.mjs:
How often does it reproduce? Is there a required condition?
With the directory structure as described it reproduces 100% on node.js v20.3 and v20.4. I have not tried other versions.
What is the expected behavior? Why is that the expected behavior?
Neither assertion should trigger.
What do you see instead?
With the above code sample, the second assert will fail with the following message:
Additional information
No response
The text was updated successfully, but these errors were encountered: