github.com/andy2046/gopie@v0.7.0/pkg/ratelimit/ratelimit.go (about)

     1  // Package ratelimit implements a rate limiter.
     2  package ratelimit
     3  
     4  import (
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  )
     9  
    10  // Limit defines the maximum number of requests per second.
    11  type Limit float64
    12  
    13  // Every converts a time interval between requests to a Limit.
    14  func Every(interval time.Duration) Limit {
    15  	if interval <= 0 {
    16  		panic("ratelimit: invalid time interval for Every")
    17  	}
    18  	return 1 / Limit(interval.Seconds())
    19  }
    20  
    21  // Limiter implements a token bucket limiter at rate `r` tokens per second with burst size of `b` tokens.
    22  type Limiter struct {
    23  	limit  Limit
    24  	burst  int
    25  	mu     sync.Mutex
    26  	tokens float64
    27  	// last is the last time the limiter's tokens got updated
    28  	last time.Time
    29  	// lastRequest is the latest time of a request
    30  	lastRequest time.Time
    31  }
    32  
    33  // Limit returns the Limiter's rate.
    34  func (l *Limiter) Limit() Limit {
    35  	return l.limit
    36  }
    37  
    38  // Burst returns the Limiter's burst size.
    39  func (l *Limiter) Burst() int {
    40  	return l.burst
    41  }
    42  
    43  // New returns a new Limiter at rate `r` tokens per second with burst of `b` tokens.
    44  func New(r Limit, b int) *Limiter {
    45  	return &Limiter{
    46  		limit: r,
    47  		burst: b,
    48  	}
    49  }
    50  
    51  // Allow is the shortcut for AllowN(time.Now(), 1).
    52  func (l *Limiter) Allow() bool {
    53  	return l.AllowN(time.Now(), 1)
    54  }
    55  
    56  // AllowN checks whether `n` requests may happen at time `now`.
    57  func (l *Limiter) AllowN(now time.Time, n int) bool {
    58  	return l.reserveN(now, n, 0).ok
    59  }
    60  
    61  // Wait is the shortcut for WaitN(time.Now(), 1).
    62  func (l *Limiter) Wait() (time.Duration, error) {
    63  	return l.WaitN(time.Now(), 1)
    64  }
    65  
    66  // WaitN calculates the time duration to wait before `n` requests may happen at time `now`.
    67  func (l *Limiter) WaitN(now time.Time, n int) (time.Duration, error) {
    68  	return l.waitN(now, n)
    69  }
    70  
    71  func (l *Limiter) waitN(now time.Time, n int) (time.Duration, error) {
    72  	if n > l.burst {
    73  		return 0, fmt.Errorf("ratelimit: WaitN %d exceeds limiter's burst %d", n, l.burst)
    74  	}
    75  
    76  	_, _, tokens := l.adjust(now)
    77  
    78  	// calculate the remaining number of tokens resulting from the request.
    79  	tokens -= float64(n)
    80  
    81  	var waitDuration time.Duration
    82  	if tokens < 0 {
    83  		waitDuration = l.limit.durationFromTokens(-tokens)
    84  	}
    85  
    86  	maxWaitDuration := l.limit.durationFromTokens(float64(n))
    87  	ok := n <= l.burst && waitDuration <= maxWaitDuration
    88  
    89  	if ok {
    90  		return waitDuration, nil
    91  	}
    92  	return 0, fmt.Errorf("ratelimit: WaitN %d exceeds maximum wait duration", n)
    93  }
    94  
    95  type reservation struct {
    96  	ok        bool
    97  	tokens    int
    98  	timeToAct time.Time
    99  }
   100  
   101  func (l *Limiter) reserveN(now time.Time, n int, maxWaitDuration time.Duration) reservation {
   102  	l.mu.Lock()
   103  
   104  	noow, last, tokens := l.adjust(now)
   105  
   106  	// calculate the remaining number of tokens resulting from the request.
   107  	tokens -= float64(n)
   108  
   109  	var waitDuration time.Duration
   110  	if tokens < 0 {
   111  		waitDuration = l.limit.durationFromTokens(-tokens)
   112  	}
   113  
   114  	ok := n <= l.burst && waitDuration <= maxWaitDuration
   115  
   116  	r := reservation{
   117  		ok: ok,
   118  	}
   119  
   120  	if ok {
   121  		r.tokens = n
   122  		r.timeToAct = noow.Add(waitDuration)
   123  		l.last = noow
   124  		l.tokens = tokens
   125  		l.lastRequest = r.timeToAct
   126  	} else {
   127  		l.last = last
   128  	}
   129  
   130  	l.mu.Unlock()
   131  	return r
   132  }
   133  
   134  // adjust calculates the updated state for Limiter resulting from the passage of time.
   135  func (l *Limiter) adjust(now time.Time) (newNow, newLast time.Time, newTokens float64) {
   136  	last := l.last
   137  	if now.Before(last) {
   138  		last = now
   139  	}
   140  
   141  	maxElapsed := l.limit.durationFromTokens(float64(l.burst) - l.tokens)
   142  	elapsed := now.Sub(last)
   143  	if elapsed > maxElapsed {
   144  		elapsed = maxElapsed
   145  	}
   146  
   147  	delta := l.limit.tokensFromDuration(elapsed)
   148  	tokens := l.tokens + delta
   149  	if burst := float64(l.burst); tokens > burst {
   150  		tokens = burst
   151  	}
   152  
   153  	return now, last, tokens
   154  }
   155  
   156  func (lmt Limit) durationFromTokens(tokens float64) time.Duration {
   157  	seconds := tokens / float64(lmt)
   158  	return time.Nanosecond * time.Duration(1e9*seconds)
   159  }
   160  
   161  func (lmt Limit) tokensFromDuration(d time.Duration) float64 {
   162  	return d.Seconds() * float64(lmt)
   163  }