fix(filewatch): debounce raw OS events and serve the settled file#58
Merged
Conversation
Rework FileWatcher coalescing into a trailing-edge debounce: a file's burst of raw OS events (truncate/write/close plus the Windows duplicate events) now collapses into a single notification fired only after the file has been quiet for the debounce window. By then the editor has closed the handle, so every subscriber that re-reads the file observes the finished file instead of one mid-write. Injects TimeProvider so the debounce is testable. Because the watcher now delivers a change only after the writer has closed, MarkdownContentService can carry its cached body on each DiscoveredItem (RawBody) and MarkdownContentParser serves it directly, skipping a redundant per-request disk read + re-parse that could otherwise observe a half-written file. Also guard the file-watch path against adopting an ExcludePaths subtree on edit (IsExcludedAbsolute), matching DiscoverFiles, so a source that carves out a subtree for another source can't publish a competing route.
|
🛰️ Docs preview: https://pr-58.pennington-dev.pages.dev Rebuilt on every push to this PR; torn down when it closes. |
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.
What
Makes the file-watch path deliver settled files, and serves the resulting cached parse instead of re-reading from disk. Three related changes on the watch path:
1. Trailing-edge debounce in
FileWatcherReplaces the old leading-edge coalescing with a trailing-edge debounce. A single save fans out into a truncate/write/close burst (plus the duplicate events Windows raises); these now collapse into one notification fired only after the file has been quiet for the debounce window. By then the editor has closed the handle, so every subscriber that re-reads the file (each
IFileWatchAwarecache, the live-reload server) observes the finished file instead of one mid-write — no sharing violations, no momentarily-truncated bodies.TimeProvideris injected so the debounce is testable.2. Serve the cached body from
MarkdownContentService→MarkdownContentParserBecause the watcher now only delivers a change after the writer has closed,
MarkdownContentService.DiscoverAsynccan carry its already-parsed body on eachDiscoveredItem(newRawBody), andMarkdownContentParserserves it directly. This skips a redundant per-request disk read + re-parse that could otherwise observe a half-written file. Falls back to reading the file for sources that don't pre-parse a body.3. Guard the watch path against
ExcludePathssubtreesOn edit,
MarkdownContentServicenow skips files inside anExcludePathssubtree (IsExcludedAbsolute), matching whatDiscoverFilesalready does at discovery time — so a source that carves out a subtree for another source can't adopt an excluded file and publish a route that competes with the owning source's.Test plan
dotnet build Pennington.slnx— whole solution compiles (covers theFileWatcherconstructor-signature change and theRawBodyaddition across all consumers).dotnet test tests/Pennington.Tests— 1037 passed, 0 failed, including new tests:FileWatcher_CoalescesRapidBurst_IntoSingleNotificationParseAsync_DiscoveredItemCarriesCachedBody_ServesCacheWithoutReadingDiskParseAsync_CachedBodyWithoutMetadata_FallsBackToDiskOnFileChanged_Changed_ExcludedSubtree_NotCachedBranched off
main; contains no blog/content edits.