Skip to content

Add fast-path in wildcard search#7515

Open
am11 wants to merge 2 commits into
NuGet:devfrom
am11:patch-1
Open

Add fast-path in wildcard search#7515
am11 wants to merge 2 commits into
NuGet:devfrom
am11:patch-1

Conversation

@am11

@am11 am11 commented Jun 26, 2026

Copy link
Copy Markdown

Bug

Fixes: NuGet/Home#13572

Description

Before: when all paths are pre-resolved (no wildcards), each AddFiles still enumerated the file's entire containing directory of size M via Directory.GetFiles, so resolving N files cost O(N*M) inode lookups (the dominant file I/O, and the frame that intermittently fails on VirtioFS).

After: each pre-resolved path is confirmed with a single File.Exists stat, O(N) total with one inode touched per file, falling back to enumeration only when the exact path isn't found.

PR Checklist

  • Meaningful title, helpful description and a linked NuGet/Home issue
  • Added tests (it's a performance improvement without changing the usage semantic)
  • Link to an issue or pull request to update docs if this PR changes settings, environment variables, new feature, etc.

@am11 am11 requested a review from a team as a code owner June 26, 2026 13:01
@am11 am11 requested review from Nigusu-Allehu and nkolev92 June 26, 2026 13:01
@dotnet-policy-service dotnet-policy-service Bot added the Community PRs created by someone not in the NuGet team label Jun 26, 2026
// on some virtual file systems (e.g. VirtioFS). Fall back to enumeration when not found.
if (!searchDirectory && !IsWildcardSearch(searchPath) && File.Exists(fullSearchPath))
{
return [new SearchPathResult(fullSearchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), isFile: true)];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's a subtle different here in that the casing of the result can be different, but idk if that matters.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

On case-insensitive filesystem, it will succeed File.Exists check and return the input casing. On case-sensitive filesystem it will fail File.Exists and continue to the fallback (existing) code.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

yes, my point was on case insensitive, it'll return the path with the casing provided in the method call, which may not match the true casing on disk and the previous implementation did just that.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I agree that it has no functional bearing, but just curious does it really resolve the path with actual disk casing? This test fails if we try to resolve the full path before returning from this method

public void PerformWildcardSearch_WithDirectoryRelativePathAndFileName_OnWindows_FindsMatchingFile(string searchPath)
.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a fast-path to PathResolver.PerformWildcardSearch to avoid directory enumeration when the input path is already fully resolved (no wildcards), improving performance and reducing susceptibility to intermittent failures on some virtual file systems.

Changes:

  • Compute a fullSearchPath once and reuse it for regex creation.
  • Add a direct File.Exists probe for non-directory, non-wildcard searches to avoid Directory.GetFiles enumeration in the common “exact file” case.

// A search path with no wildcards refers to a single file. Probe it directly instead of
// enumerating the whole directory, which is the dominant cost and can fail intermittently
// on some virtual file systems (e.g. VirtioFS). Fall back to enumeration when not found.
if (!searchDirectory && !IsWildcardSearch(searchPath) && File.Exists(fullSearchPath))

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It seems like an intentional decision going by line 151 below and this test case

[Theory]
[InlineData("", false)]
[InlineData("*", true)]
[InlineData("a.*", true)]
[InlineData("*.b", true)]
[InlineData("*.*", true)]
[InlineData("a/b", false)]
[InlineData("a/b.*", true)]
[InlineData(@"a\b", false)]
[InlineData(@"a\b.*", true)]
[InlineData("**", true)]
[InlineData("**/a", true)]
[InlineData("**/*.a", true)]
[InlineData(@"**\a", true)]
[InlineData(@"**\*.a", true)]
[InlineData("?", false)]
[InlineData("[", false)]
public void IsWildcardSearch_WithValidFilter_ReturnsExpectedResult(string filter, bool expectedResult)
{
Assert.Equal(expectedResult, PathResolver.IsWildcardSearch(filter));

@Nigusu-Allehu Nigusu-Allehu self-assigned this Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Community PRs created by someone not in the NuGet team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Suggestion: optimize NuGet.Packaging.PackageBuilder.AddFiles()

4 participants