Skip to content

Add regression test for reading the file-app sentinel while a writer holds it open - #21456

Open
David Fowler (davidfowl) wants to merge 3 commits into
mainfrom
agent/fix-fileapp-rebuild-worker-ran-race
Open

David Fowler (davidfowl) wants to merge 3 commits into
mainfrom
agent/fix-fileapp-rebuild-worker-ran-race

Conversation

@davidfowl

@davidfowl David Fowler (davidfowl) commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Update after merging main (2026-10-30): #21360 has since landed on main. It switched both sentinel polls in FileAppRebuildCommandBuildsEditedSourceAndRestartsResource to a new MarkerHasValue helper, which opens the file with the same FileShare.ReadWrite | FileShare.Delete mode, so the sharing-violation fix described below is already on main. To resolve the conflict, I dropped this PR's duplicate ReadSentinelWrittenByRunningProcess helper and kept main's polls. This PR now contributes regression coverage only: MarkerHasValueToleratesConcurrentWriter holds the sentinel open as File.WriteAllText(Async) does (FileMode.Create, FileAccess.Write, FileShare.Read) and asserts that MarkerHasValue reads it. It also checks that a missing file returns false. It passes locally together with FileAppRebuildCommandBuildsEditedSourceAndRestartsResource, and exact-head CI is green. The analysis and before/after evidence below were gathered against the pre-#21360 base and still describe the root cause.

Fixes #20912: DotnetProjectBuildCoordinatorTests.FileAppRebuildCommandBuildsEditedSourceAndRestartsResource intermittently fails on Windows with:

System.IO.IOException : The process cannot access the file '...\Workspace\<id>\worker-ran.txt' because it is being used by another process.
   at System.IO.File.ReadAllText(String path, Encoding encoding)
   at ...FileAppRebuildCommandBuildsEditedSourceAndRestartsResource (polling predicate)

Root cause

The file app under test writes its sentinel with File.WriteAllTextAsync, which opens the file FileAccess.Write, FileShare.Read. The test polls the sentinel with File.ReadAllText, which opens FileAccess.Read, FileShare.Read. That share mode denies any existing writer.

On Windows, if the poll opens the file while the app's write handle is open, the open fails with a sharing violation. The exception escapes the AssertIsTrueRetryAsync predicate and fails the test. The reverse ordering can also make the app's own write fail. Linux doesn't enforce these share modes, which is why every failure is on Windows.

Fix

The polls go through ReadSentinelWrittenByRunningProcess, which opens the sentinel with FileShare.ReadWrite | FileShare.Delete, so neither the test nor the app can hit a sharing violation. The polls still wait for the exact expected content ("initial", then "updated"). A partially written read just doesn't match yet.

No assertions were weakened, no sleeps or retries were added, and no exceptions are swallowed apart from FileNotFoundException, which replaces the previous File.Exists check. The change is test-only.

Regression test ReadSentinelWrittenByRunningProcessToleratesConcurrentWriter: it holds the sentinel open exactly as File.WriteAllText(Async) does (FileMode.Create, FileAccess.Write, FileShare.Read) and reads it through the helper. This is deterministic, with no timing involved.

Evidence

CI window: ci.yml runs created 2026-10-16..2026-10-30 (main push, PR, dispatch), first attempt only, job Hosting.Dotnet (ubuntu-latest|windows-latest).

  • Denominator: 1,051 completed first-attempt jobs (Windows 498, Ubuntu 508; 100 cancelled excluded). 40 failed.
  • Windows: 7/498 (1.4%) failed with this exact worker-ran.txt sharing violation. All 7 were thrown from the polling predicate: 6 from the initial poll, 1 from the post-rebuild poll.
    • 2 other Windows failures were StopAsync timeouts in other tests (the DCP stop family, tracked separately).
    • 3 were build errors.
    • 25 had logs unavailable (BlobNotFound), counted as unknown, so 7 is a lower bound.
  • Ubuntu: 0/508 test failures (3 build errors).

Before/after on windows-latest (this PR):

Head Change Hosting.Dotnet (windows-latest)
2f10a45c (before) regression test + helper using File.ReadAllText failed: ReadSentinelWrittenByRunningProcessToleratesConcurrentWriter threw the same IOException ... worker-ran.txt ... being used by another process from File.ReadAllText (199/200 passed). job
b319cccc (after) helper opens with FileShare.ReadWrite | FileShare.Delete passed: 200/200, including the regression test and FileAppRebuildCommandBuildsEditedSourceAndRestartsResource. job. Full CI run 41069348773 green on attempt 1.

