public static async Task <ITokenBucket> GetTokenBucketAsync(CustomerThrottle customerThrottle)
 {
     return(await TokenBuckets.BucketWithFixedIntervalRefillStrategy(
                capacity : (long)customerThrottle.MaximumRequestsPerSecond,
                refillTokens : (long)customerThrottle.MaximumRequestsPerSecond,
                period : oneSecond).ConfigureAwait(false));
 }
        private async Task <ITokenBucket> CreateAndStoreTokenBucketContext(CustomerThrottle customerThrottle)
        {
            object objNewTokenBucket = await TokenBucketFactory.GetTokenBucketAsync(customerThrottle).ConfigureAwait(false);

            CustomerThrottleContext newThrottleContext = CustomerThrottleContext.Create(
                customerThrottle: customerThrottle,
                tokenBucket: objNewTokenBucket);

            contextDict.AddOrUpdate(customerThrottle.CustomerIdentityKey, newThrottleContext,
                                    updateValueFactory: (factoryKey, factoryValue) =>
            {
                return(newThrottleContext);
            });

            return((ITokenBucket)objNewTokenBucket);
        }
        /// <summary>
        /// Executes the delegate function on a throttled basis.
        ///
        /// When this overload is called and no CancellationToken was passed at instance construction,
        ///  then the internal cancellation logic will use the CancellationToken passed to this method, and the same CancellationToken will be passed to the delegate.
        /// When this overload is called and a CancellationToken was passed at instance construction,
        ///  then the internal cancellation logic will use the instance CancellationToken, and the CancellationToken passed to this overload will be passed to the delegate.
        /// </summary>
        /// <param name="customerThrottle">Determines which throttle-rate to use. If you pass a customerThrottle with an ID that hasn't been seen previously by the instance,
        /// then a new underlying TokenBucket is created. If you pass a customerThrottle with an ID that has been seen previously by the instance, then you'll get the
        /// underlying TokenBucket already associated with that ID.
        /// If you want to change the throttling rate associated with a particular ID, just pass in a new customerThrottle object with the new maximumRequestsPerSecond property
        /// value and the same ID.</param>
        /// <param name="request">The request object (of type TRequest) to pass to the delegate.</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task <TResponse> ExecuteThrottledAsync(CustomerThrottle customerThrottle, TRequest request, CancellationToken cancellationToken)
        {
            CheckIfThisInstanceIsDisposed_ForPlacementAtTopOfPublicMethods();

            if (customerThrottle == null)
            {
                throw new ArgumentNullException(nameof(customerThrottle));
            }

            customerThrottle.ValidateArguments();

            if (this.cancellationToken.IsNotNone())
            {
                return(await ExecuteThrottledAsync(customerThrottle, request, this.cancellationToken, cancellationToken).ConfigureAwait(false));
            }
            else
            {
                return(await ExecuteThrottledAsync(customerThrottle, request, cancellationToken, cancellationToken).ConfigureAwait(false));
            }
        }
        private async Task <TResponse> ExecuteThrottledAsync(CustomerThrottle customerThrottle, TRequest request, CancellationToken cancellationTokenToCheckInternally,
                                                             CancellationToken cancellationTokenToPassToDelegate)
        {
            ITokenBucket tokenBucket = null;

            if (customerThrottle.MaximumRequestsPerSecond > 0)
            {
                tokenBucket = await RetrieveMatchingTokenBucketContextOrMakeNew(customerThrottle).ConfigureAwait(false);

                if (cancellationTokenToCheckInternally.IsNotNone())
                {
                    cancellationTokenToCheckInternally.ThrowIfCancellationRequested();
                }

                await tokenBucket.WaitConsumeAsync(1, cancellationTokenToCheckInternally).ConfigureAwait(false);
            }

            if (cancellationTokenToCheckInternally.IsNotNone())
            {
                cancellationTokenToCheckInternally.ThrowIfCancellationRequested();
            }

            return(await delegateFunction(request, cancellationTokenToPassToDelegate).ConfigureAwait(false));
        }
        private async Task <ITokenBucket> RetrieveMatchingTokenBucketContextOrMakeNew(CustomerThrottle customerThrottle)
        {
            ITokenBucket tokenBucket;

            if (contextDict.TryGetValue(customerThrottle.CustomerIdentityKey, out var storedThrottle))
            {
                if (!storedThrottle.CustomerThrottle.Equals(customerThrottle))
                {
                    tokenBucket = await CreateAndStoreTokenBucketContext(customerThrottle).ConfigureAwait(false);
                }
                else // if storedThrottle.CustomerThrottle.Equals(customerThrottle)
                {
                    tokenBucket = (ITokenBucket)storedThrottle.TokenBucket;
                }
            }
            else // (! contextDict.TryGetValue(customerThrottle.CustomerIdentityKey, out var storedThrottle))
            {
                tokenBucket = await CreateAndStoreTokenBucketContext(customerThrottle).ConfigureAwait(false);
            }

            return(tokenBucket);
        }