token_bucket.go (2387B)
1 package ratelimit 2 3 import ( 4 "sync" 5 ) 6 7 // TokenBucket provides a concurrency safe utility for adding and removing 8 // tokens from the available token bucket. 9 type TokenBucket struct { 10 remainingTokens uint 11 maxCapacity uint 12 minCapacity uint 13 mu sync.Mutex 14 } 15 16 // NewTokenBucket returns an initialized TokenBucket with the capacity 17 // specified. 18 func NewTokenBucket(i uint) *TokenBucket { 19 return &TokenBucket{ 20 remainingTokens: i, 21 maxCapacity: i, 22 minCapacity: 1, 23 } 24 } 25 26 // Retrieve attempts to reduce the available tokens by the amount requested. If 27 // there are tokens available true will be returned along with the number of 28 // available tokens remaining. If amount requested is larger than the available 29 // capacity, false will be returned along with the available capacity. If the 30 // amount is less than the available capacity, the capacity will be reduced by 31 // that amount, and the remaining capacity and true will be returned. 32 func (t *TokenBucket) Retrieve(amount uint) (available uint, retrieved bool) { 33 t.mu.Lock() 34 defer t.mu.Unlock() 35 36 if amount > t.remainingTokens { 37 return t.remainingTokens, false 38 } 39 40 t.remainingTokens -= amount 41 return t.remainingTokens, true 42 } 43 44 // Refund returns the amount of tokens back to the available token bucket, up 45 // to the initial capacity. 46 func (t *TokenBucket) Refund(amount uint) { 47 t.mu.Lock() 48 defer t.mu.Unlock() 49 50 // Capacity cannot exceed max capacity. 51 t.remainingTokens = uintMin(t.remainingTokens+amount, t.maxCapacity) 52 } 53 54 // Capacity returns the maximum capacity of tokens that the bucket could 55 // contain. 56 func (t *TokenBucket) Capacity() uint { 57 t.mu.Lock() 58 defer t.mu.Unlock() 59 60 return t.maxCapacity 61 } 62 63 // Remaining returns the number of tokens that remaining in the bucket. 64 func (t *TokenBucket) Remaining() uint { 65 t.mu.Lock() 66 defer t.mu.Unlock() 67 68 return t.remainingTokens 69 } 70 71 // Resize adjusts the size of the token bucket. Returns the capacity remaining. 72 func (t *TokenBucket) Resize(size uint) uint { 73 t.mu.Lock() 74 defer t.mu.Unlock() 75 76 t.maxCapacity = uintMax(size, t.minCapacity) 77 78 // Capacity needs to be capped at max capacity, if max size reduced. 79 t.remainingTokens = uintMin(t.remainingTokens, t.maxCapacity) 80 81 return t.remainingTokens 82 } 83 84 func uintMin(a, b uint) uint { 85 if a < b { 86 return a 87 } 88 return b 89 } 90 91 func uintMax(a, b uint) uint { 92 if a > b { 93 return a 94 } 95 return b 96 }