public virtual async Task <RateLimitCounter> ProcessRequestAsync(ClientRequestIdentity requestIdentity, RateLimitRule rule, CancellationToken cancellationToken = default)
        {
            var counter = new RateLimitCounter
            {
                Timestamp = DateTime.UtcNow,
                Count     = 1
            };

            var counterId = BuildCounterKey(requestIdentity, rule);

            // serial reads and writes on same key
            using (await AsyncKeyLock.WriterLockAsync(counterId).ConfigureAwait(false))
            {
                var entry = await _counterStore.GetAsync(counterId, cancellationToken);

                // entry has not expired
                if (entry.HasValue && entry.Value.Timestamp + rule.PeriodTimespan.Value >= DateTime.UtcNow)
                {
                    // increment request count
                    var totalCount = entry.Value.Count + _config.RateIncrementer?.Invoke() ?? 1;

                    // deep copy
                    counter = new RateLimitCounter
                    {
                        Timestamp = entry.Value.Timestamp,
                        Count     = totalCount
                    };
                }

                // stores: id (string) - timestamp (datetime) - total_requests (long)
                await _counterStore.SetAsync(counterId, counter, rule.PeriodTimespan.Value, cancellationToken);
            }

            return(counter);
        }
Пример #2
0
        public async Task <RateLimitCounter> IncrementAsync(string counterId, TimeSpan interval, Func <double> RateIncrementer = null)
        {
            using (await AsyncLock.WriterLockAsync(counterId).ConfigureAwait(false))
            {
                // Determine current interval and its start
                var now            = DateTime.UtcNow;
                var intervalNumber = now.Ticks / interval.Ticks;
                var intervalStart  = new DateTime(intervalNumber * interval.Ticks, DateTimeKind.Utc);
                var count          = await _cache.GetOrCreateAsync(counterId, entry => {
                    // Set the cache entry expiry to the end of the current interval
                    var intervalEnd = intervalStart + interval;
                    entry.SetAbsoluteExpiration(intervalEnd - now);
                    return(Task.FromResult(0D));
                });

                count += RateIncrementer?.Invoke() ?? 1D;
                _cache.Set(counterId, count);
                return(new RateLimitCounter
                {
                    Count = count,
                    Timestamp = intervalStart
                });
            }
        }