-
Notifications
You must be signed in to change notification settings - Fork 0
/
limiter.go
72 lines (60 loc) · 1.81 KB
/
limiter.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
package kredis
// From the Kredis code docs:
//
// A limiter is a specialized form of a counter that can be checked whether it
// has been exceeded and is provided fail safe. This means it can be used to
// guard login screens from brute force attacks without denying access in case
// Redis is offline.
type Limiter struct {
Proxy
limit uint64
}
func NewLimiter(key string, limit uint64, opts ...ProxyOption) (*Limiter, error) {
proxy, err := NewProxy(key, opts...)
if err != nil {
return nil, err
}
return &Limiter{Proxy: *proxy, limit: limit}, nil
}
// Similarly to the Poke func, the error be ignored, depending on the use case
func NewLimiterWithDefault(key string, limit uint64, defaultValue int64, opts ...ProxyOption) (l *Limiter, err error) {
proxy, err := NewProxy(key, opts...)
if err != nil {
return
}
l = &Limiter{Proxy: *proxy, limit: limit}
err = proxy.watch(func() error {
return l.increment(defaultValue)
})
return
}
// Depending on the use case, the error can and should be ignored.
//
// You can "poke" yourself above the limit and should use the func IsExceeded()
// to know if the limit is reached.
func (l *Limiter) Poke() error {
return l.increment(1)
}
// It's possible for this func to return false even if we're above the limit
// due to a potential race condition.
func (l *Limiter) IsExceeded() bool {
v, err := l.client.Get(l.ctx, l.key).Uint64()
if err != nil {
// key either does not exist or Redis is down; return false as a failsafe
return false
}
return v >= l.limit
}
func (l *Limiter) Reset() (err error) {
_, err = l.client.Del(l.ctx, l.key).Result()
return
}
func (l *Limiter) increment(by int64) (err error) {
pipe := l.client.TxPipeline()
if l.expiresIn > 0 {
pipe.SetNX(l.ctx, l.key, 0, l.expiresIn)
}
pipe.IncrBy(l.ctx, l.key, 1)
_, err = pipe.Exec(l.ctx)
return
}