03-rate-limiter: implement token bucket Limiter with step-by-step comments
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbnJj9ZqEx9nUvzkjdwfMq
This commit is contained in:
+30
-1
@@ -17,22 +17,51 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Limiter struct {
|
type Limiter struct {
|
||||||
|
|
||||||
// TODO: fields (mutex, current tokens, max, rate, last refill time)
|
// TODO: fields (mutex, current tokens, max, rate, last refill time)
|
||||||
|
mu sync.Mutex
|
||||||
|
tokens float64
|
||||||
|
max float64
|
||||||
|
rate float64
|
||||||
|
last time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLimiter returns a limiter allowing `rate` ops/sec with bursts up to `burst`.
|
// NewLimiter returns a limiter allowing `rate` ops/sec with bursts up to `burst`.
|
||||||
// TODO: implement me.
|
// TODO: implement me.
|
||||||
func NewLimiter(rate float64, burst int) *Limiter {
|
func NewLimiter(rate float64, burst int) *Limiter {
|
||||||
return &Limiter{}
|
return &Limiter{tokens: float64(burst), rate: rate, max: float64(burst), last: time.Now()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow reports whether one operation may proceed now.
|
// Allow reports whether one operation may proceed now.
|
||||||
// TODO: implement me.
|
// TODO: implement me.
|
||||||
func (l *Limiter) Allow() bool {
|
func (l *Limiter) Allow() bool {
|
||||||
|
/*
|
||||||
|
1. Lock — refill + consume is a read-modify-write on two fields;
|
||||||
|
without the lock two goroutines can both see 1 token and both take it.
|
||||||
|
2. Lazy refill — tokens earned since the last call = elapsed * rate.
|
||||||
|
float64 so fractions (0.6 of a token) carry over instead of truncating to 0.
|
||||||
|
3. Cap at max — idle time must not build unlimited credit (burst is a ceiling).
|
||||||
|
4. Update last on EVERY call, granted or not — otherwise the same interval
|
||||||
|
is credited again on the next call.
|
||||||
|
5. If at least one whole token is available, take it and allow; else deny.
|
||||||
|
Non-blocking: never sleeps or waits.
|
||||||
|
*/
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
l.tokens += l.rate * time.Since(l.last).Seconds()
|
||||||
|
if l.tokens > l.max {
|
||||||
|
l.tokens = l.max
|
||||||
|
}
|
||||||
|
l.last = time.Now()
|
||||||
|
if l.tokens >= 1 {
|
||||||
|
l.tokens--
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user