Skip to content

Orleans test host: corrupt/evicted DynamicNode assembly poisons every TestCluster.DeployAsync (TypeLoadException 'format is invalid') — whole-project cascade #618

Closed

Description

@rbuergi

Non-deterministic, process-global flake in MeshWeaver.Hosting.Orleans.Test — independent of any single PR (observed on PR #607 run 30028943225 shard 2, but the branch is 132/132 green locally; latent in main).

Symptom

After ~3 min of green, a wall of failures (22 on that shard) all throwing the identical exception, all sharing one dynamic-assembly GUID 5af7f5d3886840588e9b1bb520f0481b, all at silo host build (before any grain activation / stream / classifier):

System.TypeLoadException: Could not load type
'MeshWeaver.Graph.Generated.type_OrleansInstanceType5af7f5d3…MeshNodeProviderAttribute'
from assembly 'DynamicNode_type_OrleansInstanceType5af7f5d3…' because the format is invalid.
  at System.Reflection.RuntimeAssembly.IsDefined(...)
  at Orleans.Serialization.Internal.ReferencedAssemblyProvider.AddAssembly / GetRelevantAssemblies
  at Orleans.Serialization.ServiceCollectionExtensions.AddSerializer
  at Orleans.Hosting.DefaultSiloServices.AddDefaultServices
  … OrleansTestBase.InitializeAsync → TestCluster.DeployAsync

Mechanism

One earlier test compiles+loads a dynamically-generated NodeType assembly (DynamicNode_…) whose on-disk image is corrupt/truncated or whose collectible ALC is later evicted, leaving a dangling Assembly reference. Orleans' default AddSerializer enumerates all loaded assemblies and calls assembly.IsDefined(...) at every DeployAsync — so from that instant, one poisoned assembly fails every subsequent silo build in the process. Process-global assembly-load pollution → whole-project cascade, not a per-test logic bug.

Root-cause candidates

Fix direction

A corrupt/evicted dynamically-compiled NodeType assembly must never remain enumerable by Orleans' ReferencedAssemblyProvider — either guarantee the emitted image is complete before load (fail the compile, don't load a truncated dll) or ensure ALC eviction removes the Assembly from the domain's enumerable set before Orleans re-enumerates.

Not blocking

The branch under test is sound (132/132 green locally). This is a shared test-host hazard; a fresh CI run is expected green.

🤖 Generated with Claude Code

