Skip to content

Make the upgrade-wrapper E2E marker read tolerant of the detached writer#448

Merged
ppXD merged 2 commits into
mainfrom
fix/windows-upgrade-wrapper-marker-race
Jun 15, 2026
Merged

Make the upgrade-wrapper E2E marker read tolerant of the detached writer#448
ppXD merged 2 commits into
mainfrom
fix/windows-upgrade-wrapper-marker-race

Conversation

@ppXD

@ppXD ppXD commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Wrapper_DetachedTask_RunsAsSystemAfterWrapperExits intermittently failed with IOException: ... because it is being used by another process (seen once on PR Wire disk self-heal into K8s-agent local-exec mode #440 CI, passed on re-run). The detached Task Scheduler inner script writes the identity marker asynchronously; the test waited for the file to exist, then immediately File.ReadAllText'd it — which opens with FileShare.Read and collides with the writer's still-open handle (and could also read a partial/empty file mid-write).
  • Replace the read with ReadMarkerWhenReady: open the marker with FileShare.ReadWrite (don't contend on the writer's handle) and poll until the content is non-empty, within a bounded 30s timeout with an actionable failure message naming the marker path.

Scope

  • Surgical to the test/helper. The other marker-based tests only assert existence, so only this content-read needed the fix.
  • No production change — the marker is a test fixture; production reads upgrade traces from the agent over Halibut RPC, not via a concurrent local File.ReadAllText, so the race does not exist in production code (verified).
  • Windows-only E2E (gated behind OperatingSystem.IsWindows()).

Test plan

  • Squid.WindowsTentacleE2ETests compiles clean cross-platform (the test body skips on non-Windows).
  • Windows E2E CI (WindowsTentacle*E2E) exercises the real path; the fix removes the file-handle contention that caused the intermittent IOException.

Wrapper_DetachedTask_RunsAsSystemAfterWrapperExits intermittently failed with
"The process cannot access the file ... because it is being used by another
process". The detached Task Scheduler inner script writes the identity marker
asynchronously; the test waits for the file to EXIST, then immediately
File.ReadAllText'd it — which opens with FileShare.Read and collides with the
writer's still-open handle (and could also read a partial/empty file mid-write).

Replace the read with ReadMarkerWhenReady: open the marker with
FileShare.ReadWrite (don't contend on the writer's handle) and poll until the
content is non-empty, within a bounded 30s timeout with an actionable failure
naming the marker path. The other marker-based tests only assert existence, so
only this content-read needed the fix.

Test-fixture race only — the marker is a test artifact; production reads upgrade
traces from the agent over Halibut RPC, not via a concurrent local file read, so
no production upgrade-wrapper code changes. Windows-only E2E (gated behind
OperatingSystem.IsWindows()); the project compiles clean cross-platform.
@ppXD ppXD added this to the 1.9.3 milestone Jun 15, 2026
The detached-task identity assertions read the marker the inner script
writes asynchronously. Polling until merely non-empty leaves a narrow
window where a torn/partial read could return content that then fails the
'system' assertions. Have ReadMarkerWhenReady accept an optional readiness
predicate (default: non-empty) and pass one requiring the well-formed
identity, so a partial read is retried within the bounded timeout instead
of returned. Clarify the doc-comment that the retry loop — not
FileShare.ReadWrite — is the load-bearing defence.
@ppXD
ppXD merged commit dee160b into main Jun 15, 2026
14 checks passed
@ppXD
ppXD deleted the fix/windows-upgrade-wrapper-marker-race branch June 15, 2026 05:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant