Skip to content

feat(membership): retain local death knowledge for absent silos - #11589

Merged
ReubenBond merged 4 commits into
dotnet:mainfrom
ReubenBond:rb-membership-retirement-watermark
Oct 27, 2026
Merged

ReubenBond merged 4 commits into
dotnet:mainfrom
ReubenBond:rb-membership-retirement-watermark

Conversation

@ReubenBond

@ReubenBond ReubenBond commented Oct 26, 2026 •

Copy link
Copy Markdown
Member

This follow-up builds on the canonical membership-view fixes merged in #11588 and is rebased onto main. The original missing-silo diagnosis was reported by @nightBaker in #11581.

Problem

Once membership cleanup prunes a dead silo's row, ordinary status lookups can return None and lose the evidence of death. Delayed client-route updates can then retain or reintroduce stale routes, and durable-job adoption can treat a pruned owner as a graceful release and reset its adoption count.

Solution

Keep canonical membership rows authoritative and retain local status knowledge separately: present rows supply their actual status; absent identities use a fixed generation cutoff, compact recent-death records, and existing successor/version-aware evidence.

ClusterMembershipOptions.SiloGenerationRetirementAge is a nullable TimeSpan, defaulting to one hour. Each peer establishes its cutoff from its local UTC clock when publishing a view. The cutoff advances monotonically, and fixed snapshots keep fixed status answers. null disables age-based retirement, negative values are configuration errors, and zero is valid. Present long-running members retain their actual status regardless of generation age.

Observed deaths remain individually recorded until the cutoff covers their generations. With retirement enabled and normal timestamp-derived generations, history follows the recent generation window rather than cluster lifetime; it is a time-window bound, not a byte cap. With retirement disabled, observed death records remain for the observer's lifetime. A new observer can use versioned prior-registration evidence or existing fresh validation to resolve recent absent identities before the cutoff.

Publish same-version status-knowledge updates, refresh client routing on those snapshots, remove retired peers even after their rows are gone, and use the recorded membership version for durable-job adoption accounting. Local pre-registration Created status stays separate from canonical rows. Storage providers and persisted membership schemas remain unchanged.

Deferred strengthening

  • TODO: Establish the retirement floor centrally through membership and fence stale admissions using the membership version/ETag. The selected local-cutoff design accepts the extreme case where a registration write stalls beyond the configured age and later commits after an observer has classified the absent identity as dead. The committed row then supplies its actual status. The implementation includes the corresponding TODO at the publication boundary.
Microsoft Reviewers: Open in CodeFlow

Copilot AI balanced review requested due to automatic review settings October 26, 2026 20:59

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.

🟡 Changes recommended

Zero-age startup, retained successor evidence, and older snapshot deserialization have unresolved correctness issues.

3 open issues
What changed in this PR

This PR builds on #11588’s canonical membership views to retain death knowledge after silo rows are pruned, supporting reliable client routing and durable-job adoption.

Changes:

  • Adds configurable generation retirement and retained death records without changing persisted membership schemas.
  • Publishes same-version status updates and incorporates them into routing and adoption decisions.
  • Adds regression tests, configuration validation, API metadata, and documentation.

Validation was limited to source inspection; test and coverage results were not verified.

File Description
test/​Orleans.Runtime.Internal.Tests/​ClusterMembershipSnapshotTests.cs Tests status classification, pruning, and serialization.
test/​Orleans.DurableJobs.Tests/​DurableJobs/​JournaledJobShardRetirementTests.cs Tests adoption accounting after owner pruning.
test/​Orleans.DurableJobs.Tests/​DurableJobs/​JournaledJobShardManagerTests.cs Adds membership removal support to the test helper.
test/​Orleans.Core.Tests/​Membership/​MembershipTableSnapshotTests.cs Tests death retention, merging, and cutoff boundaries.
test/​Orleans.Core.Tests/​Membership/​MembershipTableManagerTests.cs Tests canonical startup views and delayed registration reconciliation.
test/​Orleans.Core.Tests/​Membership/​MembershipRetirementTests.cs Tests retirement publication with controlled clocks.
test/​Orleans.Core.Tests/​Membership/​ClusterMembershipOptionsTests.cs Tests retirement defaults and validation.
test/​Orleans.Core.Tests/​Directory/​MockClusterMembershipService.cs Supports same-version snapshot publication in tests.
test/​Orleans.Core.Tests/​Directory/​ClientDirectoryTests.cs Tests stale-route removal and delayed-update rejection.
src/​Orleans.Runtime/​MembershipService/​SiloStatusOracle.cs Returns local lifecycle status separately from membership rows.
src/​Orleans.Runtime/​MembershipService/​MembershipTableSnapshotExtensions.cs Carries retirement knowledge into cluster snapshots.
src/​Orleans.Runtime/​MembershipService/​MembershipTableManager.cs Applies retirement during publication and reconciles local status.
src/​Orleans.Runtime/​MembershipService/​IClusterMembershipService.cs Documents same-version membership updates.
src/​Orleans.Runtime/​MembershipService/​ClusterMembershipSnapshot.cs Adds retained death knowledge and successor checks.
src/​Orleans.Runtime/​MembershipService/​ClusterMembershipService.cs Publishes and exposes same-version status changes.
src/​Orleans.Runtime/​GrainDirectory/​ClientDirectory.cs Refreshes routes using snapshot identity and retained status.
src/​Orleans.Runtime/​Configuration/​Validators/​SiloClusteringValidator.cs Rejects negative retirement ages.
src/​Orleans.DurableJobs/​JournaledJobShardManager.cs Uses recorded membership versions when classifying previous owners.
src/​Orleans.Core/​Runtime/​MembershipTableSnapshot.cs Retains and compacts death evidence separately from canonical rows.
src/​Orleans.Core/​Configuration/​Options/​ClusterMembershipOptions.cs Adds the nullable retirement-age option.
src/​Orleans.Core.Abstractions/​IDs/​SiloAddress.cs Calculates generation retirement floors.
src/​api/​Orleans.Core/​Orleans.Core.cs Exposes the new option in the generated API surface.
docs/​site/​src/​content/​docs/​implementation/​cluster-management.md Explains retirement behavior, retention, and operational limits.

