public override async Task CanDequeueMultipleResourcesAtOnce() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(2, QueueProcessingOrder.OldestFirst, 2, TimeSpan.Zero, 2, autoReplenishment: false)); using var lease = await limiter.WaitAsync(2).DefaultTimeout(); Assert.True(lease.IsAcquired); var wait1 = limiter.WaitAsync(1); var wait2 = limiter.WaitAsync(1); Assert.False(wait1.IsCompleted); Assert.False(wait2.IsCompleted); lease.Dispose(); Assert.True(limiter.TryReplenish()); var lease1 = await wait1.DefaultTimeout(); var lease2 = await wait2.DefaultTimeout(); Assert.True(lease1.IsAcquired); Assert.True(lease2.IsAcquired); }
public async Task ReplenishWorksWithTicksOverInt32Max() { using var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(10, QueueProcessingOrder.OldestFirst, 2, TimeSpan.FromMilliseconds(2), 1, autoReplenishment: false)); var lease = limiter.Acquire(10); Assert.True(lease.IsAcquired); var wait = limiter.WaitAsync(1); Assert.False(wait.IsCompleted); var replenishInternalMethod = typeof(TokenBucketRateLimiter).GetMethod("ReplenishInternal", Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance) !; // Ensure next tick is over uint.MaxValue var tick = Stopwatch.GetTimestamp() + uint.MaxValue; replenishInternalMethod.Invoke(limiter, new object[] { tick }); lease = await wait; Assert.True(lease.IsAcquired); wait = limiter.WaitAsync(1); Assert.False(wait.IsCompleted); // Tick 1 millisecond too soon and verify that the queued item wasn't completed replenishInternalMethod.Invoke(limiter, new object[] { tick + 1L * (long)(TimeSpan.TicksPerMillisecond / TickFrequency) }); Assert.False(wait.IsCompleted); // ticks would wrap if using uint replenishInternalMethod.Invoke(limiter, new object[] { tick + 2L * (long)(TimeSpan.TicksPerMillisecond / TickFrequency) }); lease = await wait; Assert.True(lease.IsAcquired); }
public override async Task CanAcquireResourceAsync_QueuesAndGrabsNewest() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(2, QueueProcessingOrder.NewestFirst, 3, TimeSpan.FromMinutes(0), 1, autoReplenishment: false)); var lease = await limiter.WaitAsync(2); Assert.True(lease.IsAcquired); var wait1 = limiter.WaitAsync(2); var wait2 = limiter.WaitAsync(); Assert.False(wait1.IsCompleted); Assert.False(wait2.IsCompleted); lease.Dispose(); Assert.True(limiter.TryReplenish()); // second queued item completes first with NewestFirst lease = await wait2; Assert.True(lease.IsAcquired); Assert.False(wait1.IsCompleted); lease.Dispose(); Assert.Equal(0, limiter.GetAvailablePermits()); Assert.True(limiter.TryReplenish()); Assert.True(limiter.TryReplenish()); lease = await wait1; Assert.True(lease.IsAcquired); }
public override async Task CanFillQueueWithNewestFirstAfterCancelingQueuedRequestWithAnotherQueuedRequest() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(2, QueueProcessingOrder.NewestFirst, 2, TimeSpan.Zero, 2, autoReplenishment: false)); var lease = limiter.Acquire(2); Assert.True(lease.IsAcquired); var cts = new CancellationTokenSource(); var wait = limiter.WaitAsync(1, cts.Token); // Add another item to queue, will be completed as failed later when we queue another item var wait2 = limiter.WaitAsync(1); Assert.False(wait.IsCompleted); cts.Cancel(); var ex = await Assert.ThrowsAsync <TaskCanceledException>(() => wait.AsTask()); Assert.Equal(cts.Token, ex.CancellationToken); lease.Dispose(); var wait3 = limiter.WaitAsync(2); Assert.False(wait3.IsCompleted); // will be kicked by wait3 because we're using NewestFirst lease = await wait2; Assert.False(lease.IsAcquired); limiter.TryReplenish(); lease = await wait3; Assert.True(lease.IsAcquired); }
public override async Task CanAcquireResourcesWithWaitAsyncWithQueuedItemsIfNewestFirst() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(2, QueueProcessingOrder.NewestFirst, 2, TimeSpan.Zero, 2, autoReplenishment: false)); var lease = limiter.Acquire(1); Assert.True(lease.IsAcquired); var wait = limiter.WaitAsync(2); Assert.False(wait.IsCompleted); Assert.Equal(1, limiter.GetAvailablePermits()); lease = await limiter.WaitAsync(1).DefaultTimeout(); Assert.True(lease.IsAcquired); Assert.False(wait.IsCompleted); limiter.TryReplenish(); lease = await wait.DefaultTimeout(); Assert.True(lease.IsAcquired); }
public override async Task DropsMultipleOldestWhenQueuingMoreThanLimit_NewestFirst() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(2, QueueProcessingOrder.NewestFirst, 2, TimeSpan.Zero, 1, autoReplenishment: false)); var lease = limiter.Acquire(2); Assert.True(lease.IsAcquired); var wait = limiter.WaitAsync(1); Assert.False(wait.IsCompleted); var wait2 = limiter.WaitAsync(1); Assert.False(wait2.IsCompleted); var wait3 = limiter.WaitAsync(2); var lease1 = await wait; var lease2 = await wait2; Assert.False(lease1.IsAcquired); Assert.False(lease2.IsAcquired); Assert.False(wait3.IsCompleted); limiter.TryReplenish(); limiter.TryReplenish(); lease = await wait3; Assert.True(lease.IsAcquired); }
public override async Task DisposeAsyncReleasesQueuedAcquires() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(1, QueueProcessingOrder.OldestFirst, 3, TimeSpan.Zero, 1, autoReplenishment: false)); var lease = limiter.Acquire(1); var wait1 = limiter.WaitAsync(1); var wait2 = limiter.WaitAsync(1); var wait3 = limiter.WaitAsync(1); Assert.False(wait1.IsCompleted); Assert.False(wait2.IsCompleted); Assert.False(wait3.IsCompleted); await limiter.DisposeAsync(); lease = await wait1; Assert.False(lease.IsAcquired); lease = await wait2; Assert.False(lease.IsAcquired); lease = await wait3; Assert.False(lease.IsAcquired); // Throws after disposal Assert.Throws <ObjectDisposedException>(() => limiter.Acquire(1)); await Assert.ThrowsAsync <ObjectDisposedException>(() => limiter.WaitAsync(1).AsTask()); }
public override async Task CanAcquireResourceAsync_QueuesAndGrabsOldest() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(1, QueueProcessingOrder.OldestFirst, 2, TimeSpan.Zero, 1, autoReplenishment: false)); var lease = await limiter.WaitAsync(); Assert.True(lease.IsAcquired); var wait1 = limiter.WaitAsync(); var wait2 = limiter.WaitAsync(); Assert.False(wait1.IsCompleted); Assert.False(wait2.IsCompleted); lease.Dispose(); Assert.True(limiter.TryReplenish()); lease = await wait1; Assert.True(lease.IsAcquired); Assert.False(wait2.IsCompleted); lease.Dispose(); Assert.Equal(0, limiter.GetAvailablePermits()); Assert.True(limiter.TryReplenish()); lease = await wait2; Assert.True(lease.IsAcquired); }
public override async Task LargeAcquiresAndQueuesDoNotIntegerOverflow() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(int.MaxValue, QueueProcessingOrder.NewestFirst, int.MaxValue, TimeSpan.Zero, int.MaxValue, autoReplenishment: false)); var lease = limiter.Acquire(int.MaxValue); Assert.True(lease.IsAcquired); // Fill queue var wait = limiter.WaitAsync(3); Assert.False(wait.IsCompleted); var wait2 = limiter.WaitAsync(int.MaxValue); Assert.False(wait2.IsCompleted); var lease1 = await wait; Assert.False(lease1.IsAcquired); limiter.TryReplenish(); var lease2 = await wait2; Assert.True(lease2.IsAcquired); }
public override async Task CannotAcquireResourcesWithWaitAsyncWithQueuedItemsIfOldestFirst() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(2, QueueProcessingOrder.OldestFirst, 3, TimeSpan.Zero, 2, autoReplenishment: false)); var lease = limiter.Acquire(1); Assert.True(lease.IsAcquired); var wait = limiter.WaitAsync(2); var wait2 = limiter.WaitAsync(1); Assert.False(wait.IsCompleted); Assert.False(wait2.IsCompleted); limiter.TryReplenish(); lease = await wait; Assert.True(lease.IsAcquired); Assert.False(wait2.IsCompleted); limiter.TryReplenish(); lease = await wait2; Assert.True(lease.IsAcquired); }
public override async Task FailsWhenQueuingMoreThanLimit_OldestFirst() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(1, QueueProcessingOrder.OldestFirst, 1, TimeSpan.Zero, 1, autoReplenishment: false)); using var lease = limiter.Acquire(1); var wait = limiter.WaitAsync(1); var failedLease = await limiter.WaitAsync(1); Assert.False(failedLease.IsAcquired); Assert.True(failedLease.TryGetMetadata(MetadataName.RetryAfter, out var timeSpan)); Assert.Equal(TimeSpan.Zero, timeSpan); }
public override async Task CanAcquireResourceAsync() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(1, QueueProcessingOrder.NewestFirst, 1, TimeSpan.Zero, 1, autoReplenishment: false)); using var lease = await limiter.WaitAsync(); Assert.True(lease.IsAcquired); var wait = limiter.WaitAsync(); Assert.False(wait.IsCompleted); Assert.True(limiter.TryReplenish()); Assert.True((await wait).IsAcquired); }
public override async Task WaitAsyncZero_WithoutAvailabilityWaitsForAvailability() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(1, QueueProcessingOrder.NewestFirst, 1, TimeSpan.Zero, 1, autoReplenishment: false)); var lease = await limiter.WaitAsync(1); Assert.True(lease.IsAcquired); var wait = limiter.WaitAsync(0); Assert.False(wait.IsCompleted); lease.Dispose(); Assert.True(limiter.TryReplenish()); using var lease2 = await wait; Assert.True(lease2.IsAcquired); }
public async Task CorrectRetryMetadataWithLargeTokensPerPeriod() { var options = new TokenBucketRateLimiterOptions(2, QueueProcessingOrder.OldestFirst, 1, TimeSpan.FromSeconds(20), 100, autoReplenishment: false); var limiter = new TokenBucketRateLimiter(options); using var lease = limiter.Acquire(2); // Queue item which changes the retry after time for failed items var wait = limiter.WaitAsync(1); Assert.False(wait.IsCompleted); var failedLease = await limiter.WaitAsync(2); Assert.False(failedLease.IsAcquired); Assert.True(failedLease.TryGetMetadata(MetadataName.RetryAfter, out var typedMetadata)); Assert.Equal(options.ReplenishmentPeriod, typedMetadata); }
public override async Task WaitAsyncZero_WithAvailability() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(1, QueueProcessingOrder.NewestFirst, 1, TimeSpan.Zero, 1, autoReplenishment: false)); using var lease = await limiter.WaitAsync(0); Assert.True(lease.IsAcquired); }
public async Task AutoReplenish_ReplenishesTokens() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(2, QueueProcessingOrder.OldestFirst, 1, TimeSpan.FromMilliseconds(1000), 1, autoReplenishment: true)); Assert.Equal(2, limiter.GetAvailablePermits()); limiter.Acquire(2); var lease = await limiter.WaitAsync(1); Assert.True(lease.IsAcquired); }
public async Task ReplenishWorksWhenTicksWrap() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(10, QueueProcessingOrder.OldestFirst, 2, TimeSpan.FromMilliseconds(2), 1, autoReplenishment: false)); var lease = limiter.Acquire(10); Assert.True(lease.IsAcquired); var wait = limiter.WaitAsync(1); Assert.False(wait.IsCompleted); // This will set the last tick to the max value limiter.ReplenishInternal(uint.MaxValue); lease = await wait.DefaultTimeout(); Assert.True(lease.IsAcquired); wait = limiter.WaitAsync(1); Assert.False(wait.IsCompleted); // ticks wrapped, should replenish limiter.ReplenishInternal(2); lease = await wait.DefaultTimeout(); Assert.True(lease.IsAcquired); limiter.ReplenishInternal(uint.MaxValue); wait = limiter.WaitAsync(2); Assert.False(wait.IsCompleted); // ticks wrapped, but only 1 millisecond passed, make sure the wrapping behaves correctly and replenish doesn't happen limiter.ReplenishInternal(1); Assert.False(wait.IsCompleted); Assert.Equal(1, limiter.GetAvailablePermits()); }
public async Task ReplenishWorksWhenTicksWrap() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(10, QueueProcessingOrder.OldestFirst, 2, TimeSpan.FromMilliseconds(2), 1, autoReplenishment: false)); var lease = limiter.Acquire(10); Assert.True(lease.IsAcquired); var wait = limiter.WaitAsync(1); Assert.False(wait.IsCompleted); var replenishInternalMethod = typeof(TokenBucketRateLimiter).GetMethod("ReplenishInternal", Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance) !; // This will set the last tick to the max value replenishInternalMethod.Invoke(limiter, new object[] { uint.MaxValue }); lease = await wait; Assert.True(lease.IsAcquired); wait = limiter.WaitAsync(1); Assert.False(wait.IsCompleted); // ticks wrapped, should replenish replenishInternalMethod.Invoke(limiter, new object[] { 2U }); lease = await wait; Assert.True(lease.IsAcquired); replenishInternalMethod.Invoke(limiter, new object[] { uint.MaxValue }); wait = limiter.WaitAsync(2); Assert.False(wait.IsCompleted); // ticks wrapped, but only 1 millisecond passed, make sure the wrapping behaves correctly and replenish doesn't happen replenishInternalMethod.Invoke(limiter, new object[] { 1U }); Assert.False(wait.IsCompleted); Assert.Equal(1, limiter.GetAvailablePermits()); }
public override async Task DropsRequestedLeaseIfPermitCountGreaterThanQueueLimitAndNoAvailability_NewestFirst() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(2, QueueProcessingOrder.NewestFirst, 1, TimeSpan.Zero, 1, autoReplenishment: false)); var lease = limiter.Acquire(2); Assert.True(lease.IsAcquired); // Fill queue var wait = limiter.WaitAsync(1); Assert.False(wait.IsCompleted); var lease1 = await limiter.WaitAsync(2); Assert.False(lease1.IsAcquired); limiter.TryReplenish(); lease = await wait; Assert.True(lease.IsAcquired); }
public override async Task QueueAvailableAfterQueueLimitHitAndResources_BecomeAvailable() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(1, QueueProcessingOrder.OldestFirst, 1, TimeSpan.Zero, 1, autoReplenishment: false)); var lease = limiter.Acquire(1); var wait = limiter.WaitAsync(1); var failedLease = await limiter.WaitAsync(1); Assert.False(failedLease.IsAcquired); limiter.TryReplenish(); lease = await wait; Assert.True(lease.IsAcquired); wait = limiter.WaitAsync(1); Assert.False(wait.IsCompleted); limiter.TryReplenish(); lease = await wait; Assert.True(lease.IsAcquired); }
public async Task CorrectRetryMetadataWithNonZeroAvailableItems() { var options = new TokenBucketRateLimiterOptions(3, QueueProcessingOrder.OldestFirst, 1, TimeSpan.FromSeconds(20), 1, autoReplenishment: false); var limiter = new TokenBucketRateLimiter(options); using var lease = limiter.Acquire(2); var failedLease = await limiter.WaitAsync(3); Assert.False(failedLease.IsAcquired); Assert.True(failedLease.TryGetMetadata(MetadataName.RetryAfter, out var typedMetadata)); Assert.Equal(options.ReplenishmentPeriod.Ticks * 2, typedMetadata.Ticks); }
public override async Task CancelUpdatesQueueLimit() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(1, QueueProcessingOrder.OldestFirst, 1, TimeSpan.Zero, 1, autoReplenishment: false)); var lease = limiter.Acquire(1); Assert.True(lease.IsAcquired); var cts = new CancellationTokenSource(); var wait = limiter.WaitAsync(1, cts.Token); cts.Cancel(); var ex = await Assert.ThrowsAsync <TaskCanceledException>(() => wait.AsTask()); Assert.Equal(cts.Token, ex.CancellationToken); wait = limiter.WaitAsync(1); Assert.False(wait.IsCompleted); limiter.TryReplenish(); lease = await wait; Assert.True(lease.IsAcquired); }
public async Task RetryMetadataOnFailedWaitAsync() { var options = new TokenBucketRateLimiterOptions(2, QueueProcessingOrder.OldestFirst, 1, TimeSpan.FromSeconds(20), 1, autoReplenishment: false); var limiter = new TokenBucketRateLimiter(options); using var lease = limiter.Acquire(2); var failedLease = await limiter.WaitAsync(2).DefaultTimeout(); Assert.False(failedLease.IsAcquired); Assert.True(failedLease.TryGetMetadata(MetadataName.RetryAfter.Name, out var metadata)); Assert.Equal(options.ReplenishmentPeriod * 2, metadata); Assert.True(failedLease.TryGetMetadata(MetadataName.RetryAfter, out var typedMetadata)); Assert.Equal(options.ReplenishmentPeriod * 2, typedMetadata); Assert.Collection(failedLease.MetadataNames, item => item.Equals(MetadataName.RetryAfter.Name)); }
public override async Task CanCancelWaitAsyncBeforeQueuing() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(1, QueueProcessingOrder.OldestFirst, 1, TimeSpan.Zero, 1, autoReplenishment: false)); var lease = limiter.Acquire(1); Assert.True(lease.IsAcquired); var cts = new CancellationTokenSource(); cts.Cancel(); await Assert.ThrowsAsync <TaskCanceledException>(() => limiter.WaitAsync(1, cts.Token).AsTask()); lease.Dispose(); Assert.True(limiter.TryReplenish()); Assert.Equal(1, limiter.GetAvailablePermits()); }
public override async Task CanDisposeAfterCancelingQueuedRequest() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(1, QueueProcessingOrder.OldestFirst, 1, TimeSpan.Zero, 1, autoReplenishment: false)); var lease = limiter.Acquire(1); Assert.True(lease.IsAcquired); var cts = new CancellationTokenSource(); var wait = limiter.WaitAsync(1, cts.Token); cts.Cancel(); var ex = await Assert.ThrowsAsync <TaskCanceledException>(() => wait.AsTask()); Assert.Equal(cts.Token, ex.CancellationToken); // Make sure dispose doesn't have any side-effects when dealing with a canceled queued item limiter.Dispose(); }
public override async Task ThrowsWhenWaitingForLessThanZero() { var limiter = new TokenBucketRateLimiter(new TokenBucketRateLimiterOptions(1, QueueProcessingOrder.NewestFirst, 1, TimeSpan.Zero, 1, autoReplenishment: false)); await Assert.ThrowsAsync <ArgumentOutOfRangeException>(async() => await limiter.WaitAsync(-1)); }