Skip to content

Avoid recursive FileSystemWatcher when not required in PhysicalFilesWatcher#128072

Draft
svick wants to merge 1 commit into
dotnet:mainfrom
svick:non-recursive-watcher
Draft

Avoid recursive FileSystemWatcher when not required in PhysicalFilesWatcher#128072
svick wants to merge 1 commit into
dotnet:mainfrom
svick:non-recursive-watcher

Conversation

@svick
Copy link
Copy Markdown
Member

@svick svick commented May 12, 2026

Note

This pull request was authored by GitHub Copilot.

PhysicalFilesWatcher previously set IncludeSubdirectories = true unconditionally on the wrapped FileSystemWatcher. On Linux this means inotify watches are created for every descendant directory under the root, which is expensive (and can hit the per-user inotify limit) when the root is something like / — as is the case when an application is launched by systemd without an explicit WorkingDirectory. The default Hosting setup only watches appsettings.json and appsettings.<env>.json, neither of which actually requires recursion.

Now IncludeSubdirectories is enabled only when at least one registered token's pattern actually references a subdirectory:

  • a file path token whose path contains /
  • a wildcard token whose pattern contains / or **
  • or the wrapped FileSystemWatcher watches a strict ancestor of _root (where every event is in a subdirectory from the FSW's perspective)

The state is maintained via a small counter that is incremented when a qualifying token is actually added to the lookup and decremented when one is removed (via ReportChangeForMatchedEntries or OnError's CancelAll). The FSW's IncludeSubdirectories is re-evaluated every time TryEnableFileSystemWatcher runs (which is once per CreateFileChangeToken call), so the value can both go from false to true (when a subdirectory pattern is added) and back to false (when subdirectory tokens have all fired and a new root-only token is registered).

Fixes #126708.

…atcher

PhysicalFilesWatcher previously set IncludeSubdirectories=true unconditionally on the wrapped FileSystemWatcher. On Linux this causes inotify watches to be added for every descendant directory under the root, which can be very expensive (or hit inotify limits) when the root is something like / -- as happens with applications launched by systemd with no explicit WorkingDirectory.

Now IncludeSubdirectories is enabled only when at least one registered token's pattern actually references a subdirectory (file path contains '/', or wildcard contains '/' or '**'), or when the wrapped FSW watches a strict ancestor of root (where every event is in a subdirectory from the FSW's perspective). The state is maintained via a counter updated when tokens are added or removed, and re-evaluated each time the FSW is enabled.

Fixes dotnet#126708

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 12, 2026 12:16
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-io
See info in area-owners.md if you want to be subscribed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR changes Microsoft.Extensions.FileProviders.Physical’s PhysicalFilesWatcher to avoid enabling recursive FileSystemWatcher.IncludeSubdirectories unless it’s actually needed by the registered change-token patterns, reducing the risk of expensive recursive watch setup (notably on Linux/inotify) for common root-only patterns.

Changes:

  • Track whether any currently-registered token patterns require subdirectory watching and toggle IncludeSubdirectories accordingly when enabling the watcher.
  • Handle the “FSW path is an ancestor of _root” case by always requiring subdirectory watching.
  • Add tests validating upgrade/downgrade behavior of IncludeSubdirectories as tokens are added and removed.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFilesWatcher.cs Introduces _fileWatcherIsAboveRoot and a token counter to conditionally set IncludeSubdirectories in TryEnableFileSystemWatcher, and keeps the counter in sync on add/remove/error paths.
src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFilesWatcherTests.cs Adds unit tests to verify IncludeSubdirectories is enabled only when patterns require it and that it can upgrade/downgrade as tokens come and go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Runaway inotify watches with HostApplicationBuilder

2 participants