-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathsliding_window.go
More file actions
48 lines (42 loc) · 1.3 KB
/
sliding_window.go
File metadata and controls
48 lines (42 loc) · 1.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package ratelimit
import (
"fmt"
"time"
)
type SlidingWindow struct {
identifier string
rate int
windowSize int
store Store
}
// The sliding window algorithm
// given a key like `user-post:avinassh`, rate
// if the doesn't exist then allow, refill and allow
// if key exists
// - check if the current value less than limit, if less allow and increment
// - if the limit has already exceeded, then see if it can be refilled
// NewSlidingWindow returns a new rate limiter which uses the token bucket algorithm.
func NewSlidingWindow(identifier string, rate, windowSize int, store Store) SlidingWindow {
return SlidingWindow{identifier, rate, windowSize, store}
}
func (sw *SlidingWindow) Allow(key string) (bool, error) {
s, err := sw.AllowWithStatus(key)
if err != nil {
return false, err
}
return s.Allowed, nil
}
func (sw *SlidingWindow) AllowWithStatus(key string) (Status, error) {
userKey := fmt.Sprintf("%s:%s", sw.identifier, key)
res, err := sw.store.Inc(userKey, sw.rate, sw.windowSize, int(TimeMillis(timeNow())))
if err != nil {
return Status{}, err
}
timeElapsed := timeNow().Sub(res.LastRefill)
s := Status{
Allowed: res.Allowed,
Remaining: sw.rate - res.Counter,
NextRefresh: timeElapsed + (time.Duration(sw.windowSize) * time.Millisecond),
}
return s, nil
}