Skip to content

test(runtime): cover expired directory lease cleanup - #10960

Open
ReubenBond wants to merge 4 commits into
dotnet:mainfrom
ReubenBond:rb-10858-raise-runtime-coverage
Open

test(runtime): cover expired directory lease cleanup#10960
ReubenBond wants to merge 4 commits into
dotnet:mainfrom
ReubenBond:rb-10858-raise-runtime-coverage

Conversation

@ReubenBond

@ReubenBond ReubenBond commented Sep 2, 2026

Copy link
Copy Markdown
Member

Summary

GrainDirectoryPartition.CleanupExpiredLeasesCore was the highest-risk remaining directory hotspot at 0% line coverage, 0% branch coverage, complexity 20, and CRAP 420. Lease cleanup had no deterministic test which proved exact-boundary expiration, selective registration removal, or idempotence.

This change adds a three-silo fake-time scenario which creates two lease generations, invokes cleanup before expiration, at the first exact expiration, and again without advancing time. It asserts that cleanup retains active holds, prunes expired range and silo holds, removes only registrations on the expired silo, preserves later and live state, and produces the expected registration and lookup outcomes without sleeps or polling.

An internal IGrainDirectoryTestHooks operation provides an awaited scheduler barrier and six aggregate cleanup counts. It routes through the production queued entry point, exposes no private identities, and leaves periodic scheduling unchanged.

Focused coverage moves CleanupExpiredLeasesCore from 0% line / 0% branch / CRAP 420 to 91.5% line / 100% branch / CRAP 20.25. GrainDirectoryPartition.cs moves from 63.7% to 74.9% line coverage and from 62.7% to 74.5% branch coverage in the focused lease fixture.

Contributes to #10858.

Microsoft Reviewers: Open in CodeFlow

Copilot AI lite review requested due to automatic review settings September 2, 2026 06: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.

Copilot review overview

🟡 Changes recommended

A new fire-and-forget Task from periodic cleanup is discarded without .Ignore(), risking unobserved exceptions and deviating from established runtime patterns.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 Medium severity

New issues introduced by this change (1)
Severity Finding
Medium severity src/​Orleans.Runtime/​GrainDirectory/​DistributedGrainDirectory.csCleanupExpiredLeases() now returns a Task, but this periodic loop discards it using _ = ....…
What changed in this PR

Adds deterministic, fake-time coverage for GrainDirectoryPartition.CleanupExpiredLeasesCore by introducing a test hook which can trigger queued cleanup and return aggregate counts, enabling assertions around exact-boundary expiration, selective removal, and idempotence without sleeps/polling.

Changes:

  • Add a 3-silo fake-time test scenario which exercises cleanup before expiration, at exact expiration, and repeated cleanup without time advancing.
  • Extend IGrainDirectoryTestHooks with CleanupExpiredLeasesAsync() and introduce GrainDirectoryLeaseCleanupResult to return cleanup counts.
  • Update directory partition cleanup entry point to queue cleanup and capture counts, and adjust the periodic cleanup caller accordingly.
File Description
test/​Orleans.GrainDirectory.Tests/​GrainDirectory/​GrainDirectoryLeaseTests.cs Adds deterministic, fake-time test coverage for expired lease cleanup behavior and idempotence across partitions.
src/​Orleans.Runtime/​OrleansContracts.txt Updates generated contract declarations to include CleanupExpiredLeasesAsync on IGrainDirectoryTestHooks.
src/​Orleans.Runtime/​GrainDirectory/​IGrainDirectoryPartition.cs Adds CleanupExpiredLeasesAsync test hook method and the GrainDirectoryLeaseCleanupResult serialized return type.
src/​Orleans.Runtime/​GrainDirectory/​GrainDirectoryPartition.cs Implements queued cleanup result capture used by the new test hook while reusing CleanupExpiredLeasesCore.
src/​Orleans.Runtime/​GrainDirectory/​DistributedGrainDirectory.cs Adjusts periodic cleanup loop to call the new CleanupExpiredLeases() Task-returning method.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Orleans.Runtime/GrainDirectory/DistributedGrainDirectory.cs
Copilot AI review requested due to automatic review settings September 2, 2026 06:58
@ReubenBond

Copy link
Copy Markdown
Member Author

@copilot review

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.

Copilot review overview

🟡 Changes recommended

