public async Task Create_DisposeAsyncDisposesAllLimiters() { var limiterFactory = new TrackingRateLimiterFactory <int>(); using var limiter = PartitionedRateLimiter.Create <string, int>(resource => { if (resource == "1") { return(RateLimitPartition.Get(1, key => limiterFactory.GetLimiter(key))); } return(RateLimitPartition.Get(2, key => limiterFactory.GetLimiter(key))); }); limiter.Acquire("1"); limiter.Acquire("2"); await limiter.DisposeAsync(); Assert.Equal(2, limiterFactory.Limiters.Count); Assert.Equal(1, limiterFactory.Limiters[0].Limiter.AcquireCallCount); Assert.Equal(1, limiterFactory.Limiters[0].Limiter.DisposeCallCount); Assert.Equal(1, limiterFactory.Limiters[0].Limiter.DisposeAsyncCallCount); Assert.Equal(1, limiterFactory.Limiters[1].Limiter.AcquireCallCount); Assert.Equal(1, limiterFactory.Limiters[1].Limiter.DisposeCallCount); Assert.Equal(1, limiterFactory.Limiters[1].Limiter.DisposeAsyncCallCount); }
public void Create_PassedInEqualityComparerIsUsed() { var limiterFactory = new TrackingRateLimiterFactory <int>(); var equality = new TestEquality(); using var limiter = PartitionedRateLimiter.Create <string, int>(resource => { if (resource == "1") { return(RateLimitPartition.Get(1, key => limiterFactory.GetLimiter(key))); } return(RateLimitPartition.Get(2, key => limiterFactory.GetLimiter(key))); }, equality); limiter.Acquire("1"); // GetHashCode to add item to dictionary (skips TryGet for empty dictionary) Assert.Equal(0, equality.EqualsCallCount); Assert.Equal(1, equality.GetHashCodeCallCount); limiter.Acquire("1"); // GetHashCode and Equal from TryGet to see if item is in dictionary Assert.Equal(1, equality.EqualsCallCount); Assert.Equal(2, equality.GetHashCodeCallCount); limiter.Acquire("2"); // GetHashCode from TryGet (fails check) and second GetHashCode to add item to dictionary Assert.Equal(1, equality.EqualsCallCount); Assert.Equal(4, equality.GetHashCodeCallCount); Assert.Equal(2, limiterFactory.Limiters.Count); Assert.Equal(2, limiterFactory.Limiters[0].Limiter.AcquireCallCount); Assert.Equal(1, limiterFactory.Limiters[1].Limiter.AcquireCallCount); }
public async Task Create_BlockingWaitDoesNotBlockOtherPartitions() { var limiterFactory = new TrackingRateLimiterFactory <int>(); using var limiter = PartitionedRateLimiter.Create <string, int>(resource => { if (resource == "1") { return(RateLimitPartition.Get(1, key => limiterFactory.GetLimiter(key))); } return(RateLimitPartition.GetConcurrencyLimiter(2, _ => new ConcurrencyLimiterOptions(1, QueueProcessingOrder.OldestFirst, 2))); }); var lease = await limiter.WaitAndAcquireAsync("2"); var wait = limiter.WaitAndAcquireAsync("2"); Assert.False(wait.IsCompleted); // Different partition, should not be blocked by the wait in the other partition await limiter.WaitAndAcquireAsync("1"); lease.Dispose(); await wait; Assert.Equal(1, limiterFactory.Limiters.Count); Assert.Equal(0, limiterFactory.Limiters[0].Limiter.AcquireCallCount); Assert.Equal(1, limiterFactory.Limiters[0].Limiter.WaitAndAcquireAsyncCallCount); }
public void Create_MultiplePartitionsWork() { var limiterFactory = new TrackingRateLimiterFactory <int>(); using var limiter = PartitionedRateLimiter.Create <string, int>(resource => { if (resource == "1") { return(RateLimitPartition.Get(1, key => limiterFactory.GetLimiter(key))); } else { return(RateLimitPartition.Get(2, key => limiterFactory.GetLimiter(key))); } }); limiter.Acquire("1"); limiter.Acquire("2"); limiter.Acquire("1"); limiter.Acquire("2"); Assert.Equal(2, limiterFactory.Limiters.Count); Assert.Equal(2, limiterFactory.Limiters[0].Limiter.AcquireCallCount); Assert.Equal(1, limiterFactory.Limiters[0].Key); Assert.Equal(2, limiterFactory.Limiters[1].Limiter.AcquireCallCount); Assert.Equal(2, limiterFactory.Limiters[1].Key); }
public void Create_GetAvailablePermitsCallsUnderlyingPartitionsLimiter() { var limiterFactory = new TrackingRateLimiterFactory <int>(); using var limiter = PartitionedRateLimiter.Create <string, int>(resource => { return(RateLimitPartition.Get(1, key => limiterFactory.GetLimiter(key))); }); limiter.GetAvailablePermits(""); Assert.Equal(1, limiterFactory.Limiters.Count); Assert.Equal(1, limiterFactory.Limiters[0].Limiter.GetAvailablePermitsCallCount); }
public void Create_DisposeWithoutLimitersNoops() { var limiterFactory = new TrackingRateLimiterFactory <int>(); using var limiter = PartitionedRateLimiter.Create <string, int>(resource => { return(RateLimitPartition.Get(1, key => limiterFactory.GetLimiter(key))); }); limiter.Dispose(); Assert.Equal(0, limiterFactory.Limiters.Count); }
public async Task Create_WaitAsyncCallsUnderlyingPartitionsLimiter() { var limiterFactory = new TrackingRateLimiterFactory <int>(); using var limiter = PartitionedRateLimiter.Create <string, int>(resource => { return(RateLimitPartition.Get(1, key => limiterFactory.GetLimiter(key))); }); await limiter.WaitAndAcquireAsync(""); Assert.Equal(1, limiterFactory.Limiters.Count); Assert.Equal(1, limiterFactory.Limiters[0].Limiter.WaitAndAcquireAsyncCallCount); }
public async Task Create_BlockingFactoryDoesNotBlockOtherPartitions() { var limiterFactory = new TrackingRateLimiterFactory <int>(); var tcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); var startedTcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); using var limiter = PartitionedRateLimiter.Create <string, int>(resource => { if (resource == "1") { return(RateLimitPartition.Get(1, key => { startedTcs.SetResult(null); // block the factory method Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(10))); return limiterFactory.GetLimiter(key); })); } return(RateLimitPartition.Get(2, key => limiterFactory.GetLimiter(key))); }); var lease = await limiter.WaitAndAcquireAsync("2"); var blockedTask = Task.Run(async() => { await limiter.WaitAndAcquireAsync("1"); }); await startedTcs.Task; // Other partitions aren't blocked await limiter.WaitAndAcquireAsync("2"); // Try to acquire from the blocking limiter, this should wait until the blocking limiter has been resolved and not create a new one var blockedTask2 = Task.Run(async() => { await limiter.WaitAndAcquireAsync("1"); }); // unblock limiter factory tcs.SetResult(null); await blockedTask; await blockedTask2; // Only 2 limiters should have been created Assert.Equal(2, limiterFactory.Limiters.Count); Assert.Equal(2, limiterFactory.Limiters[0].Limiter.WaitAndAcquireAsyncCallCount); Assert.Equal(2, limiterFactory.Limiters[1].Limiter.WaitAndAcquireAsyncCallCount); }
public void Create_DisposeThrowsForFutureMethodCalls() { var limiterFactory = new TrackingRateLimiterFactory <int>(); using var limiter = PartitionedRateLimiter.Create <string, int>(resource => { return(RateLimitPartition.Get(1, key => limiterFactory.GetLimiter(key))); }); limiter.Dispose(); Assert.Throws <ObjectDisposedException>(() => limiter.Acquire("1")); Assert.Equal(0, limiterFactory.Limiters.Count); }
public async Task Create_PartitionIsCached() { var limiterFactory = new TrackingRateLimiterFactory <int>(); using var limiter = PartitionedRateLimiter.Create <string, int>(resource => { return(RateLimitPartition.Create(1, key => limiterFactory.GetLimiter(key))); }); limiter.Acquire(""); await limiter.WaitAsync(""); limiter.Acquire(""); await limiter.WaitAsync(""); Assert.Equal(1, limiterFactory.Limiters.Count); Assert.Equal(2, limiterFactory.Limiters[0].Limiter.AcquireCallCount); Assert.Equal(2, limiterFactory.Limiters[0].Limiter.WaitAsyncCallCount); }