Skip to content

Compile source snapshot: an empty cached answer can no longer win the race (#612) - #892

Merged
rbuergi merged 2 commits into
mainfrom
fix/612-empty-source-snapshot-race
Aug 7, 2026
Merged

rbuergi merged 2 commits into
mainfrom
fix/612-empty-source-snapshot-race

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #612 (sub-case b) — the CI wedge where a first compile ends Error under load and no re-trigger ever recovers, taking out whole shards (runs 30004790036, 31153582625).

Mechanism: MeshNodeCompilationService.SnapshotSources raced the authoritative DirectSourceProbe against the cached synced source query with Merge(...).FirstAsync(). The cached query replays its latest set synchronously on subscribe, while the probe cannot answer before its ~1 s chunk quiet window — so a cached query that had latched empty (a missed source-create update under CI load) always won the race. The compile consumed zero sources → deterministic CS0103 → Status=Error + park — and every retry, including the un-parking RequestedReleaseAt re-trigger, re-ran the same race and re-failed identically. "Sub-case b: persistent clobber / dead subscription" was actually a persistent deterministic re-failure; the watcher, trigger chain, and mirror are all healthy (verified in both CI dumps: triggers stamped, fresh compile activities minted, Matched Code nodes (0) in the compile log).

Changes

Tests

  • SourceSnapshot_StaleEmptyCachedAnswer_MustNotOutraceDirectProbe — deterministic pin of the race (fails on old semantics).
  • SourceSnapshot_ProbeSilentAndCachedEmpty_SettlesEmptyConsensus / SourceSnapshot_HealthyCachedNonEmpty_WinsWithoutWaitingForProbe — companion contracts.
  • FirstCompileError_LatchedEmptySourceQuery_FreshTriggerMustRecompileFromDirectRead — end-to-end repro of the exact CI wedge (fails on old semantics with the CI-identical terminal picture).

Verification

Fail-without/pass-with confirmed for both pins; CodeEditRecompileTest 11/11 and the compile-family suite 24/24 green; re-verified 11/11 after merging current main; dotnet build -c Release -warnaserror clean on MeshWeaver.Graph and Hosting.Monolith.Test.

Related follow-ups filed: #890 (distinct process-wide compile NRE), #891 (watchers die silently on own-stream OnError).

No What's New entry: internal reliability fix; the user-visible effect (compile no longer wedges) rides the bug fix.

🤖 Generated with Claude Code

rbuergi and others added 2 commits August 7, 2026 08:58
… race

Fixes #612 (sub-case b). The compile's point-in-time source snapshot raced
the authoritative DirectSourceProbe against the cached synced source query
with Merge(...).FirstAsync(). The cached query replays its latest set
SYNCHRONOUSLY on subscribe while the probe cannot answer before its ~1s
chunk quiet window — so a cached query that had latched EMPTY (a missed
source-create update under CI load) always won with an empty set. The
compile then consumed ZERO sources, the configuration lambda failed CS0103
deterministically, CompilationStatus=Error parked the type — and every
retry, including the un-parking RequestedReleaseAt re-trigger, re-ran the
same race and re-failed identically. That is the 'RE-TRIGGER: STILL STUCK
(sub-case b: persistent clobber / dead subscription)' CI wedge: not a dead
subscription — a persistent deterministic re-failure.

Fix: RaceSourceSnapshot combinator — first NON-EMPTY answer from either
leg wins immediately (healthy cache keeps zero probe latency, the #690
guard); EMPTY settles only by consensus of both legs, so source-less
configuration-only NodeTypes still compile; a never-emitting leg still
falls to the outer Timeout.

Also: non-Roslyn compile aborts now surface exception TYPE + STACK on the
activity log, the node's CompilationError names the exception class, and
the terminal LogWarning carries the exception object — the recurring CI
first-compile NRE (PR-884 run 31153582625) was undiagnosable because every
sink reduced it to .Message. The next occurrence will name its throw site.

Tests (CodeEditRecompileTest): deterministic pin of the race
(SourceSnapshot_StaleEmptyCachedAnswer_MustNotOutraceDirectProbe), empty-
consensus + healthy-cache contracts, and an end-to-end repro of the exact
CI wedge (FirstCompileError_LatchedEmptySourceQuery_FreshTriggerMustRecompileFromDirectRead)
— fail-without/pass-with verified; compile-family suite 24/24 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 7, 2026 07:00

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

This PR fixes a CI wedge in NodeType compilation where an empty cached synced-query source snapshot could always “win” a Merge(...).FirstAsync() race against the authoritative direct probe, leading to deterministic zero-source compiles, CompilationStatus=Error, and unrecoverable re-trigger loops under load.

Changes:

  • Replaces the Merge(...).FirstAsync() source snapshot race with a new RaceSourceSnapshot combinator that only allows non-empty results to win immediately; empty can only settle by consensus (both legs complete without sources).
  • Improves non-Roslyn compile failure diagnosability by recording exception type + stack in the activity log, including the exception type in CompilationError, and logging the exception object to preserve stacks.
  • Adds deterministic unit + end-to-end tests that pin the stale-empty-cache race and the exact “first compile error → re-trigger still stuck” CI wedge signature; upgrades WaitForLatestRelease diagnostics to distinguish dead mirror vs dead watcher vs deterministic re-failure.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
test/MeshWeaver.Hosting.Monolith.Test/CodeEditRecompileTest.cs Adds deterministic pins for the source-snapshot race and an end-to-end repro; enhances WaitForLatestRelease diagnostics with post-trigger probing.
src/MeshWeaver.Graph/Configuration/NodeTypeCompilationHelpers.cs Improves compile failure logging: activity log now includes exception type + stack for non-Roslyn aborts; node error summary includes exception type; logger receives exception object.
src/MeshWeaver.Graph/Configuration/MeshNodeCompilationService.cs Introduces RaceSourceSnapshot and updates SnapshotSources to prevent empty cached answers from outracing the direct probe.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

public static class EmptyRaceLayoutAreas
{
public static UiControl Overview(LayoutAreaHost host, RenderingContext _)
=> Controls.Html("<div id='marker'>EMPTYRACE_V1</div>");
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

367 tests  ±0   363 ✅ ±0   4m 53s ⏱️ -1s
 10 suites ±0     4 💤 ±0 
 10 files   ±0     0 ❌ ±0 

Results for commit 444218e. ± Comparison against base commit 2bb80fb.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

1 121 tests  ±0   1 114 ✅ ±0   5m 10s ⏱️ -23s
   11 suites ±0       7 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 444218e. ± Comparison against base commit 2bb80fb.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

1 538 tests  ±0   1 434 ✅ ±0   5m 42s ⏱️ + 1m 27s
   11 suites ±0     104 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 444218e. ± Comparison against base commit 2bb80fb.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

   10 files  ±0     10 suites  ±0   4m 41s ⏱️ -30s
1 487 tests ±0  1 487 ✅ ±0  0 💤 ±0  0 ❌ ±0 
1 575 runs  ±0  1 575 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit 444218e. ± Comparison against base commit 2bb80fb.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

1 547 tests  ±0   1 358 ✅ ±0   6m 57s ⏱️ +16s
   11 suites ±0     189 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 444218e. ± Comparison against base commit 2bb80fb.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

2 022 tests  +4   2 021 ✅ +4   7m 0s ⏱️ + 1m 30s
   11 suites ±0       1 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 444218e. ± Comparison against base commit 2bb80fb.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Test Results

   64 files  ±0     64 suites  ±0   34m 25s ⏱️ + 2m 18s
8 082 tests +4  7 777 ✅ +4  305 💤 ±0  0 ❌ ±0 
8 170 runs  +4  7 865 ✅ +4  305 💤 ±0  0 ❌ ±0 

Results for commit 444218e. ± Comparison against base commit 2bb80fb.

@rbuergi
rbuergi merged commit 0a01185 into main Aug 7, 2026
17 checks passed
@rbuergi
rbuergi deleted the fix/612-empty-source-snapshot-race branch August 19, 2026 10:32
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.

CI wedge: CodeEditRecompileTest StaleBarrierType first compile ends Error, re-trigger not picked up (sub-case b) — shard timeout exit=124

2 participants