Skip to content

Instantly share code, notes, and snippets.

@threadedstream
Created January 19, 2025 14:20
Show Gist options
  • Save threadedstream/3c04bfcd8c16fdcd45c18533b3755736 to your computer and use it in GitHub Desktop.
Save threadedstream/3c04bfcd8c16fdcd45c18533b3755736 to your computer and use it in GitHub Desktop.
RateLimiter implemented using SlidingWindow algorithm
package main
import (
"sync"
"sync/atomic"
"time"
)
type RateLimiter struct {
q []*atomic.Int64
commitedReqs, limit int64
mu sync.RWMutex
}
// NewRateLimiter returns fresh instance of RateLimiter
func NewRateLimiter(limit int64) *RateLimiter {
rl := &RateLimiter{
q: make([]*atomic.Int64, 0, 60),
limit: limit,
}
start := time.Now().Unix()
end := start + 60
for start < end {
rl.q = append(rl.q, &atomic.Int64{})
start++
}
go rl.clean()
return rl
}
func (rl *RateLimiter) clean() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
select {
case <-ticker.C:
rl.flushBucket()
}
}
func (rl *RateLimiter) flushBucket() {
rl.mu.Lock()
defer rl.mu.Unlock()
el := rl.q[0]
rl.q = rl.q[1:]
rl.commitedReqs -= el.Load()
rl.q = append(rl.q, el)
}
func (rl *RateLimiter) Limit() bool {
bucket := rl.q[time.Now().Unix()%int64(time.Minute.Seconds())]
rl.mu.RLock()
if rl.commitedReqs >= rl.limit {
rl.mu.RUnlock()
return false
}
rl.mu.RUnlock()
// update number of bucket requests
bucket.Add(1)
rl.mu.Lock()
defer rl.mu.Unlock()
rl.commitedReqs++
return true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment