[release/10.0] Fix NoopLimiter disposal in DefaultPartitionedRateLimiter Heartbeat - #133647
Conversation
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @JulieLeeMSFT, @VSadov |
…127582) Fixes `DefaultPartitionedRateLimiter` so that `NoopLimiter` partitions are evicted and disposed by the Heartbeat timer, which previously never happened because `NoopLimiter.IdleDuration` always returns `null`. ## Changes Made - Introduced a `LimiterEntry` wrapper that tracks a `LastAccessTimestamp` (updated on each `Acquire`/`WaitAsync` call) alongside the `RateLimiter` instance. - Added `GetIdleDuration(LimiterEntry)` helper that falls back to the elapsed time since last access for `NoopLimiter` (whose `IdleDuration` is always `null`), and returns `null` for all other limiter types that return `null` (preserving the existing "do not evict" contract). - Updated the Heartbeat eviction check to use the `is TimeSpan idleDuration && idleDuration > s_idleTimeLimit` pattern (with `?? TimeSpan.Zero` for the under-lock re-check), correctly handling the nullable `TimeSpan?` return — `null` skips eviction, matching the original pattern. - Used `Volatile.Read`/`Volatile.Write` for atomic 64-bit timestamp access on 32-bit platforms. - Added a `RateLimiterHelper.GetElapsedTime(long)` overload to avoid `.GetValueOrDefault()` at call sites. - Added tests covering `NoopLimiter` eviction via Heartbeat and verifying that non-`NoopLimiter` partitions with `IdleDuration == null` are not evicted while active. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com> Co-authored-by: Vladimir Sadov <vsadov@microsoft.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
78e27af to
85f3f55
Compare
|
@agocke fyi there are build failures |
|
@copilot please fix conflicts |
Co-authored-by: agocke <515774+agocke@users.noreply.github.com>
The branch already includes the current |
|
/ba-g failures look like #131992, and the fix hasn't been backported |
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The backport preserves existing semantics, addresses the leak with concurrency-safe tracking, and includes focused regression coverage.
Review effort: Balanced
Findings: None
What changed in this PR
Backports the .NET 11 fix that evicts stale NoopLimiter partitions, preventing unbounded retention and heartbeat overhead.
Changes:
- Tracks partition access timestamps and safely evicts idle
NoopLimiterinstances. - Adds elapsed-time support for non-null timestamps.
- Adds regression tests for eviction and null
IdleDurationbehavior.
Validation: Builds and tests were not run in the constrained review environment.
| File | Description |
|---|---|
DefaultPartitionedRateLimiter.cs |
Adds access tracking and NoopLimiter eviction. |
RateLimiterHelper.cs |
Adds a non-null timestamp overload. |
PartitionedRateLimiterTests.cs |
Tests eviction and compatibility behavior. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Backport of #127582 to release/10.0
/cc @agocke @copilot
Customer Impact
DefaultPartitionedRateLimiter never disposes NoopLimiter leading to memory leak and increased CPU usage as it performs its internal work on list of all limiters every 100ms.
Regression
Looks like this code existed all the way back to at least .NET 7.
Testing
New unit tests added, change validated by user.
Risk
Low