-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Closed
Description
Description
TokenBucketRateLimiter.WaitAsync(int, CancellationToken) still counts the requested tokens against the QueueLimit after they've been canceled by the CancellationToken after an incomplete ValueTask is returned. This is eventually fixed when the request is dequeued from the Deque<RequestRegistration> by ReplenishInternal, but that could be too late.
Reproduction Steps
var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(1, QueueProcessingOrder.OldestFirst, 1,
TimeSpan.Zero, 1, autoReplenishment: false));
limiter.Acquire(1);
var cts = new CancellationTokenSource();
var firstWaitTask = limiter.WaitAsync(1, cts.Token);
cts.Cancel();
await Assert.ThrowsAsync<OperationCanceledException>(() => firstWaitTask.AsTask());
// This incorrectly returns a failed lease immediately because ctc.Cancel() did not reduce the _queueCount.
var secondWaitTask = limiter.WaitAsync(1, cts.Token);
//Assert.False(secondWaitTask.IsCompleted)
Assert.True(limiter.TryReplenish());
// WaitAsync() no longer returns a failed lease because we called TryReplenish(), but that shouldn't be necessary
var thirdWaitTask = limiter.WaitAsync(1, cts.Token);
Assert.False(thirdWaitTask.IsCompleted)Expected behavior
The secondWaitTask should initially be incomplete and later complete successfully when the TokenBucketRateLimiter is replenished.
Actual behavior
The secondWaitTask immediately returns a non-acquired lease because the QueueLimit has supposedly been exceeded (but not really).
Regression?
No.
Known Workarounds
No response
Configuration
No response
Other information
No response
Reactions are currently unavailable