perf(runtime): lazily allocate activation request tracking - #10115
Open
ReubenBond wants to merge 4 commits into
Open
perf(runtime): lazily allocate activation request tracking#10115ReubenBond wants to merge 4 commits into
ReubenBond wants to merge 4 commits into
Conversation
Contributor
There was a problem hiding this comment.
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
_waitingRequestsand_runningRequestsinActivationDataand 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
ReubenBond
force-pushed
the
rebond/lazy-activation-request-tracking
branch
from
August 18, 2026 09:41
2119bab to
932b619
Compare
Contributor
There was a problem hiding this comment.
Review details
Suppressed comments (1)
src/Orleans.Runtime/Catalog/GrainTypeSharedContext.cs:26
- The new request-tracking pools are unbounded (
ConcurrentStackwith no size cap) and will retain the backing arrays of any largeList/Dictionaryinstances 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
ReubenBond
force-pushed
the
rebond/lazy-activation-request-tracking
branch
from
August 21, 2026 21:14
932b619 to
fe6d45c
Compare
Contributor
There was a problem hiding this comment.
Review details
Suppressed comments (1)
src/Orleans.Runtime/Catalog/ActivationData.cs:505
GetRequestCountalready holdsthis, but callingWaitingCountre-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 ?? 0directly 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
marked this pull request as ready for review
August 21, 2026 22:30
ReubenBond
force-pushed
the
rebond/lazy-activation-request-tracking
branch
from
August 23, 2026 15:51
fe6d45c to
3849915
Compare
Contributor
Code coverage52.17% line coverage - 52,895 / 101,389 lines Coverage details
|
ReubenBond
force-pushed
the
rebond/lazy-activation-request-tracking
branch
from
August 28, 2026 17:26
3849915 to
aedf82f
Compare
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>
Contributor
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 1
New issues introduced by this change (2)
| Severity | Finding |
|---|---|
src/Orleans.Runtime/Catalog/StatelessWorkerGrainContext.cs — GetRequestStatus() acquires the ActivationData lock for every worker inspected. In… |
|
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) | ||
| { |
ReubenBond
force-pushed
the
rebond/lazy-activation-request-tracking
branch
from
August 28, 2026 18:18
aedf82f to
9c6fbca
Compare
Contributor
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 2
New issues introduced by this change (2)
| Severity | Finding |
|---|---|
src/Orleans.Runtime/Catalog/ActivationData.cs — Throwing InvalidOperationException when a running request cannot be removed introduces a new… |
|
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 |
|---|---|
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 |
|---|---|
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(); |
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.


Summary
Validation
dotnet build src\Orleans.Runtime\Orleans.Runtime.csproj --nologo --verbosity minimal -property:UseSharedCompilation=falsedotnet build test\Grains\TestVersionGrains\TestVersionGrains.csproj --nologo --verbosity minimal -property:UseSharedCompilation=falsedotnet build test\Grains\TestVersionGrains2\TestVersionGrains2.csproj --nologo --verbosity minimal -property:UseSharedCompilation=falsedotnet test test\Orleans.Runtime.Tests\Orleans.Runtime.Tests.csproj --no-build --nologo --verbosity minimal -property:UseSharedCompilation=falseMicrosoft Reviewers: Open in CodeFlow