Files

50 lines
1004 B
Go

package auth
import (
"sync"
"time"
)
// buckets are token buckets by key: rate a second, up to burst at once. Ones
// full and unused are dropped as more arrive, so a key is not kept for ever.
type buckets struct {
mu sync.Mutex
rate float64
burst float64
by map[string]*bucket
}
type bucket struct {
tokens float64
at time.Time
}
func newBuckets(rate, burst float64) *buckets {
return &buckets{rate: rate, burst: burst, by: map[string]*bucket{}}
}
// allow takes a token for key at now, reporting whether there was one.
func (b *buckets) allow(key string, now time.Time) bool {
b.mu.Lock()
defer b.mu.Unlock()
if len(b.by) > 50_000 {
for k, v := range b.by {
if now.Sub(v.at).Seconds()*b.rate >= b.burst {
delete(b.by, k)
}
}
}
k, ok := b.by[key]
if !ok {
k = &bucket{tokens: b.burst, at: now}
b.by[key] = k
}
k.tokens = min(b.burst, k.tokens+now.Sub(k.at).Seconds()*b.rate)
k.at = now
if k.tokens < 1 {
return false
}
k.tokens--
return true
}