fix(core): use recursive FSEvents on macOS instead of non-recursive kqueue#34523
fix(core): use recursive FSEvents on macOS instead of non-recursive kqueue#34523comp615 wants to merge 1 commit intonrwl:masterfrom
Conversation
👷 Deploy request for nx-docs pending review.Visit the deploys page to approve it
|
👷 Deploy request for nx-dev pending review.Visit the deploys page to approve it
|
|
View your CI Pipeline Execution ↗ for commit c086f96
☁️ Nx Cloud last updated this comment at |
comp615
left a comment
There was a problem hiding this comment.
Note: I feel like there might be a better refactor / solution here (e.g. pull them all up since they are common? Should FileEventKind::Modify(ModifyKind::Data(_)) => continue, even exist now?)...but I didn't have a full understanding and this is a narrow change. Feel free to build upon!
There was a problem hiding this comment.
Important
At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.
Nx Cloud has identified a possible root cause for your failed CI:
This CI failure appears to be related to the environment or external dependencies rather than your code changes.
No code changes were suggested for this issue.
You can trigger a rerun by pushing an empty commit:
git commit --allow-empty -m "chore: trigger rerun"
git push
🎓 Learn more about Self-Healing CI on nx.dev
…queue PR nrwl#34329 switched all watched paths to non-recursive to reduce inotify watch count on Linux. On macOS, the notify crate uses kqueue for non-recursive watches instead of FSEvents. kqueue silently fails at scale (~5,250+ directories) due to vnode table pressure, causing the daemon to never detect file changes in large monorepos. This fix uses platform-conditional watch modes: - macOS: single recursive watch on the workspace root (uses FSEvents) - Linux/Windows: non-recursive per-directory watches (preserves nrwl#33781 fix) On macOS, FSEvents handles recursive watching natively from a single root path, so directory enumeration and dynamic registration are skipped entirely. This also improves daemon startup time on macOS (from ~10 minutes to <1 second in a 354-project monorepo). Fixes nrwl#34522 Amp-Thread-ID: https://ampcode.com/threads/T-019c7804-5473-73ae-861f-d5dc1572a1ce Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019c7804-5473-73ae-861f-d5dc1572a1ce Co-authored-by: Amp <amp@ampcode.com>
7cb2bda to
c086f96
Compare
Current Behavior
Since Nx 22.5.0, the daemon's native file watcher silently drops all file change events on macOS in large monorepos (~5,250+ watched directories).
nx watch,nx serve, and any daemon-dependent file watching is broken.The root cause is that #34329 switched all watched paths to
WatchedPath::non_recursive(). On macOS, thenotifycrate uses kqueue for non-recursive watches instead of FSEvents. kqueue silently fails at scale due to vnode table pressure (kern.num_vnodes == kern.maxvnodes), causing the daemon to never detect file changes.This is a scale-dependent bug: it works fine in small workspaces (~30 directories) but breaks silently in large ones.
Expected Behavior
The macOS file watcher should detect file creates, modifications, and deletions at any scale, matching the behavior of Nx 22.4.x.
Fix
Use platform-conditional watch modes:
On macOS, FSEvents handles recursive watching from a single root path, so directory enumeration and dynamic registration are skipped entirely. This also improves daemon startup time on macOS from ~10 minutes to <1 second in a 354-project monorepo.
What changed in
watcher.rson_action): Wrapped in#[cfg(not(target_os = "macos"))]since FSEvents already watches the full tree.Linux/Windows behavior is completely unchanged.
Why the event filter is fine as-is
We verified that with recursive FSEvents watches, macOS emits specific
FileEventKindvariants (Create(File),Modify(Data(Content)),Remove(File),Modify(Name(Any))) that the currentwatch_filterer.rsalready handles correctly. Zero events were rejected by the catch-all. TheModify(Any)/Create(Any)variants are kqueue artifacts that are not needed with FSEvents.Why kqueue fails silently
Apple's File System Events Programming Guide explicitly recommends FSEvents over kqueue for large hierarchies: "If you are monitoring a large hierarchy of content, you should use file system events instead." kqueue requires
open(path, O_EVTONLY)per watched directory. Under vnode table pressure, the kernel recycles vnodes with kqueue watches attached without notifying the watcher. There is no error, no partial delivery, and no diagnostic signal.Tested on
Related Issue(s)
Fixes #34522