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);
        }
示例#10
0
        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);
        }