Repository navigation
fix(compiler): never alias a read to a same-MVID load context that can no longer load its build (#5555) - #6261
Conversation
…n no longer load its build FindLiveContextOfSameBuild matched contexts by MVID alone. The MVID is read once from the file header and outlives the file, so a context over an evicted store name (v14766-…) that never loaded its assembly was handed to a read of the present copy (v18979-…) and every load answered "No file at" the deleted path. Each recompile re-adopted identical bytes under a new node version and was aliased back to the same dead context: a permanent self-heal/recompile loop. On memex.systemorph.com 2026-10-07 Store/Plugin instances failed 604 times in 25 minutes on one pod, the type node was written ~6,900 times in 15 hours, and the replica sat at an 11+ GiB heap with GC above half of wall time (#5555, #6161). NodeAssemblyLoadContext.CanLoadItsBuild (assembly loaded, or its file present) now gates the alias. ScanPinSupersessionTest.AReadIsNeverAliasedToASameBuildContextThatCanNoLongerLoadIt is red on the old resolver; its control keeps the in-memory reuse. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The new filesystem existence check retains a TOCTOU race with concurrent retention eviction.
1 open finding
What changed in this PR
Prevents compiler-cache aliasing to assembly contexts whose backing files were evicted.
Changes:
- Adds a loadability guard to same-MVID reuse.
- Adds regression and loaded-assembly control tests.
- Documents the incident and cache invariant.
| File | Description |
|---|---|
CompilationCacheService.cs |
Gates same-build context aliasing. |
ScanPinSupersessionTest.cs |
Tests evicted and loaded contexts. |
NodeTypeCompilation.md |
Documents the failure and fix. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
… — an existence check keeps the deletion race Review on #6261: File.Exists then return leaves a TOCTOU window against the retention sweep. TryMakeResident loads the candidate's assembly (the durable guarantee) and skips it when the load loses the race (file gone, context retired). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Test Results (shard 3) 4 files ±0 4 suites ±0 6m 52s ⏱️ +14s Results for commit ea1ef08. ± Comparison against base commit 051dfd6. This pull request removes 1 and adds 1 tests. Note that renamed tests count towards both. |
Test Results (shard 4) 4 files ±0 4 suites ±0 16m 28s ⏱️ -29s Results for commit ea1ef08. ± Comparison against base commit 051dfd6. This pull request removes 39 and adds 17 tests. Note that renamed tests count towards both. |
Test Results 22 files ±0 22 suites ±0 47m 51s ⏱️ -17s Results for commit ea1ef08. ± Comparison against base commit 051dfd6. This pull request removes 40 and adds 18 tests. Note that renamed tests count towards both. |

What is growing
On memex.systemorph.com a NodeType compile/bind loop drives the heap, not the document-part seal (Plugins#2979 is deployed and did its part).
Ok → Ok → Pending. The dispatched adoption lands the same bytes (MVID1d9f4d8e…, hash1db01ff9e178) under a newv{version}store name.Ops/Actions/logs-oom-storeplugin-bind-20261007): 604× in 25 min on one pod:EnrichWithNodeType: the recorded build of Store/Plugin (… v18979 …) still does not load in this process … Failed to load assembly at /data/assembly-cache/Store_Plugin/v18979-c003e001-1db01ff9e178.dll — No file at '/data/assembly-cache/Store_Plugin/v14766-c003e001-1db01ff9e178.dll'. Every plugin root (AppleMaps, RemoteControl, Crm, …) logsOverlay self-heal … stuck … AGAIN after 13 self-heal(s).65f6466887-5cx77shows a flat 11.3–11.6 GiB with GC at 56% of wall time from 09:31 to 10:03Z, which is inside this loop window.Root cause
CompilationCacheService.FindLiveContextOfSameBuildaliases a read to a live context with the same MVID.BuildMvidis read once from the file header and outlives the file. A context created overv14766-…that never loaded its assembly was therefore handed to a read of the presentv18979-…, and its load fails on the evicted path. Each self-heal recompile re-adopts identical bytes under a new version and is aliased back to the same dead context. The loop never ends.Fix
NodeAssemblyLoadContext.CanLoadItsBuildreturns true when the assembly is loaded or its file still exists. It now gates the alias. In-memory reuse is unchanged.Tests
ScanPinSupersessionTest.AReadIsNeverAliasedToASameBuildContextThatCanNoLongerLoadItis red on the old resolver. It fails atNotBeSameAs, a deterministic negative control.ASameBuildContextThatHasLoadedItsAssemblyIsStillReusedAfterItsFileIsGonekeeps the loaded-generation reuse.ScanPinSupersessionTestpassed 11/11 andAssemblyLoadContextLeakTestpassed 6/6.MeshWeaver.Compiler.Pipelineand its test project build clean with-c Release -warnaserror.Not established
v14766.Deploy / recycle
After the image carrying this rolls, every pod starts with a fresh context cache. No recycle is needed for the fix to take effect. Name to watch:
Store/Pluginnode version should stop climbing.Pairs-with: none — no public surface removed (one public property added on a sealed internal-use class).
Companion: Systemorph/MeshWeaver.Plugins — recycle-on-new-build keys on the bytes (MVID), not the version-bearing path.
Refs #5555 #6161 #6163 #6152
🤖 Generated with Claude Code