-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlimiter.go
More file actions
71 lines (62 loc) · 2.27 KB
/
Copy pathlimiter.go
File metadata and controls
71 lines (62 loc) · 2.27 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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package rate
import "github.com/clipperhouse/ntime"
// Limiter is a rate limiter that can be used to limit the rate of requests to a given key.
type Limiter[TInput any, TKey comparable] struct {
keyFunc KeyFunc[TInput, TKey]
limits []Limit
limitFuncs []LimitFunc[TInput]
buckets bucketMap[TKey]
}
// KeyFunc is a function that takes an input and returns a bucket key.
type KeyFunc[TInput any, TKey comparable] func(input TInput) TKey
// NewLimiter creates a new rate limiter
func NewLimiter[TInput any, TKey comparable](keyFunc KeyFunc[TInput, TKey], limits ...Limit) *Limiter[TInput, TKey] {
return &Limiter[TInput, TKey]{
keyFunc: keyFunc,
limits: limits,
}
}
// NewLimiterFunc creates a new rate limiter with a dynamic limit function. Use this if you
// wish to apply a different limit based on the input, for example by URL path. The LimitFunc
// takes the same input type as the Keyer function.
func NewLimiterFunc[TInput any, TKey comparable](keyFunc KeyFunc[TInput, TKey], limitFuncs ...LimitFunc[TInput]) *Limiter[TInput, TKey] {
return &Limiter[TInput, TKey]{
keyFunc: keyFunc,
limitFuncs: limitFuncs,
}
}
func (r *Limiter[TInput, TKey]) getLimits(input TInput) []Limit {
if len(r.limits) > 0 {
return r.limits
}
// limits and limitFuncs are mutually exclusive.
limits := make([]Limit, len(r.limitFuncs))
for i, limitFunc := range r.limitFuncs {
limits[i] = limitFunc(input)
}
return limits
}
// GC deletes buckets that are full, i.e, buckets for which enough
// time has passed that they are no longer relevant. A full bucket
// and a non-existent bucket have the same semantics.
//
// Without GC, buckets (memory) will grow unbounded.
//
// This can be a moderately expensive operation, depending
// on the number of buckets. If you want a cheaper operation,
// see [Clear].
func (r *Limiter[TInput, TKey]) GC() (deleted int64) {
return r.buckets.gc(ntime.Now)
}
// Clear deletes all buckets. This is semantically
// equivalent to refilling all buckets.
//
// You would use this method for garbage collection
// purposes, as the limiter's memory will grow unbounded
// otherwise.
//
// See also the [GC] method, which is more selective, and
// only deletes buckets that are no longer meaningful.
func (r *Limiter[TInput, TKey]) Clear() {
r.buckets.m.Clear()
}