Skip to content
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: fix nonNativeWatcher watching folder with existing files #45500

Merged

Conversation

MoLow
Copy link
Member

@MoLow MoLow commented Nov 17, 2022

a simple case that has not worked before this fix:

const fs = require('fs');
const watcher = fs.watch(__dirname, { recursive: true });
watcher.on('change', (event, filename) => {
  console.log(event, filename);
});

setTimeout(() => fs.writeFileSync(__filename, fs.readFileSync(__filename)), 1000);

@MoLow MoLow requested a review from anonrig November 17, 2022 21:29
@nodejs-github-bot nodejs-github-bot added fs Issues and PRs related to the fs subsystem / file system. needs-ci PRs that need a full CI run. labels Nov 17, 2022
@MoLow MoLow force-pushed the fix-recursive-watch-existing-file branch from d76e5e4 to 4a4b599 Compare November 17, 2022 21:29
test/parallel/test-fs-watch-recursive.js Show resolved Hide resolved
lib/internal/fs/recursive_watch.js Outdated Show resolved Hide resolved
lib/internal/fs/recursive_watch.js Show resolved Hide resolved
test/parallel/test-fs-watch-recursive.js Show resolved Hide resolved
test/parallel/test-fs-watch-recursive.js Show resolved Hide resolved
lib/internal/fs/recursive_watch.js Show resolved Hide resolved
@MoLow
Copy link
Member Author

MoLow commented Nov 17, 2022

CC @nodejs/fs

@MoLow
Copy link
Member Author

MoLow commented Nov 25, 2022

@anonrig I was able to find the inconsistency on macOS:
it seems that a file is considered renamed if it is created in the last 20 seconds

const fs = require('fs');
const path = require('path');


setInterval(() => fs.writeFileSync(__filename, fs.readFileSync(__filename)), 2000);

const watcher = fs.watch(__dirname, { recursive: true });
watcher.on('change', (event, filename) => {
  if (filename === path.basename(__filename))
    console.log(event, Date.now() - fs.statSync(filename).birthtimeMs);
});

outputs:

./node a.js
rename 3191.91748046875
rename 5191.91748046875
rename 7192.91748046875
rename 9192.91748046875
rename 11193.91748046875
rename 13194.91748046875
rename 15195.91748046875
rename 17195.91748046875
rename 19197.91748046875
rename 21198.91748046875
change 23199.91748046875
change 25200.91748046875
change 27201.91748046875

@MoLow MoLow requested a review from anonrig November 25, 2022 07:16
@@ -202,6 +205,10 @@ class FSWatcher extends EventEmitter {
this.emit('change', 'rename', pathRelative(this.#rootPath, file));
} else if (currentStats.isDirectory()) {
this.#watchFolder(file);
} else {
// Watching a directory will trigger a change event for child files)
const event = DateNow() - currentStats.birthtimeMs < NEW_FILES_AGE ? 'rename' : 'change';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this boolean expression better for detecting the creation of files?

currentStats.birthTimeMs !== 0 && previousStats.birthtimeMs === 0

@anonrig
Copy link
Member

anonrig commented Nov 25, 2022

@anonrig I was able to find the inconsistency on macOS: it seems that a file is considered renamed if it is created in the last 20 seconds

@MoLow This code does not produce rename on macOS with node 19 for me.

➜  node git:(perf/stream-encoding) ✗ node test.js
change 10095.632568359375
change 12096.632568359375
change 14097.632568359375
change 16098.632568359375
change 18100.632568359375
change 20100.632568359375
change 22100.632568359375
change 24101.632568359375
change 26102.632568359375
change 28102.632568359375
change 30104.632568359375
change 32104.632568359375
change 34105.632568359375
change 36106.632568359375
change 38105.632568359375

You are using ./node. I assume this is your personal build. Can you retry using the distributed node and validate if you receive the same response?

@MoLow
Copy link
Member Author

MoLow commented Nov 26, 2022

This code does not produce rename on macOS with node 19 for me.

@anonrig this reproduces only in the case where the file is "new" IE created recently,
besides here you said macOS only fires rename now you say it only fires change - so you agree it is inconsistent :)

@anonrig
Copy link
Member

anonrig commented Nov 27, 2022

@anonrig this reproduces only in the case where the file is "new" IE created recently, besides here you said macOS only fires rename now you say it only fires change - so you agree it is inconsistent :)

@MoLow I dug into the Node code, as it seems if when the file is newly created, it triggers a rename event, but after a while, it is changed to a change event. For me, it was 10 seconds, although for you, it was 20 seconds. (On a different run, I also had a 20 second delay). For this particular reason (inconsistency) my thoughts are to be persistent, and emit a change event only.

I looked into both libuv code and kqueue docs. I think we should open an issue to libuv to understand the reasoning behind this, and document this on the Node docs.

Meanwhile, I recommend, only emitting change event, add a comment to the particular line that throws change and just merging this pull request, for the sake of unblocking development. WDYT @MoLow?

PS: My thoughts are: Let's keep the test as it is right now.

@MoLow
Copy link
Member Author

MoLow commented Nov 27, 2022

@anonrig I have reverted the condition, can you approve?

@MoLow MoLow added the request-ci Add this label to start a Jenkins CI on a PR. label Nov 27, 2022
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Nov 27, 2022
@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@MoLow MoLow added the commit-queue-squash Add this label to instruct the Commit Queue to squash all the PR commits into the first one. label Nov 27, 2022
@nodejs-github-bot
Copy link
Collaborator

@MoLow MoLow added the commit-queue Add this label to land a pull request using GitHub Actions. label Nov 27, 2022
@nodejs-github-bot nodejs-github-bot removed the commit-queue Add this label to land a pull request using GitHub Actions. label Nov 27, 2022
@nodejs-github-bot nodejs-github-bot merged commit 147d810 into nodejs:main Nov 27, 2022
@nodejs-github-bot
Copy link
Collaborator

Landed in 147d810

@MoLow MoLow deleted the fix-recursive-watch-existing-file branch November 27, 2022 22:03
@danielleadams
Copy link
Contributor

@MoLow, same here - the test couldn't be landed in v18.x-staging. Do you mind creating a backport PR here too?

@danielleadams danielleadams added the backport-requested-v18.x PRs awaiting manual backport to the v18.x-staging branch. label Dec 30, 2022
MoLow added a commit to MoLow/node that referenced this pull request Dec 31, 2022
@MoLow MoLow added backport-open-v18.x Indicate that the PR has an open backport. and removed backport-requested-v18.x PRs awaiting manual backport to the v18.x-staging branch. labels Dec 31, 2022
MoLow added a commit to MoLow/node that referenced this pull request Jan 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport-open-v18.x Indicate that the PR has an open backport. commit-queue-squash Add this label to instruct the Commit Queue to squash all the PR commits into the first one. fs Issues and PRs related to the fs subsystem / file system. needs-ci PRs that need a full CI run.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants