fs: watch directories, not files, in recursive fs.watch fallback - #65486
fs: watch directories, not files, in recursive fs.watch fallback#65486codebytere wants to merge 1 commit into
Conversation
|
Review requested:
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #65486 +/- ##
==========================================
- Coverage 90.12% 90.11% -0.01%
==========================================
Files 752 751 -1
Lines 252315 252488 +173
Branches 47444 47497 +53
==========================================
+ Hits 227395 227538 +143
- Misses 16217 16266 +49
+ Partials 8703 8684 -19
🚀 New features to boost your workflow:
|
6a5d660 to
bd967d5
Compare
The JavaScript recursive watcher used on platforms without a native one (notably Linux) armed an fs.watch() handle and a stat() for every file in the tree, and answered every event by stat()ing and re-reading the whole directory it happened in. A 13k-entry tree cost 13.5k inotify watches, ~150 ms and ~50 MB to set up, and appending to one file in a 3900-entry directory cost ~9 ms of CPU per event. A file replaced by rename() (the usual editor save) also stopped being reported, since its watch stayed on the old inode. inotify reports changes to the entries of a watched directory, with their names, so on Linux watch each directory once (symbolic links keep their own watcher, as before), keep the set of known paths, and resolve an event with a single stat() of the named entry: unknown names are added and reported as 'rename', vanished ones are dropped and reported as 'rename', file changes are reported as 'change'. kqueue and event ports only report that a directory changed, so on the other platforms served by this fallback every file keeps its own watcher and a directory event rescans that directory, as before. unref() and ref() now reach the underlying handles. Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
bd967d5 to
7774d81
Compare
|
the smartos failure was legit: event ports (and kqueue) only report that a watched directory changed, not which entry or writes to files inside it, so per-directory watches are an inotify-only option. 7774d81 keeps the per-directory design on Linux and, on the other platforms this fallback serves, keeps a watcher per file and rescans the directory on a directory event as |
Makes the JavaScript recursive watcher (used where
fs.watch()has no native recursive mode) arm one watcher per directory instead of one per file on Linux, and answer each event with onestat()instead of re-reading the directory.Linux, this repository's
test/tree (13 500 entries, 896 directories):fs.watch(test, { recursive: true })setupbenchmark/fs/bench-watch-recursive.js(new),test/fixtures, 30 runstest/paralleltest/parallelThe fallback armed an
fs.watch()handle and ran astatSync()for every file in the tree, and handled every event by stat()ing and re-reading the whole directory it happened in. A file replaced byrename()(the usual editor save) also stopped being reported, because its watch stayed on the old inode, andwatcher.ref()/unref()were no-ops on this path.inotify reports changes to a watched directory's entries with the entry name, so on Linux the watcher now keeps one handle per directory (symbolic links keep their own, as before) plus the set of known paths, and resolves an event with a single
stat()of the named entry: new names are reported as'rename'and descended into if they are directories, vanished ones are dropped with their subtree and reported as'rename', changes to known files are'change'. kqueue and event ports only report that the directory itself changed, so on the other platforms this fallback serves (illumos, the BSDs, AIX) files keep a watcher each and a directory event rescans that directory, as today. Two event differences remain, both matching the native watchers: creating a file with content yields'rename'then'change', and a file keeps being reported after it is replaced byrename().ref()/unref()now reach the handles.Tests: all
test-fs-watch-recursive-*,test-fs-watch-ignore-*,test-fs-promises-watch*and watch-mode tests pass in both modes (the per-file mode exercised on Linux by flipping the platform check; recursive ones 3/3 repeated runs); a new Linux-only test covers the per-directory handle count, rename-over reporting, events behind a symbolic link, removal of a watched root directory and root file, andref()/unref().Disclosure: the code, test, benchmark, measurements and this description were written by Claude Code, directed and reviewed by @codebytere.