Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.Threading.RateLimiting.DefaultPartitionedRateLimiter cache invalidation bug #95611

Closed
PaulKlein opened this issue Dec 4, 2023 · 3 comments · Fixed by #98075
Closed

Comments

@PaulKlein
Copy link
Contributor

Description

In the System.Threading.RateLimiting nuget package, PartitionedRateLimiter.Create() returns DefaultPartionedRateLimiter instances.

The DefaultPartionedRateLimiter internally caches the created partioned RateLimiter instances. It tracks when this cache is invalid using _cacheInvalid which is checked from the Heartbeat method.

However the Heartbeat() method never clears this flag, so once set, the cache invalidation runs every time the heartbeat triggers, which by default is every 100 milliseconds. This means it is constantly clearing + re-initialising the cache list.

See: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/DefaultPartitionedRateLimiter.cs#L216

Reproduction Steps

Create a partitioned rate limiter, and request a rate limit lease to trigger the initial cache invalidation.

var partitionedRateLimiter = PartitionedRateLimiter.Create<string, string>(
  resource => RateLimitPartition.GetNoLimiter(resource)
);
var rateLimiter = partitionedRateLimiter.AttemptAcquire("myResource");

From then on, every time DefaultPartionedRateLimiter.Heartbeat() fires, the _cacheInvalid logic triggers and clears + re-initialises the _cachedLimiters list.

Expected behavior

After the _cachedLimiters list is rebuilt, _cacheInvalid should be reset to false so that it is only rebuilt when required.

Actual behavior

The cached list is rebuilt every 100 milliseconds unnecessarily.

Regression?

No response

Known Workarounds

No response

Configuration

System.Threading.RateLimiting Nuget package 8.0.0

Other information

No response

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Dec 4, 2023
@ghost
Copy link

ghost commented Dec 4, 2023

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

Issue Details

Description

In the System.Threading.RateLimiting nuget package, PartitionedRateLimiter.Create() returns DefaultPartionedRateLimiter instances.

The DefaultPartionedRateLimiter internally caches the created partioned RateLimiter instances. It tracks when this cache is invalid using _cacheInvalid which is checked from the Heartbeat method.

However the Heartbeat() method never clears this flag, so once set, the cache invalidation runs every time the heartbeat triggers, which by default is every 100 milliseconds. This means it is constantly clearing + re-initialising the cache list.

See: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/DefaultPartitionedRateLimiter.cs#L216

Reproduction Steps

Create a partitioned rate limiter, and request a rate limit lease to trigger the initial cache invalidation.

var partitionedRateLimiter = PartitionedRateLimiter.Create<string, string>(
  resource => RateLimitPartition.GetNoLimiter(resource)
);
var rateLimiter = partitionedRateLimiter.AttemptAcquire("myResource");

From then on, every time DefaultPartionedRateLimiter.Heartbeat() fires, the _cacheInvalid logic triggers and clears + re-initialises the _cachedLimiters list.

Expected behavior

After the _cachedLimiters list is rebuilt, _cacheInvalid should be reset to false so that it is only rebuilt when required.

Actual behavior

The cached list is rebuilt every 100 milliseconds unnecessarily.

Regression?

No response

Known Workarounds

No response

Configuration

System.Threading.RateLimiting Nuget package 8.0.0

Other information

No response

Author: PaulKlein
Assignees: -
Labels:

area-System.Threading

Milestone: -

@BrennanConroy
Copy link
Member

Thanks for catching that, would you be interested in contributing a PR to fix it?

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Feb 7, 2024
@PaulKlein
Copy link
Contributor Author

Sure, I think it's just a case of resetting this flag after rebuilding the cache? I've added a simple PR for this, hopefully this is OK.

I looked at the tests as well but I wasn't sure if there's any easy way to add a test for this.

Cheers

BrennanConroy pushed a commit that referenced this issue Feb 13, 2024
@ghost ghost removed in-pr There is an active PR which will close this issue when it is merged untriaged New issue has not been triaged by the area owner labels Feb 13, 2024
@BrennanConroy BrennanConroy added this to the 9.0.0 milestone Feb 13, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Mar 16, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants