Skip to content

perf(runtime): lazily allocate activation request tracking - #10115

Open
ReubenBond wants to merge 4 commits into
dotnet:mainfrom
ReubenBond:rebond/lazy-activation-request-tracking
Open

perf(runtime): lazily allocate activation request tracking#10115
ReubenBond wants to merge 4 commits into
dotnet:mainfrom
ReubenBond:rebond/lazy-activation-request-tracking

Conversation

@ReubenBond

@ReubenBond ReubenBond commented May 17, 2026

Copy link
Copy Markdown
Member

Summary

  • Lazily allocate activation waiting/running request collections instead of creating them for every activation.
  • Return empty request tracking collections for reuse.
  • Preserve null-safe inactivity and request-count semantics for activations with no queued or running requests.

Validation

  • dotnet build src\Orleans.Runtime\Orleans.Runtime.csproj --nologo --verbosity minimal -property:UseSharedCompilation=false
  • dotnet build test\Grains\TestVersionGrains\TestVersionGrains.csproj --nologo --verbosity minimal -property:UseSharedCompilation=false
  • dotnet build test\Grains\TestVersionGrains2\TestVersionGrains2.csproj --nologo --verbosity minimal -property:UseSharedCompilation=false
  • dotnet test test\Orleans.Runtime.Tests\Orleans.Runtime.Tests.csproj --no-build --nologo --verbosity minimal -property:UseSharedCompilation=false
Microsoft Reviewers: Open in CodeFlow

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.

Pull request overview

This PR reduces per-activation allocations in the Orleans runtime by lazily creating (and reusing) the collections used to track queued and running requests for an activation, while keeping existing “no requests” semantics intact.

Changes:

  • Lazily allocate _waitingRequests and _runningRequests in ActivationData and update related logic to be null-safe.
  • Introduce shared pools for the request-tracking collections and return them for reuse when they become empty.
Show a summary per file
File Description
src/Orleans.Runtime/Catalog/GrainTypeSharedContext.cs Adds shared pools plus rent/return helpers for request-tracking collections.
src/Orleans.Runtime/Catalog/ActivationData.cs Switches request tracking to lazy allocation, adds return-to-pool logic, and updates null-safe request-count/inactivity checks.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 1

Comment thread src/Orleans.Runtime/Catalog/GrainTypeSharedContext.cs Outdated
@ReubenBond ReubenBond changed the title Lazily allocate activation request tracking perf(runtime): lazily allocate activation request tracking May 29, 2026
Copilot AI review requested due to automatic review settings August 18, 2026 09:41
@ReubenBond
ReubenBond force-pushed the rebond/lazy-activation-request-tracking branch from 2119bab to 932b619 Compare August 18, 2026 09:41

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.

Review details

Suppressed comments (1)

src/Orleans.Runtime/Catalog/GrainTypeSharedContext.cs:26

  • The new request-tracking pools are unbounded (ConcurrentStack with no size cap) and will retain the backing arrays of any large List/Dictionary instances which get returned. In a long-running silo, a transient spike in queued/running requests could permanently increase steady-state memory usage (large-capacity collections stay rooted by the pool even after load subsides). Consider bounding the pool size and/or avoiding pooling large-capacity collections (e.g., discard or trim when above a threshold) to prevent memory bloat.
    private static readonly ConcurrentStack<Dictionary<Message, CoarseStopwatch>> RunningRequestsMapPool = new();
    private static readonly ConcurrentStack<List<(Message Message, CoarseStopwatch QueuedTime)>> WaitingRequestsListPool = new();
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings August 21, 2026 21:14
@ReubenBond
ReubenBond force-pushed the rebond/lazy-activation-request-tracking branch from 932b619 to fe6d45c Compare August 21, 2026 21:14

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.

Review details

Suppressed comments (1)

src/Orleans.Runtime/Catalog/ActivationData.cs:505

  • GetRequestCount already holds this, but calling WaitingCount re-enters the monitor on every overload check and activation-metrics read. Since the waiting count can be read safely under this existing lock, use _waitingRequests?.Count ?? 0 directly here to avoid adding a synchronization operation to this hot path.
            return (_runningRequests?.Count ?? 0) + WaitingCount;
  • Files reviewed: 1/1 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@ReubenBond
ReubenBond marked this pull request as ready for review August 21, 2026 22:30
Copilot AI review requested due to automatic review settings August 23, 2026 15:51
@ReubenBond
ReubenBond force-pushed the rebond/lazy-activation-request-tracking branch from fe6d45c to 3849915 Compare August 23, 2026 15:51

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.

Review details

  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@github-actions

Copy link
Copy Markdown
Contributor

Code coverage

52.17% line coverage - 52,895 / 101,389 lines

Coverage details

Copilot AI review requested due to automatic review settings August 28, 2026 17:26
@ReubenBond
ReubenBond force-pushed the rebond/lazy-activation-request-tracking branch from 3849915 to aedf82f Compare August 28, 2026 17:26
ReubenBond and others added 3 commits August 28, 2026 10:26
Rent waiting and running request collections only while an activation uses them. Bound pooled capacity and synchronize state observations so collection reuse preserves activation ownership and lifecycle invariants.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use one pooled tracker lease per activation, return it only after waiting and running requests are empty, and collapse stateless-worker status reads to one lock acquisition. Add deterministic transition, cancellation, deactivation, reuse, double-return, and concurrent-reader coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

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

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

New issues introduced by this change (2)
Severity Finding
Medium severity src/​Orleans.Runtime/​Catalog/​StatelessWorkerGrainContext.cs — GetRequestStatus() acquires the ActivationData lock for every worker inspected. In…
Low severity src/​Orleans.Runtime/​Catalog/​ActivationData.cs — These public request-status properties now unconditionally take the activation lock. They are…

Comment on lines +268 to 271
var candidate = _workers[i];
var status = candidate.GetRequestStatus();
if (status.IsInactive)
{
Comment thread src/Orleans.Runtime/Catalog/ActivationData.cs Outdated
Copilot AI review requested due to automatic review settings August 28, 2026 18:18
@ReubenBond
ReubenBond force-pushed the rebond/lazy-activation-request-tracking branch from aedf82f to 9c6fbca Compare August 28, 2026 18:18

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

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

New issues introduced by this change (2)
Severity Finding
Medium severity src/​Orleans.Runtime/​Catalog/​ActivationData.cs — Throwing InvalidOperationException when a running request cannot be removed introduces a new…
Low severity test/​Orleans.Runtime.Internal.Tests/​ActivationsLifeCycleTests/​ActivationRequestTrackerTests.cs — Using TaskCreationOptions.LongRunning here can create a dedicated thread per producer (64 threads),…
Pre-existing issues (1)
Severity Finding
Medium severity src/​Orleans.Runtime/​Catalog/​StatelessWorkerGrainContext.cs — GetRequestStatus() acquires the ActivationData lock for every worker inspected. In… View comment
Issues resolved since last review (1)
Severity Finding
Low severity src/​Orleans.Runtime/​Catalog/​ActivationData.cs — These public request-status properties now unconditionally take the activation lock. They are… View resolved comment

Comment on lines +1514 to +1519
if (!_requestTracker!.RemoveRunning(message))
{
throw new InvalidOperationException($"Completed request {message} was not tracked by activation {this}.");
}

ReturnRequestTrackerIfEmpty();
tracker.AddWaiting(messages[index]);
UpdateRequestStatus(activation, tracker);
}
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default)).ToArray();
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