Repository navigation
feat(membership): retain local health history - #10502
Merged
Merged
Conversation
ReubenBond
force-pushed
the
rb-health-status-history
branch
2 times, most recently
from
August 11, 2026 21:14
95146d1 to
c407cde
Compare
ReubenBond
force-pushed
the
rb-health-status-history
branch
from
August 11, 2026 23:32
c407cde to
13b9481
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
This PR enhances Orleans membership health monitoring by retaining a short history of local silo health events and by making time-dependent health/probing behavior testable and deterministic via injected (keyed) TimeProviders.
Changes:
- Add a typed, 1-minute local health event history and interval-based aggregation (with separate local vs network health categories).
- Serialize/limit on-demand health sampling cadence (targeting once-per-second) and use aggregated local-host degradation to dilate probe timeouts.
- Plumb the keyed membership
TimeProviderthrough membership/runtime components and add focused unit tests for the new behavior.
Show a summary per file
| File | Description |
|---|---|
| test/Orleans.Runtime.Tests/Diagnostics/WatchdogTests.cs | Adds tests validating that watchdog runtime checks emit typed local health events. |
| test/Orleans.Core.Tests/Membership/SiloHealthMonitorTests.cs | Updates mocks and adds coverage for probe-timeout dilation driven by local health status. |
| test/Orleans.Core.Tests/Membership/ProbeRequestMonitorTests.cs | Adds test coverage for ProbeRequestMonitor using injected time and reset semantics. |
| test/Orleans.Core.Tests/Membership/MembershipSystemTargetTests.cs | Adds tests covering indirect probe timing and local health interval queries. |
| test/Orleans.Core.Tests/Membership/MembershipAgentTests.cs | Updates to new ILocalSiloHealthMonitor API and new ClusterHealthMonitor ctor signature. |
| test/Orleans.Core.Tests/Membership/LocalSiloHealthMonitorTests.cs | Adds extensive tests for event retention, aggregation, concurrency, and sampling behavior. |
| test/Orleans.Core.Tests/Membership/ClusterHealthMonitorTests.cs | Updates to new ILocalSiloHealthMonitor API and adds time-provider-based stale-join eviction test. |
| src/Orleans.Runtime/Silo/Watchdog.cs | Records runtime stall/GC pause/component stall events into the local health event recorder; uses injected keyed TimeProvider. |
| src/Orleans.Runtime/Networking/ProbeRequestMonitor.cs | Switches to injected keyed TimeProvider and timestamp-based tracking. |
| src/Orleans.Runtime/MembershipService/SiloHealthMonitor.cs | Uses TimeProvider for timing/cancellation; dilates probe timeout based on aggregated local health status. |
| src/Orleans.Runtime/MembershipService/MembershipSystemTarget.cs | Measures probe timing using TimeProvider and queries aggregated local health over the probe interval. |
| src/Orleans.Runtime/MembershipService/LocalSiloHealthMonitor.cs | Introduces typed health events/status, retention window, on-demand caching cadence, and interval aggregation APIs. |
| src/Orleans.Runtime/MembershipService/ClusterHealthMonitor.cs | Moves membership processing and shutdown timing onto injected keyed TimeProvider. |
| src/Orleans.Runtime/Hosting/DefaultSiloServices.cs | Registers ILocalSiloHealthEventRecorder from the LocalSiloHealthMonitor singleton. |
| src/Orleans.Core/Configuration/Options/ClusterMembershipOptions.cs | Updates option documentation to reflect new sampling/logging behavior. |
Review details
Tip
Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 15/15 changed files
- Comments generated: 1
- Review effort level: Lite
Contributor
There was a problem hiding this comment.
Review details
Suppressed comments (2)
src/Orleans.Runtime/MembershipService/SiloHealthMonitor.cs:56
ElapsedSinceLastResponsereads_lastSuccessfulResponseTimestampas along?without synchronization.Nullable<long>is not an atomic read/write (it is larger than 64-bits), so this can tear whenProbeDirectly/ProbeIndirectlyupdate the field concurrently withProbingSiloHealthMonitorreading it, resulting in incorrect/negative elapsed values or sporadicHasValueflips. Consider storing a non-nullablelongtimestamp and reading it atomically viaVolatile.Read(the field is always initialized in the ctor anyway).
public TimeSpan? ElapsedSinceLastResponse
=> _lastSuccessfulResponseTimestamp is { } timestamp ? _timeProvider.GetElapsedTime(timestamp) : null;
src/Orleans.Runtime/MembershipService/LocalSiloHealthMonitor.cs:593
SubtractTimestampconvertsTimeSpanto a timestamp delta viaduration.TotalSeconds * TimestampFrequency. For very small durations (egTimeSpan.FromTicks(1)), thedoubleconversion can round down and produce a 0 delta, making lookback queries subtly wrong at sub-second granularity. Using integer arithmetic based onduration.Ticksavoids precision loss.
private long SubtractTimestamp(long timestamp, TimeSpan duration)
=> timestamp - (long)(duration.TotalSeconds * _timeProvider.TimestampFrequency);
- Files reviewed: 16/16 changed files
- Comments generated: 0 new
- Review effort level: Lite
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 63571f45-a79f-49f9-a50e-296c1355e312
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 63571f45-a79f-49f9-a50e-296c1355e312
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 63571f45-a79f-49f9-a50e-296c1355e312
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 63571f45-a79f-49f9-a50e-296c1355e312
ReubenBond
force-pushed
the
rb-health-status-history
branch
from
August 12, 2026 02:19
85c5aae to
d76a87a
Compare
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 63571f45-a79f-49f9-a50e-296c1355e312
Contributor
There was a problem hiding this comment.
Review details
Suppressed comments (2)
src/Orleans.Runtime/MembershipService/SiloHealthMonitor.cs:296
- ProbeDirectly calls roundTripTimer.GetElapsedTime(...) twice (once when formatting the OperationCanceledException message and again when computing roundTripTime). If time advances between those calls, the exception message and logged round-trip time can disagree, and it does extra timestamp reads.
catch (OperationCanceledException exception)
{
failureException = new OperationCanceledException(
$"The ping attempt was cancelled after {roundTripTimer.GetElapsedTime(out _)}. Ping #{id}",
exception);
}
catch (Exception exception)
{
failureException = exception;
}
var roundTripTime = roundTripTimer.GetElapsedTime(out _);
src/Orleans.Runtime/MembershipService/LocalSiloHealthMonitor.cs:52
- LocalSiloHealthStatus.Complaints currently includes any event with a non-null Complaint, even when the event has Score=0 (for example, informational events like GarbageCollectionPause). This can add noise to degradation logs since the complaint list can include non-degrading observations.
internal readonly record struct LocalSiloHealthStatus(int Score, ImmutableArray<LocalSiloHealthEvent> Events)
{
public ImmutableArray<string> Complaints
=> [.. Events.Where(static status => status.Complaint is not null).Select(static status => status.Complaint!)];
- Files reviewed: 18/18 changed files
- Comments generated: 0 new
- Review effort level: Lite
This was referenced Aug 28, 2026
This was referenced Aug 31, 2026
Merged
This was referenced Sep 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Local silo health checks currently run on demand, can overlap, and retain no history. This makes concurrent probe decisions repeat expensive checks and prevents callers from reasoning about recent host degradation.
This change records one minute of typed health events, serializes sampling to at most once per second, and aggregates the worst observation per check over a requested interval. Host-local and network-derived checks are classified separately so probe timeout dilation uses only recent local-host degradation.
Watchdog runtime, GC, component-check, and thread-pool stalls remain distinct events for query-time aggregation. Membership health components now use the keyed membership TimeProvider while preserving the connection-liveness checks already present on main.
Microsoft Reviewers: Open in CodeFlow