The production periodic cleanup path now allocates and computes a cleanup result which is discarded, introducing avoidable overhead in a runtime hot path.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (2)
Severity Finding
Medium severity src/​Orleans.Runtime/​GrainDirectory/​GrainDirectoryPartition.csCleanupExpiredLeases() is now used by the production periodic cleanup loop, but it always…
Low severity src/​Orleans.Runtime/​GrainDirectory/​DistributedGrainDirectory.cs — If CleanupExpiredLeases() is restored to a fire-and-forget void method to avoid per-tick result…
Issues resolved since last review (1)
Severity Finding
Medium severity src/​Orleans.Runtime/​GrainDirectory/​DistributedGrainDirectory.csCleanupExpiredLeases() now returns a Task, but this periodic loop discards it using _ = ....… View resolved comment

Comment thread src/Orleans.Runtime/GrainDirectory/GrainDirectoryPartition.cs Outdated
Comment thread src/Orleans.Runtime/GrainDirectory/DistributedGrainDirectory.cs Outdated
Copilot AI review requested due to automatic review settings September 2, 2026 07:46
@ReubenBond

Copy link
Copy Markdown
Member Author

@copilot review

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.

Copilot review overview

🔵 Needs a closer look

The new test currently relies on a magic-duration calculation and duplicated cluster setup which makes it brittle and harder to maintain as membership/lease settings evolve.

Review tier: Lite
Findings: None

Issues resolved since last review (2)
Severity Finding
Low severity src/​Orleans.Runtime/​GrainDirectory/​DistributedGrainDirectory.cs — If CleanupExpiredLeases() is restored to a fire-and-forget void method to avoid per-tick result… View resolved comment
Medium severity src/​Orleans.Runtime/​GrainDirectory/​GrainDirectoryPartition.csCleanupExpiredLeases() is now used by the production periodic cleanup loop, but it always… View resolved comment
Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

test/Orleans.GrainDirectory.Tests/GrainDirectory/GrainDirectoryLeaseTests.cs:568

  • The test hard-codes the effective dead-silo lease duration as directoryLeaseDuration - 1s, which is implicitly tied to the configured membership failure-detection timeout. This is brittle: if ClusterMembershipOptions defaults or the test’s configured probe settings change, the expected lease expiration will drift and the test will fail for non-behavioral reasons. Compute the expected lease duration using the same production helper used by DistributedGrainDirectory instead of subtracting a magic constant.

This issue also appears on line 761 of the same file.

test/Orleans.GrainDirectory.Tests/GrainDirectory/GrainDirectoryLeaseTests.cs:782

  • CreateCleanupCluster duplicates the configuration logic in CreateCluster (same distributed directory enablement, membership manager wiring, time provider registration, and membership options). This increases the chance these helpers diverge over time and makes failures harder to diagnose when one helper is updated but the other is not. Consider consolidating into a single helper which takes siloCount and rangeLeaseDuration parameters so all lease tests share one canonical cluster configuration.
    private static (InProcessTestCluster Cluster, FakeTimeProvider TimeProvider) CreateCleanupCluster(
        TimeSpan rangeLeaseDuration)
    {
        var timeProvider = new FakeTimeProvider(InitialTime);
        var builder = new InProcessTestClusterBuilder(3);
#pragma warning disable ORLEANSEXP003
        builder.Options.UseDistributedGrainDirectory = true;
#pragma warning restore ORLEANSEXP003
        builder.ConfigureSilo((_, siloBuilder) =>
        {
            siloBuilder.Services.AddSingleton<MembershipTableManager>();
            siloBuilder.Services.AddSingleton<IMembershipManager, LeaseTestMembershipManager>();
            siloBuilder.Services.AddSingleton<TimeProvider>(timeProvider);
            siloBuilder.Services.AddKeyedSingleton(TimeProviderNames.Membership, TimeProvider.System);
            siloBuilder.Services.Configure<ClusterMembershipOptions>(options =>
            {
                options.ProbeTimeout = TimeSpan.FromSeconds(1);
                options.MaxProbeTimeout = TimeSpan.FromSeconds(1);
                options.NumMissedProbesLimit = 1;
            });
            siloBuilder.Services.PostConfigure<GrainDirectoryOptions>(options => options.RangeLeaseDuration = rangeLeaseDuration);
        });

Copilot AI review requested due to automatic review settings September 2, 2026 08:24
@ReubenBond

Copy link
Copy Markdown
Member Author

@copilot review

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.

Copilot review overview

🟢 Approval recommended

The changes are additive and test-focused, with the runtime impact isolated to an internal test hook and no verified correctness, compatibility, or operational issues found in the updated code paths.

Review tier: Lite
Findings: None

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