-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathinmemoryimpl.go
85 lines (70 loc) · 1.76 KB
/
inmemoryimpl.go
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package limitless
import (
"math"
"sync"
"time"
)
// Note:
// This in-memory implementation is not designed for user specific rate limiting
// as huge amount of RAM is required for storing rate limiting info for all users.
// Use case: third party api calls rate limiting
// Refer to redis implementation for flexible rate limiting
// Can't be used in distributed environment
type inMemoryTokenBucket struct {
mu sync.Mutex // lock
capacity int64 // max tokens
rate int // request per second
tokens int64 // available tokens to be used
lastUpdate time.Time // tokens updated at
}
func NewInMemoryTokenBucket(capacity int64, rate int) *inMemoryTokenBucket {
return &inMemoryTokenBucket{
capacity: capacity,
rate: rate,
tokens: capacity,
lastUpdate: time.Now(),
}
}
func (tb *inMemoryTokenBucket) allow() (*bool, error) {
tb.mu.Lock()
defer tb.mu.Unlock()
err := tb.load()
if err != nil {
return nil, err
}
tb.refill()
allow := tb.available()
if allow != nil && !*allow {
return boolPtr(false), nil
}
err = tb.exhaust()
if err != nil {
return nil, err
}
return boolPtr(true), nil
}
func (tb *inMemoryTokenBucket) exhaust() error {
if tb.tokens > 0 {
tb.lastUpdate = time.Now()
tb.tokens--
}
return nil
}
func (tb *inMemoryTokenBucket) available() *bool {
if tb.tokens < 1 {
return boolPtr(false)
}
return nil
}
func (tb *inMemoryTokenBucket) refill() {
elapsed := time.Since(tb.lastUpdate)
tokensToAdd := math.Floor(elapsed.Seconds() * float64(tb.rate))
if tokensToAdd > 0 {
newTokens := tb.tokens + int64(tokensToAdd)
tb.lastUpdate = time.Now()
tb.tokens = int64(math.Min(float64(newTokens), float64(tb.capacity)))
}
}
func (tb *inMemoryTokenBucket) load() error {
return nil
}