-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathattribute_limiter.go
190 lines (166 loc) · 4.75 KB
/
attribute_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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package ratelimiter
import (
"fmt"
"sync"
"time"
)
// AttributeMap is a custom map type of string key and Limiter instance as value
type AttributeMap map[string]Limiter
// AttributeBasedLimiter is an instance that can manage multiple rate limiter instances
// with different configutations.
type AttributeBasedLimiter struct {
attributeMap AttributeMap
m sync.Mutex
syncMode bool
}
// HasKey check if AttributeBasedLimiter has a limiter for the key.
//
// Parameters:
//
// 1. key: a unique key string, example: IP address, token, uuid etc
//
// Returns a boolean flag, if true, the key is already present, false otherwise.
func (a *AttributeBasedLimiter) HasKey(key string) bool {
a.m.Lock()
_, ok := a.attributeMap[key]
a.m.Unlock()
return ok
}
// CreateNewKey create a new key-limiter assiociation.
//
// Parameters:
//
// 1. key: a unique key string, example: IP address, token, uuid etc
//
// 2. limit: The number of tasks to be allowd
//
// 3. size: duration
//
// Returns error if the key already exists.
func (a *AttributeBasedLimiter) CreateNewKey(key string, limit uint64, size time.Duration) error {
a.m.Lock()
defer a.m.Unlock()
return a.createNewKey(key, limit, size)
}
func (a *AttributeBasedLimiter) createNewKey(key string, limit uint64, size time.Duration) error {
if _, ok := a.attributeMap[key]; ok {
return fmt.Errorf(
"key %s is already defined", key,
)
}
// create a new entry:
if !a.syncMode {
a.attributeMap[key] = NewDefaultLimiter(limit, size)
} else {
a.attributeMap[key] = NewSyncLimiter(limit, size)
}
return nil
}
// HasOrCreateKey check if AttributeBasedLimiter has a limiter for the key.
// Create a new key-limiter assiociation if the key not exists.
//
// Parameters:
//
// 1. key: a unique key string, example: IP address, token, uuid etc
//
// 2. limit: The number of tasks to be allowd
//
// 3. size: duration
//
// Return true if the key exists or is created successfully.
func (a *AttributeBasedLimiter) HasOrCreateKey(key string, limit uint64, size time.Duration) bool {
a.m.Lock()
defer a.m.Unlock()
if _, ok := a.attributeMap[key]; ok {
return true
}
if err := a.createNewKey(key, limit, size); err == nil {
return true
}
return false
}
// ShouldAllow makes decison whether n tasks can be allowed or not.
//
// Parameters:
//
// key: a unique key string, example: IP address, token, uuid etc
//
// n: number of tasks to be processed, set this as 1 for a single task.
// (Example: An HTTP request)
//
// Returns (bool, error).
// (false, error) when limiter is inactive (or it is killed) or key is not present.
// (true/false, nil) if key exists and n tasks can be allowed or not.
func (a *AttributeBasedLimiter) ShouldAllow(key string, n uint64) (bool, error) {
a.m.Lock()
defer a.m.Unlock()
limiter, ok := a.attributeMap[key]
if ok {
return limiter.ShouldAllow(n)
}
return false, fmt.Errorf("key %s not found", key)
}
// MustShouldAllow makes decison whether n tasks can be allowed or not.
//
// Parameters:
//
// key: a unique key string, example: IP address, token, uuid etc
//
// n: number of tasks to be processed, set this as 1 for a single task.
// (Example: An HTTP request)
//
// limit: The number of tasks to be allowd
//
// size: duration
//
// Returns bool.
// (false) when limiter is inactive (or it is killed) or n tasks can be not allowed.
// (true) when n tasks can be allowed or new key-limiter.
func (a *AttributeBasedLimiter) MustShouldAllow(key string, n uint64, limit uint64, size time.Duration) bool {
a.m.Lock()
defer a.m.Unlock()
if limiter, ok := a.attributeMap[key]; ok {
allowed, err := limiter.ShouldAllow(n)
return allowed && err == nil
}
err := a.createNewKey(key, limit, size)
if err != nil {
return err == nil
}
// check ratelimiter on newly created key:
limiter := a.attributeMap[key]
allowed, err := limiter.ShouldAllow(n)
return allowed && err == nil
}
// DeleteKey remove the key and kill its underlying limiter.
//
// Parameters:
//
// 1.key: a unique key string, example: IP address, token, uuid etc
//
// Returns an error if the key is not present.
func (a *AttributeBasedLimiter) DeleteKey(key string) error {
a.m.Lock()
defer a.m.Unlock()
if limiter, ok := a.attributeMap[key]; ok {
err := limiter.Kill()
if err != nil {
return err
}
delete(a.attributeMap, key)
return nil
}
return fmt.Errorf("key %s not found", key)
}
// NewAttributeBasedLimiter creates an instance of AttributeBasedLimiter and returns it's pointer.
//
// Parameters:
//
// 1. backgroundSliding: if set to true, DefaultLimiter will be used as an underlying limiter,
// else, SyncLimiter will be used.
func NewAttributeBasedLimiter(backgroundSliding bool) *AttributeBasedLimiter {
return &AttributeBasedLimiter{
attributeMap: make(AttributeMap),
syncMode: !backgroundSliding,
}
}