Skip to content

[release/10.0] Fix NoopLimiter disposal in DefaultPartitionedRateLimiter Heartbeat - #133647

Merged
agocke merged 3 commits into
release/10.0from
backport/pr-127582-to-release/10.0
Sep 30, 2026
Merged

agocke merged 3 commits into
release/10.0from
backport/pr-127582-to-release/10.0

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Backport of #127582 to release/10.0

/cc @agocke @copilot

Customer Impact

  • Customer reported
  • Found internally

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

  • Yes
  • No

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

@azure-pipelines

Copy link
Copy Markdown
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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @VSadov
See info in area-owners.md if you want to be subscribed.

…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>
@github-actions
github-actions Bot force-pushed the backport/pr-127582-to-release/10.0 branch from 78e27af to 85f3f55 Compare September 14, 2026 17:58
@rbhanda rbhanda added the Servicing-approved Approved for servicing release label Sep 14, 2026
@rbhanda rbhanda added this to the 10.0.13 milestone Sep 14, 2026
@akoeplinger

Copy link
Copy Markdown
Member

@agocke fyi there are build failures

@agocke

agocke commented Sep 29, 2026

Copy link
Copy Markdown
Member

@copilot please fix conflicts

Co-authored-by: agocke <515774+agocke@users.noreply.github.com>

Copilot AI commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

@copilot please fix conflicts

The branch already includes the current release/10.0 base in merge commit a0b17fa, so there are no unresolved merge conflicts. I also fixed the reported compile errors in 64e4fb1ff.

@agocke

agocke commented Sep 30, 2026

Copy link
Copy Markdown
Member

/ba-g failures look like #131992, and the fix hasn't been backported

@agocke
agocke requested review from eduardo-vp and a balanced review from Copilot September 30, 2026 17:32

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 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 NoopLimiter instances.
  • Adds elapsed-time support for non-null timestamps.
  • Adds regression tests for eviction and null IdleDuration behavior.

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.

@agocke
agocke merged commit 8c770a5 into release/10.0 Sep 30, 2026
90 of 92 checks passed
@agocke
agocke deleted the backport/pr-127582-to-release/10.0 branch September 30, 2026 21:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.Threading Servicing-approved Approved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants