Ejemplo n.º 1
0
        public void DurationFromTokens(double perSecond, double durationSeconds, double tokens)
        {
            // arrange
            var limit = new Limit(perSecond);

            // act
            var durationFromTokens = limit.DurationFromTokens(tokens);

            // assert
            durationFromTokens.ShouldBe(TimeSpan.FromSeconds(durationSeconds));
        }
Ejemplo n.º 2
0
        /// <summary>
        /// wait as an asynchronous operation.
        /// </summary>
        /// <param name="count">The count.</param>
        /// <param name="cancellationToken">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
        /// <exception cref="Exception">rate: Wait(count={count}) exceeds limiter's burst {burst}.</exception>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public async Task WaitAsync(int count, CancellationToken cancellationToken)
        {
            // https://github.com/golang/time/blob/master/rate/rate.go#L226
            int   burst = default;
            Limit limit = default;

            lock (_sync)
            {
                burst = _burst;
                limit = _limit;
            }

            if (count > burst && limit != Limit.Max)
            {
                throw new Exception($"rate: Wait(count={count}) exceeds limiter's burst {burst}");
            }

            // Check if ctx is already cancelled
            cancellationToken.ThrowIfCancellationRequested();

            // Determine wait limit
            var waitLimit = limit.DurationFromTokens(count);

            while (true)
            {
                var now = _clock.UtcNow;
                var r   = ReserveImpl(now, count, waitLimit);
                if (r.Ok)
                {
                    var delay = r.DelayFrom(now);
                    if (delay > TimeSpan.Zero)
                    {
                        await Task.Delay(delay, cancellationToken).ConfigureAwait(false);
                    }

                    return;
                }

                await Task.Delay(waitLimit, cancellationToken).ConfigureAwait(false);
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        ///  reserveN is a helper method for AllowN, ReserveN, and WaitN.
        /// maxFutureReserve specifies the maximum reservation wait duration allowed.
        /// reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN.
        /// </summary>
        /// <param name="now">The now.</param>
        /// <param name="number">The number.</param>
        /// <param name="maxFutureReserve">The maximum future reserve.</param>
        /// <returns>Reservation.</returns>
        private Reservation ReserveImpl(DateTimeOffset now, int number, TimeSpan maxFutureReserve)
        {
            lock (_sync)
            {
                if (_limit == Limit.Max)
                {
                    return(new Reservation(
                               clock: _clock,
                               limiter: this,
                               ok: true,
                               tokens: number,
                               timeToAct: now));
                }

                var(newNow, last, tokens) = Advance(now);
                now = newNow;

                // Calculate the remaining number of tokens resulting from the request.
                tokens -= number;

                // Calculate the wait duration
                TimeSpan waitDuration = default;
                if (tokens < 0)
                {
                    waitDuration = _limit.DurationFromTokens(-tokens);
                }

                // Decide result
                var ok = number <= _burst && waitDuration <= maxFutureReserve;

                // Prepare reservation
                if (ok)
                {
                    var reservation = new Reservation(
                        clock: _clock,
                        limiter: this,
                        ok: true,
                        tokens: number,
                        limit: _limit,
                        timeToAct: now.Add(waitDuration));

                    _last      = newNow;
                    _tokens    = tokens;
                    _lastEvent = reservation.TimeToAct;

                    return(reservation);
                }
                else
                {
                    var reservation = new Reservation(
                        clock: _clock,
                        limiter: this,
                        ok: false,
                        limit: _limit);

                    _last = last;

                    return(reservation);
                }
            }
        }