Activity

  1. added
    ci-failureA main-branch CD run failed - no deployable image was published
    on Jul 23, 2026
  2. github-actions commented on Jul 24, 2026

    @github-actions
    Contributor

    CD failed on main: run 30071266594 for commit 414eca9adeb3006f1dcbc983808cdbdaa2d2ef21.

    No deployable image was published for this merge - every self-updating install stays on the PREVIOUS image until a CD run goes green. Triage the failed job in the linked run, fix (or re-run if infrastructure), and close this issue once CD is green.

  3. rbuergi commented on Jul 24, 2026

    @rbuergi
    ContributorAuthor

    Related occurrence (same family, different surface), main run 30072157879 (f462c60): exactly the two dynamic-compile tests (CompiledArtifact_IsVisibleFromBothSilos, ColdStart_CompileViaGetCompilationPathRequest_Succeeds) failed fast with Success=false carrying a bare "Object reference not set to an instance of an object." (NRE message, no stack — NodeTypeContractHandler.Fail(null, ex.Message) drops the stack, a diagnosability gap worth fixing) while all 132 other Orleans tests passed. Not the literal TypeLoadException signature, but the same process-global compile-path poisoning pattern. Both tests green individually and 134/134 project-green locally on the same sha.

    Additional latent lead found during triage: the OWN-node stream reduces through the pathless MeshNodeReference() legacy branch of ReduceToMeshNode (src/MeshWeaver.Graph/MeshDataSource.cs:1475+) where the sibling-satellite fix (pinned by MeshNodeReducePatchTest) was only applied to path-ful references — satellite-only patches during compile can still surface a sibling as the own-stream value.

    🤖 Generated with Claude Code

  4. github-actions commented on Jul 28, 2026

    @github-actions
    Contributor

    CD failed on main: run 30404078323 for commit abfbe1383c0095973cd81b3f376d02c72acfd2c6.

    No deployable image was published for this merge - every self-updating install stays on the PREVIOUS image until a CD run goes green. Triage the failed job in the linked run, fix (or re-run if infrastructure), and close this issue once CD is green.

  5. github-actions commented on Aug 5, 2026

    @github-actions
    Contributor

    CD failed on main: run 31012108566 for commit 885225a021064528a819952fb941af06fd43cbca.

    No deployable image was published for this merge - every self-updating install stays on the PREVIOUS image until a CD run goes green. Triage the failed job in the linked run, fix (or re-run if infrastructure), and close this issue once CD is green.

  6. github-actions commented on Aug 7, 2026

    @github-actions
    Contributor

    CD failed on main: run 31132935227 for commit 75571eeaf0dda1116feb59ce822c91c4ae5eee8f.

    No deployable image was published for this merge - every self-updating install stays on the PREVIOUS image until a CD run goes green. Triage the failed job in the linked run, fix (or re-run if infrastructure), and close this issue once CD is green.

  7. github-actions commented on Aug 7, 2026

    @github-actions
    Contributor

    CD failed on main: run 31138803129 for commit 22fab9c8bb9a21f71ac4b4d87c0c93285c838b29.

    No deployable image was published for this merge - every self-updating install stays on the PREVIOUS image until a CD run goes green. Triage the failed job in the linked run, fix (or re-run if infrastructure), and close this issue once CD is green.

  8. github-actions commented on Aug 7, 2026

    @github-actions
    Contributor

    CD failed on main: run 31139761395 for commit 87f439200da85dcb9e3015e1495253353bc9e257.

    No deployable image was published for this merge - every self-updating install stays on the PREVIOUS image until a CD run goes green. Triage the failed job in the linked run, fix (or re-run if infrastructure), and close this issue once CD is green.

  9. github-actions commented on Aug 7, 2026

    @github-actions
    Contributor

    CD failed on main: run 31142331917 for commit 778ab30d0549a0f3e0b23ed24a448d7d1e09915a.

    No deployable image was published for this merge - every self-updating install stays on the PREVIOUS image until a CD run goes green. Triage the failed job in the linked run, fix (or re-run if infrastructure), and close this issue once CD is green.

  10. github-actions commented on Aug 7, 2026

    @github-actions
    Contributor

    CD failed on main: run 31143782207 for commit fb70724b4bc350842b4d99fd58c039b901a12cd1.

    No deployable image was published for this merge - every self-updating install stays on the PREVIOUS image until a CD run goes green. Triage the failed job in the linked run, fix (or re-run if infrastructure), and close this issue once CD is green.

  11. github-actions commented on Aug 7, 2026

    @github-actions
    Contributor

    CD failed on main: run 31147515018 for commit 142e24c06d6751b072cfce1c2c989574f69bef85.

    No deployable image was published for this merge - every self-updating install stays on the PREVIOUS image until a CD run goes green. Triage the failed job in the linked run, fix (or re-run if infrastructure), and close this issue once CD is green.

  12. rbuergi commented on Aug 8, 2026

    @rbuergi
    ContributorAuthor

    Shard 0 (the Orleans shard) produced two more unrelated-PR failures today, on diffs that cannot reach the Orleans host. Recording here because this issue already owns Orleans test-host instability.

    Two different Orleans tests, two unrelated diffs, both shard 0, both passing on re-run or locally. That is the shape this issue describes (a poisoned/unstable TestCluster affecting whatever runs in it) rather than two independent product races.

    For contrast, today's other two recurring flakes were NOT this shape and each got its own issue: #945 (CrossHubPatchAtomicityTest, shard 4, twice) and #946 (ObservableQueryIntegrationTests.ScopeDescendants, shard 1, four times — root-caused as a test asserting an exact emission count on a change feed, fixed in PR #955).

    So of the four cross-PR flake sources seen today, one is fixed, one is analysed and open, and these two Orleans ones look like this issue. Worth checking whether shard 0's TestCluster lifecycle is the common factor.

  13. rbuergi commented on Aug 8, 2026

    @rbuergi
    ContributorAuthor

    The #613 teardown-ordering fix (PR #967) does not fix this — and here is what this actually needs

    Investigated while fixing #613 (node ALCs unloading before pooled work is drained). The two look like one bug — both are "a collectible node assembly touched after it should be gone" — but they are not, and the distinction points at the fix.

    Why #967 doesn't cover it:

    1. It only changes when the unload runs during mesh teardown. The live-mesh path is untouched, and so is MessageHubGrain.OnDeactivateAsync's own loadContext.Unload() — which already carries a documented KNOWN GAP for exactly this situation.
    2. The mechanism here is enumerability after unload, which no ordering change can remove. AssemblyLoadContext.Unload() is asynchronous by contract: the Assembly stays reachable from AppDomain.CurrentDomain.GetAssemblies() until the LoaderAllocator is actually collected. Orleans' ReferencedAssemblyProvider enumerates every loaded assembly at silo startup and calls RuntimeAssembly.IsDefined on each — which is what throws Could not load type '…MeshNodeProviderAttribute' … because the format is invalid. Unloading earlier or later does not change that the assembly is still enumerable in that window.
    3. NodeAssemblyLoadContext.Pin() already has a drain added for this exact TypeLoadException … format is invalid string — but the pin only protects paths that take a pin. Orleans' assembly scan takes no pin, so it walks straight past that protection.

    So the fix #618 needs is coverage of that uncovered scan — either keeping the half-unloaded assembly out of the enumeration Orleans performs, or ensuring no silo starts while such an assembly is enumerable. That is a different change from the teardown ordering in #967, and should be tracked here rather than folded into it.

    Observed impact, for prioritisation: this reddened PR #950's shard 0 with ten Orleans tests failing together, all from one TypeLoadException at TestCluster startup. It re-greened on the next run after a merge with main, so it presents as a flake — but the ten names are a single incident, not ten independent failures. Anyone tallying flake frequency should count it once.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    ci-failureA main-branch CD run failed - no deployable image was published

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions