| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- package ratelimit
- import (
- "fmt"
- "math"
- "sync/atomic"
- "time"
- )
- // Limiter is a token bucket: `burst` calls are allowed at once, then one more per `every`.
- // It counts the calls it denies and hands that count to the next allowed call.
- //
- // The budget belongs to the Limiter instance.
- // Every Allow that returns true spends from it, whatever the caller then does.
- //
- // Time follows Go's monotonic clock.
- //
- // Create one Limiter for each thing you want to limit,
- // sharing one between unrelated events lets the noisy one starve the quiet one.
- //
- // Check Allow before doing the work being limited, so a denied call stays cheap:
- //
- // if denied, ok := probeLimiter.Allow(); ok {
- // sendProbe()
- // probesDenied.Inc(int64(denied))
- // }
- //
- // For log lines use logging.RateLimited, which attaches the denied count to the logger as "suppressed".
- type Limiter struct {
- every int64
- slack int64
- tat atomic.Int64
- denied atomic.Uint64
- }
- // maxSpan bounds `burst` * `every` to half of int64, leaving the other half for process uptime so the bucket
- // math can't overflow.
- const maxSpan = math.MaxInt64 / 2
- // New returns a Limiter allowing `burst` calls at once and one more per `every`.
- // A `burst` under 1 is 1, and an `every` of 0 or less never limits.
- // It returns an error if `burst` * `every` is over about 146 years.
- func New(burst int, every time.Duration) (*Limiter, error) {
- l := &Limiter{}
- if every > 0 {
- burst = max(burst, 1)
- if int64(burst) > maxSpan/int64(every) {
- return nil, fmt.Errorf("ratelimit: burst %d * every %v is too long", burst, every)
- }
- l.every = int64(every)
- l.slack = int64(burst-1) * l.every
- }
- return l, nil
- }
- var monoStart = time.Now()
- func monoNow() int64 { return int64(time.Since(monoStart)) }
- // Allow reports whether this call is allowed.
- // If a token is available it spends it and returns the calls denied since the last allowed one, and true.
- // Otherwise it returns 0 and false, and counts the denial.
- // A nil Limiter allows everything.
- func (l *Limiter) Allow() (uint64, bool) {
- if l == nil {
- return 0, true
- }
- return l.allow(monoNow())
- }
- // allow is Allow at a given monotonic time.
- func (l *Limiter) allow(now int64) (uint64, bool) {
- // Concurrent Allow calls read the clock before racing for the bucket, so one can see a time another already spent
- // past and be denied. An unlimited Limiter must never deny, so it skips the bucket.
- if l.every == 0 {
- return 0, true
- }
- for {
- tat := l.tat.Load()
- next := max(tat, now)
- if next-now > l.slack {
- l.denied.Add(1)
- return 0, false
- }
- if l.tat.CompareAndSwap(tat, next+l.every) {
- return l.denied.Swap(0), true
- }
- }
- }
|