On Linux, both heads pass. Share modes aren't enforced there, which is consistent with 0/508 Linux failures. Locally on Linux after the fix, both tests passed 3/3.

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
    • No
  • Does the change require an update in our Aspire docs?
    • Yes
    • No

🤖 Posted by AspireAgent (AI Agent)

…writing

The FileAppRebuildCommandBuildsEditedSourceAndRestartsResource polls now go
through ReadSentinelWrittenByRunningProcess, which keeps the current
File.ReadAllText behavior. The new test holds the sentinel open the way
File.WriteAllText does and is expected to fail on Windows until the reader
is fixed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 21456

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 21456"

@github-actions github-actions Bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Oct 30, 2026
@aspire-repo-bot
aspire-repo-bot Bot requested a balanced review from Copilot October 30, 2026 13:56
@github-actions

This comment has been minimized.

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.

🔵 Needs a closer look

The draft contains only a regression test, while the sharing-safe reader implementation and Windows before/after validation remain pending.

No open issues 🎉

What changed in this PR

This draft adds regression coverage for the Windows sentinel-file sharing violation reported in #20912. The current changes prepare a “before” run; the fix is not yet included.

Changes:

  • Extracts sentinel reads into a helper used by both rebuild polling checks, preserving existing read behavior.
  • Adds a test that reads the sentinel while a writer remains open and verifies missing-file handling.
File Description
tests/​Aspire.Hosting.Dotnet.Tests/​DotnetProjectBuildCoordinatorTests.cs Extracts sentinel reading and adds concurrent-writer regression coverage.

Review effort: Balanced

File.ReadAllText opens with FileShare.Read, which on Windows conflicts with the
running file app's File.WriteAllTextAsync write handle and throws a sharing
violation out of the polling predicate. Open the sentinel with
FileShare.ReadWrite | FileShare.Delete so polling never collides with the
writer; the poll still waits for the exact expected content.

Fixes #20912

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

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.

🟢 Approval recommended

No active review issues were identified for the test-only change addressing Windows sentinel-file sharing violations.

No open issues 🎉

Review effort: Balanced

@github-actions

This comment has been minimized.

# Conflicts:
#	tests/Aspire.Hosting.Dotnet.Tests/DotnetProjectBuildCoordinatorTests.cs
@github-actions

Copy link
Copy Markdown
Contributor

Tests selector

1 / 100 PR test projects · 0 PR jobs, from 1 changed file.

Selected PR test projects (1 / 100)

Aspire.Hosting.Dotnet.Tests

Selected PR jobs (0)

none


How these were chosen — grouped by what changed

🧪 tests/Aspire.Hosting.Dotnet.Tests/DotnetProjectBuildCoordinatorTests.cs (changed test)
→ 1 directly: Aspire.Hosting.Dotnet.Tests

Job reasons

none


Selection computed for commit 2e25fa1.

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.

🟢 Approval recommended

No active review issues are identified for the added sentinel-file regression coverage.

No open issues 🎉

Review effort: Balanced

Copy link
Copy Markdown
Collaborator Author

Merged main into this branch to resolve the conflict in DotnetProjectBuildCoordinatorTests.cs. main has since added an equivalent sharing-safe MarkerHasValue helper and switched the file-app rebuild test to it. I removed this PR's duplicate ReadSentinelWrittenByRunningProcess helper and pointed the PR's concurrent-writer regression test, now MarkerHasValueToleratesConcurrentWriter, at main's helper. The test keeps the same writer handle and assertions. Both tests pass locally.

On the new head, Build CLI (osx-x64) failed during infrastructure setup at the "Upload RID-specific packages" step (Failed to CreateArtifact: Unable to make request: ENOTFOUND, job). This is unrelated to the change, so I'm rerunning the failed job once.

🤖 Posted by AspireAgent (AI Agent)

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@davidfowl David Fowler (davidfowl) changed the title Fix Windows sharing violation when polling file-app sentinel in DotnetProjectBuildCoordinatorTests Add regression test for reading the file-app sentinel while a writer holds it open Oct 31, 2026

This branch has not been deployed

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

Labels

flaky-test needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners

Projects

None yet

3 participants