Sliding Window Limiter
Track request timestamps in a sliding window to enforce a precise per-interval quota, with an injectable clock for tests.
Code
package main
import (
"fmt"
"sort"
"sync"
"time"
)
type SlidingWindowLimiter struct {
mu sync.Mutex
limit int
window time.Duration
now func() time.Time
events []time.Time
}
func NewSlidingWindowLimiter(limit int, window time.Duration, now func() time.Time) *SlidingWindowLimiter {
if limit <= 0 || window <= 0 {
panic("limit and window must be positive")
}
if now == nil {
now = time.Now
}
return &SlidingWindowLimiter{limit: limit, window: window, now: now}
}
func (l *SlidingWindowLimiter) prune(current time.Time) []time.Time {
cutoff := current.Add(-l.window)
first := sort.Search(len(l.events), func(i int) bool {
return l.events[i].After(cutoff)
})
l.events = append(l.events[:0], l.events[first:]...)
return l.events
}
func (l *SlidingWindowLimiter) Allow() bool {
l.mu.Lock()
defer l.mu.Unlock()
current := l.now()
active := l.prune(current)
if len(active) >= l.limit {
return false
}
l.events = append(active, current)
return true
}
func (l *SlidingWindowLimiter) Remaining() int {
l.mu.Lock()
defer l.mu.Unlock()
active := l.prune(l.now())
if remaining := l.limit - len(active); remaining > 0 {
return remaining
}
return 0
}
func main() {
clock := time.Date(2026, 10, 21, 12, 0, 0, 0, time.UTC)
now := func() time.Time { return clock }
limiter := NewSlidingWindowLimiter(3, time.Second, now)
for i := 0; i < 4; i++ {
clock = clock.Add(100 * time.Millisecond)
fmt.Printf("t=%dms allow=%v remaining=%d\n", (i+1)*100, limiter.Allow(), limiter.Remaining())
}
clock = clock.Add(700 * time.Millisecond)
fmt.Println("after window slides:", limiter.Allow())
}Report a problem with this exampleOpens a pre-filled issue on GitHub.