Skip to content

Instantly share code, notes, and snippets.

@thehowl
Created July 6, 2016 13:06
Show Gist options
  • Save thehowl/6279435b94e8edfc304e7c92d3a33526 to your computer and use it in GitHub Desktop.
Save thehowl/6279435b94e8edfc304e7c92d3a33526 to your computer and use it in GitHub Desktop.
Example of per-user rate limiting in go
package main
import (
"fmt"
"sync"
"time"
)
func main() {
rl := &specificRateLimiter{
Map: make(map[string]chan struct{}),
Mutex: &sync.RWMutex{},
}
for i := 0; i < 70; i++ {
rl.Request("x", 60)
fmt.Println(time.Now())
}
fmt.Println("Sleeping 10s, should get a burst of 10 afterwards, and 5 rate limited then")
time.Sleep(time.Second * 10)
for i := 0; i < 15; i++ {
rl.Request("x", 80)
fmt.Println(time.Now())
}
fmt.Println("ok. Waiting 1m20s for a 'full' message.")
time.Sleep(time.Minute + time.Second * 20)
}
type specificRateLimiter struct {
Map map[string]chan struct{}
Mutex *sync.RWMutex
}
func (s *specificRateLimiter) Request(u string, perMinute int) {
s.Mutex.RLock()
c, exists := s.Map[u]
s.Mutex.RUnlock()
if !exists {
c = makePrefilledChan(perMinute)
s.Mutex.Lock()
s.Map[u] = c
s.Mutex.Unlock()
<-c
go s.filler(u, perMinute)
}
<-c
}
func (s *specificRateLimiter) filler(el string, perMinute int) {
s.Mutex.RLock()
c := s.Map[el]
s.Mutex.RUnlock()
for {
select {
case c <- struct{}{}:
time.Sleep(time.Minute / time.Duration(perMinute))
default: // c is full
fmt.Println("full")
s.Mutex.Lock()
close(c)
delete(s.Map, el)
s.Mutex.Unlock()
return
}
}
}
func makePrefilledChan(l int) chan struct{} {
c := make(chan struct{}, l)
for i := 0; i < l; i++ {
c <- struct{}{}
}
return c
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment