github.com/richardwilkes/toolbox@v1.121.0/rate/limiter.go (about) 1 // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved. 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, version 2.0. If a copy of the MPL was not distributed with 5 // this file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 // 7 // This Source Code Form is "Incompatible With Secondary Licenses", as 8 // defined by the Mozilla Public License, version 2.0. 9 10 // Package rate provides rate limiting which supports a hierarchy of limiters, 11 // each capped by their parent. 12 package rate 13 14 import ( 15 "sync" 16 "time" 17 18 "github.com/richardwilkes/toolbox/errs" 19 ) 20 21 type limiter struct { 22 controller *controller 23 parent *limiter 24 children []*limiter 25 last int 26 capacity int 27 used int 28 closed bool 29 } 30 31 type controller struct { 32 root *limiter 33 ticker *time.Ticker 34 done chan bool 35 waiting []*request 36 lock sync.RWMutex 37 } 38 39 type request struct { 40 limiter *limiter 41 done chan error 42 amount int 43 } 44 45 // New creates a new top-level rate limiter. 'capacity' is the number of units (bytes, for example) allowed to be used 46 // in a particular time 'period'. 47 func New(capacity int, period time.Duration) Limiter { 48 c := &controller{ 49 ticker: time.NewTicker(period), 50 done: make(chan bool), 51 } 52 l := &limiter{ 53 controller: c, 54 capacity: capacity, 55 } 56 c.root = l 57 go func() { 58 for { 59 select { 60 case <-c.ticker.C: 61 c.lock.Lock() 62 c.root.reset() 63 remaining := make([]*request, 0, len(c.waiting)) 64 for _, req := range c.waiting { 65 if req.limiter.closed { 66 req.done <- errs.New("Limiter is closed") 67 continue 68 } 69 if req.amount > req.limiter.capacity { 70 req.done <- errs.Newf("Amount (%d) is greater than capacity (%d)", req.amount, req.limiter.capacity) 71 continue 72 } 73 if c.root.capacity-c.root.used > 0 { 74 available := req.limiter.capacity - req.limiter.used 75 p := req.limiter.parent 76 for p != nil { 77 pa := p.capacity - p.used 78 if pa < available { 79 available = pa 80 } 81 p = p.parent 82 } 83 if available >= req.amount { 84 req.limiter.used += req.amount 85 p = req.limiter.parent 86 for p != nil { 87 p.used += req.amount 88 p = p.parent 89 } 90 req.done <- nil 91 continue 92 } 93 } 94 remaining = append(remaining, req) 95 } 96 c.waiting = remaining 97 c.lock.Unlock() 98 case <-c.done: 99 c.ticker.Stop() 100 c.lock.Lock() 101 for _, req := range c.waiting { 102 req.done <- errs.New("Limiter is closed") 103 } 104 c.waiting = make([]*request, 0) 105 c.lock.Unlock() 106 return 107 } 108 } 109 }() 110 return l 111 } 112 113 func (l *limiter) New(capacity int) Limiter { 114 l.controller.lock.Lock() 115 defer l.controller.lock.Unlock() 116 if l.closed { 117 return nil 118 } 119 child := &limiter{ 120 controller: l.controller, 121 parent: l, 122 capacity: capacity, 123 } 124 l.children = append(l.children, child) 125 return child 126 } 127 128 func (l *limiter) Cap(applyParentCaps bool) int { 129 l.controller.lock.RLock() 130 defer l.controller.lock.RUnlock() 131 capacity := l.capacity 132 if applyParentCaps { 133 p := l.parent 134 for p != nil { 135 if p.capacity < capacity { 136 capacity = p.capacity 137 } 138 p = p.parent 139 } 140 } 141 return capacity 142 } 143 144 func (l *limiter) SetCap(capacity int) { 145 l.controller.lock.Lock() 146 l.capacity = capacity 147 l.controller.lock.Unlock() 148 } 149 150 func (l *limiter) LastUsed() int { 151 l.controller.lock.RLock() 152 defer l.controller.lock.RUnlock() 153 return l.last 154 } 155 156 func (l *limiter) Use(amount int) <-chan error { 157 done := make(chan error, 1) 158 if amount < 0 { 159 done <- errs.Newf("Amount (%d) must be positive", amount) 160 return done 161 } 162 if amount == 0 { 163 done <- nil 164 return done 165 } 166 l.controller.lock.Lock() 167 if l.closed { 168 l.controller.lock.Unlock() 169 done <- errs.New("Limiter is closed") 170 return done 171 } 172 if amount > l.capacity { 173 capacity := l.capacity 174 l.controller.lock.Unlock() 175 done <- errs.Newf("Amount (%d) is greater than capacity (%d)", amount, capacity) 176 return done 177 } 178 available := l.capacity - l.used 179 p := l.parent 180 for p != nil { 181 pa := p.capacity - p.used 182 if pa < available { 183 available = pa 184 } 185 p = p.parent 186 } 187 if available >= amount { 188 l.used += amount 189 p = l.parent 190 for p != nil { 191 p.used += amount 192 p = p.parent 193 } 194 l.controller.lock.Unlock() 195 done <- nil 196 return done 197 } 198 l.controller.waiting = append(l.controller.waiting, &request{ 199 limiter: l, 200 amount: amount, 201 done: done, 202 }) 203 l.controller.lock.Unlock() 204 return done 205 } 206 207 func (l *limiter) reset() { 208 l.last = l.used 209 l.used = 0 210 for _, child := range l.children { 211 child.reset() 212 } 213 } 214 215 func (l *limiter) Closed() bool { 216 l.controller.lock.RLock() 217 defer l.controller.lock.RUnlock() 218 return l.closed 219 } 220 221 func (l *limiter) Close() { 222 l.controller.lock.Lock() 223 if !l.closed { 224 l.close() 225 if l.parent == nil { 226 l.controller.done <- true 227 } else { 228 for i, child := range l.parent.children { 229 if child != l { 230 continue 231 } 232 j := len(l.parent.children) - 1 233 l.parent.children[i] = l.parent.children[j] 234 l.parent.children[j] = nil 235 l.parent.children = l.parent.children[:j] 236 break 237 } 238 l.closed = true 239 } 240 } 241 l.controller.lock.Unlock() 242 } 243 244 func (l *limiter) close() { 245 l.closed = true 246 for _, child := range l.children { 247 child.close() 248 } 249 }