perf(runtime): reduce activation working set entry overhead - #10114
perf(runtime): reduce activation working set entry overhead#10114ReubenBond wants to merge 12 commits into
Conversation
4ff844f to
f7f232c
Compare
There was a problem hiding this comment.
Pull request overview
This pull request reduces activation working-set entry overhead by storing idle state directly in dictionary values.
Changes:
- Replaces per-entry state objects with Boolean values.
- Adds atomic state transitions and conditional eviction handling.
- Preserves existing public interfaces.
Show a summary per file
| File | Summary | Review status |
|---|---|---|
src/Orleans.Runtime/Catalog/ActivationWorkingSet.cs |
Optimizes working-set storage and transition logic. | Critical eviction/re-add race requires an entry identity or generation check before OnIdle. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Suppressed comments (1)
src/Orleans.Runtime/Catalog/ActivationWorkingSet.cs:80
- The correctness-sensitive part of this optimization is the CAS-based active/idle transition, but the existing tests only add members and exercise collector buckets; they do not drive
OnActive,VisitMember, or conditional eviction. Please add focused working-set tests covering active→idle→active transitions and a stale scan entry racing with reactivation before relying on this change.
if (!isIdle || _members.TryUpdate(member, false, comparisonValue: true))
{
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
e2e85a3 to
e0dd203
Compare
Code coverage77.50% line coverage - 97,377 / 125,646 lines Coverage details
|
6097ce4 to
11da98e
Compare
There was a problem hiding this comment.
Review details
Suppressed comments (1)
src/Orleans.Runtime/Catalog/ActivationWorkingSet.cs:289
IActivationWorkingSetMemberis a public interface inOrleans.Runtime, so adding this required property is a source/binary compatibility break for external implementations. Unlike the observer methods, this member has no default implementation, so existing consumers implementing the interface will fail when upgrading. Please preserve compatibility with an internal state carrier/adapter, or otherwise handle this as an intentional major-version API change before exposing the property.
public interface IActivationWorkingSetMember
{
/// <summary>
/// Gets or sets whether this member was idle during the previous working-set scan.
/// </summary>
bool IsIdle { get; set; }
- Files reviewed: 5/5 changed files
- Comments generated: 1
- Review effort level: Lite
There was a problem hiding this comment.
Review details
Suppressed comments (1)
test/Orleans.Core.Tests/Runtime/ActivationCollectorTests.cs:654
- This synchronization does not make the test deterministic. After
resumeScan.Set(), the firstVisitMemberreleases the member lock, and the monitor can immediately begin the next scan whilereactivationTaskis competing for the same lock. If reactivation wins, the second scan observesIsIdle == falseand produces another idle/active transition instead of the expected eviction, causing the assertions below to fail intermittently. Pause the scan on thewouldRemove == truevisit (or otherwise gate the timer) before starting the reactivation.
resumeScan.Set();
await reactivationTask;
await lifecycle.OnStop();
- Files reviewed: 5/5 changed files
- Comments generated: 1
- Review effort level: Lite
There was a problem hiding this comment.
Review details
Suppressed comments (2)
src/Orleans.Runtime/Catalog/ActivationData.cs:1545
OnDeactivatingnow clearsIsInWorkingSet, so a request which was already running when deactivation starts (includingDeactivateOnIdle, or a deactivation selected just before the request began) reaches this block with the bit cleared and re-registers the deactivating activation viaOnActive. That temporarily makes it visible to the working set and can keep a stuck/deactivating activation counted until its final cleanup; the previous explicit-deactivation path deliberately left this bit set. Only re-add after a completion when the activation is stillValid(or otherwise exclude the deactivating state).
if (!IsInWorkingSet)
{
IsInWorkingSet = true;
_shared.InternalRuntime.ActivationWorkingSet.OnActive(this);
}
src/Orleans.Runtime/Catalog/ActivationWorkingSet.cs:70
- The member is published to
_membersbefore_activeCountis incremented. IfOnEvictedruns after this lock is released but before line 70 (the member lock is the only synchronization used by these methods), it can remove the new entry and decrement the count from zero, makingCounttemporarily negative and allowing an invalid working-set count to be published. Increment the count while still holding the member lock, asOnActivedoes.
Interlocked.Increment(ref _activeCount);
- Files reviewed: 5/5 changed files
- Comments generated: 0 new
- Review effort level: Lite
73be6ab to
c44c8e6
Compare
There was a problem hiding this comment.
Review details
Suppressed comments (2)
src/Orleans.Runtime/Catalog/ActivationWorkingSet.cs:166
- The member lock only serializes
VisitMemberwith mutations after the visit starts; it does not tie the enumerated key to the membership that was present when it was yielded. If enumeration yields a member, another thread removes and re-adds it, and then this visit runs,IsInWorkingSetis true for the new registration and a candidate result can mark that new registration idle (and emitOnIdle). Preserve an entry identity/generation, or otherwise prevent a pre-removal enumeration from mutating a re-added registration. The added race tests block re-addition whileVisitMemberholds the lock, so they do not cover this ordering.
// Enumeration can retain a member across removal and re-addition. CLOCK state is advisory, so visit the
// member's current state while holding its lock instead of adding a dictionary validation to every scan.
lock (member)
{
if (!member.IsInWorkingSet)
src/Orleans.Runtime/Catalog/ActivationWorkingSet.cs:50
- A
ConcurrentDictionaryenumerator can retain an entry afterOnEvictedremoves it. SinceOnEvictednow clearsIsIdle, this filter can yield the removed member as active fromMembers(theIncomingRequestMonitorthen analyzes it even though it is no longer registered). Include the member-registration bit in this filter so the advisory weak enumeration does not expose evicted activations.
if (!pair.Key.IsIdle)
{
- Files reviewed: 5/5 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 1
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
src/Orleans.Runtime/Catalog/ActivationWorkingSet.cs — EnumerateActiveMembers() filters IActivationWorkingSetMemberStatus entries only by IsIdle. If… |
7e4c4e8 to
2ea7a83
Compare
2ea7a83 to
f73b4f6
Compare
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 1
Pre-existing issues (1)
| Severity | Finding |
|---|---|
src/Orleans.Runtime/Catalog/ActivationWorkingSet.cs — EnumerateActiveMembers() filters IActivationWorkingSetMemberStatus entries only by IsIdle. If… View comment |
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 1
Pre-existing issues (1)
| Severity | Finding |
|---|---|
src/Orleans.Runtime/Catalog/ActivationWorkingSet.cs — EnumerateActiveMembers() filters IActivationWorkingSetMemberStatus entries only by IsIdle. If… View comment |
Suppressed comments (1)
src/Orleans.Runtime/Catalog/ActivationWorkingSet.cs:52
Membersenumeration can yield an evicted status-backed member ifConcurrentDictionaryenumeration includes a stale entry:OnEvictedclearsIsIdle, so the current predicate (!status.IsIdle) can returntrueeven whenstatus.IsInWorkingSet == false. Filtering onIsInWorkingSetavoids reporting members which have been removed from the working set (as indicated by the member’s own status bit).
if (pair.Key is IActivationWorkingSetMemberStatus status
? !status.IsIdle
: (pair.Value & IsIdleMask) == 0)
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
Review tier: Lite
Findings: None
Issues resolved since last review (1)
| Severity | Finding |
|---|---|
src/Orleans.Runtime/Catalog/ActivationWorkingSet.cs — EnumerateActiveMembers() filters IActivationWorkingSetMemberStatus entries only by IsIdle. If… View resolved comment |
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
src/Orleans.Runtime/Catalog/ActivationData.cs:432
Stateand the working-set flags are read usingVolatile.Read(ref _status), butSetStatewrites_statususing a plain store. For lock-free readers to reliably observe updates across threads, the write side should also useVolatile.Write(or make_statusvolatile).
This issue also appears on line 434 of the same file.
public void SetState(ActivationState state)
{
Debug.Assert(Monitor.IsEntered(this));
_status = (byte)((_status & ~ActivationStateMask) | (byte)state);
}
src/Orleans.Runtime/Catalog/ActivationData.cs:438
- The
_statusfield is read viaVolatile.Read(ref _status)from multiple threads (e.g.,IsInWorkingSet/IsIdleInWorkingSetgetters), butSetStatusFlagupdates it with a non-volatile write. UseVolatile.Write(or make_statusvolatile) so other threads reliably observe flag changes.
private void SetStatusFlag(byte mask, bool value)
{
Debug.Assert(Monitor.IsEntered(this));
_status = value ? (byte)(_status | mask) : (byte)(_status & ~mask);
}

Summary
ActivationWorkingSetdictionary only for membership, eliminating the per-entry state object and dictionary updates during active/idle transitions.ActivationDataalready requires, while treating weakly-consistent enumeration as advisory CLOCK behavior.ActivationDataintegration tests.Impact