perf: avoid debouncer file-ID cache walk on watcher creation; incremental watcher registry - #122
Merged
mattenarle10 merged 1 commit intoJul 27, 2026
Conversation
…ntal watcher registry With delayMs set, tauri-plugin-fs routes watch() through notify-debouncer-full, whose file-ID cache walks the entire tree (stat/handle-open per entry) at watcher creation on macOS and Windows, inside a synchronous Tauri command on the main thread — freezing the UI at startup and on folder add for large roots. - use watchImmediate (no debouncer, no cache walk; identical event shapes) - coalesce JS-side with a fixed 350ms window (bounded refresh under sustained churn — a naive trailing-edge debounce would starve) - incremental watcher registry: adding a folder creates only that watcher; failed registrations are evicted so reconciliation retries them - extract a dependency-injectable controller + 7 unit tests over the lifecycle Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sashamerzliakov
added a commit
to sashamerzliakov/markamd
that referenced
this pull request
Jul 27, 2026
…attenarle10#123) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sashamerzliakov
added a commit
to sashamerzliakov/markamd
that referenced
this pull request
Jul 29, 2026
- pr-plan.md: fork track marked merged (#1 / d312b49), the three-way split replaced with what actually landed and why it landed as one PR, order of operations rewritten around what's done and what's next. - CUSTOM.md: files-touched list was three features out of date — it predates the watcher rewrite, the minimal-diff dispatch, the tsv work, the tests and the checklist. - Removed docs/upstream-submission-drafts.md and docs/upstream-zoom-pr-draft.md. Both described PRs that merged upstream as mattenarle10#122 and mattenarle10#123 in July; leaving them reads as pending work. The release gap is now stated in the plan rather than implied: with no fork release, this repo's install link and badges point at upstream's builds, so a visitor downloading from the front page gets an app without any of the features listed above the link. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #121.
Transparency up front: I hit this bug as a user; the root-cause tracing, fix, and tests were done with AI assistance (Claude — see the commit trailer), with the mechanism claims verified against the vendored crate sources cited below. I've verified the behaviour on my own machine (macOS, Apple Silicon) and I'm happy to answer questions — same tooling in the loop.
What changed
use-folder-watcher.ts+ tests. Three behavior changes, stated explicitly:watchImmediateinstead of debouncedwatch()— withdelayMs, tauri-plugin-fs routes through notify-debouncer-full, whose file-ID cache walks the entire tree (astat/handle-open per entry) atdebouncer.watch()time on macOS and Windows, inside a synchronous Tauri command on the main thread.watchImmediateskips the debouncer entirely. Event schemas are identical (both branches serialize the innernotify::Event), soisDirectoryChangeEventfilters both the same way — refresh timing changes, per point 2.Testing
bun test55 pass. The watcher lifecycle previously had no unit coverage and no seam for injecting fakes; the extracted controller provides both.NoCache), so this change mainly benefits macOS/Windows; the incremental registry benefits all platforms.Tradeoff / limitation
Raw events now cross IPC uncoalesced during bulk writes; the JS window bounds refresh work but not message volume. If that ever shows up in practice, the durable fix is upstream in tauri-plugin-fs (async command / opt-out of the cache).
watchableFolderPaths/isDirectoryChangeEventexports are unchanged.