Review effort: Balanced

Comment thread src/Orleans.Runtime/MembershipService/ClusterMembershipSnapshot.cs Outdated
Comment thread src/Orleans.Runtime/MembershipService/MembershipTableManager.cs Outdated
Comment thread src/Orleans.Core/Runtime/MembershipTableSnapshot.cs
Keep canonical membership rows separate from a local publication-time generation cutoff and recent death records. Default retirement age to one hour with a nullable opt-out, and preserve version-aware and fresh-validation death evidence. Wire same-version client-route invalidation and durable-job adoption accounting. Document the accepted prolonged stalled-write race and the deferred central membership admission fence.
Disable generation retirement in health-monitor and membership-agent fixtures whose manually assigned generations are unrelated to their clocks. Dedicated retirement tests continue to exercise the enabled cutoff.
@ReubenBond
ReubenBond force-pushed the rb-membership-retirement-watermark branch from d4bf214 to 964e320 Compare October 27, 2026 15:21
Copilot AI balanced review requested due to automatic review settings October 27, 2026 15:21

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.

🟡 Changes recommended

Older gossip payloads can fail to deserialize safely, and pruning a successor can still erase death knowledge.

5 open issues

Review effort: Balanced

Comment thread src/Orleans.Core/Runtime/MembershipTableSnapshot.cs Outdated
Comment thread src/Orleans.Core/Runtime/MembershipTableSnapshot.cs
Keep an unregistered local silo Created when only the generation cutoff implies death, including zero-age retirement. Preserve explicit death and delayed registration/removal evidence even after compaction, and reject equal-version omissions as local death evidence. Scan retained successor identities so pruning cannot revive predecessor routes. Isolate synthetic-generation directory contract fixtures from age-based retirement and cover the transitions with deterministic regressions.
Copilot AI balanced review requested due to automatic review settings October 27, 2026 17:33

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

Cross-component death inference and terminal lifecycle behavior warrant maintainer review, particularly with admission fencing explicitly deferred.

No open issues 🎉

5 resolved since last review

Review effort: Balanced

Expose IReadOnlySet instead of ImmutableHashSet, build updates in a private HashSet, and freeze published history to preserve snapshot ownership. Share unchanged frozen histories across updates and cluster projections. Task-scoped lookup measurements favor FrozenSet over ImmutableHashSet, with HashSet offering similar lookups but no physical immutability. Cover mutable inputs, set reuse, compaction, and interface-typed serialization on both target frameworks.
Copilot AI balanced review requested due to automatic review settings October 27, 2026 18:02
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Code coverage

Metric Pull request Current main Variance
Lines 84.00% (120,571 / 143,545) 84.02% (120,516 / 143,444) -0.0208 pp
Branches 73.84% (36,132 / 48,934) 73.83% (36,070 / 48,854) +0.0060 pp

Report-only conclusion: mixed.

The current-main baseline is commit ad8435d3fe and uses the same reviewed coverage matrix.

Coverage combines every CI test matrix job, including providers, CodeGen, .NET 8/10, Linux, Windows, and macOS, using canonical physical source and branch identities.

The comparison remains report-only while normal line and branch variance is calibrated.

Coverage details

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 cross-layer changes to death classification, terminal lifecycle decisions, routing, and job ownership warrant final human validation of their distributed guarantees.

No open issues 🎉

Review effort: Balanced

@ReubenBond
ReubenBond merged commit 1643d2d into dotnet:main Oct 27, 2026
95 checks passed
@ReubenBond
ReubenBond deleted the rb-membership-retirement-watermark branch October 27, 2026 19:38
ReubenBond added a commit to ReubenBond/orleans that referenced this pull request Oct 27, 2026
ReubenBond added a commit to ReubenBond/orleans that referenced this pull request Oct 27, 2026
ReubenBond added a commit that referenced this pull request Oct 27, 2026
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.

2 participants