Documentation ¶
Overview ¶
Package limiters provides general purpose rate limiter implementations.
Index ¶
- Variables
- type Clock
- type ConcurrentBuffer
- type ConcurrentBufferBackend
- type ConcurrentBufferInMemory
- type ConcurrentBufferRedis
- type DistLocker
- type FixedWindow
- type FixedWindowInMemory
- type FixedWindowIncrementer
- type FixedWindowRedis
- type LeakyBucket
- type LeakyBucketEtcd
- type LeakyBucketInMemory
- type LeakyBucketRedis
- type LeakyBucketState
- type LeakyBucketStateBackend
- type LockConsul
- type LockEtcd
- type LockNoop
- type LockZookeeper
- type Logger
- type Registry
- type SlidingWindow
- type SlidingWindowInMemory
- type SlidingWindowIncrementer
- type SlidingWindowRedis
- type StdLogger
- type SystemClock
- type TokenBucket
- type TokenBucketEtcd
- type TokenBucketInMemory
- type TokenBucketRedis
- type TokenBucketState
- type TokenBucketStateBackend
Constants ¶
This section is empty.
Variables ¶
var ( // ErrLimitExhausted is returned by the Limiter in case the number of requests overflows the capacity of a Limiter. ErrLimitExhausted = errors.New("requests limit exhausted") // ErrRaceCondition is returned when there is a race condition while saving a state of a rate limiter. ErrRaceCondition = errors.New("race condition detected") )
Functions ¶
This section is empty.
Types ¶
type ConcurrentBuffer ¶
type ConcurrentBuffer struct {
// contains filtered or unexported fields
}
ConcurrentBuffer implements a limiter that allows concurrent requests up to the given capacity.
func NewConcurrentBuffer ¶
func NewConcurrentBuffer(locker DistLocker, concurrentStateBackend ConcurrentBufferBackend, capacity int64, logger Logger) *ConcurrentBuffer
NewConcurrentBuffer creates a new ConcurrentBuffer instance.
type ConcurrentBufferBackend ¶
type ConcurrentBufferBackend interface { // Add adds the request with the given key to the buffer and returns the total number of requests in it. Add(ctx context.Context, key string) (int64, error) // Remove removes the request from the buffer. Remove(ctx context.Context, key string) error }
ConcurrentBufferBackend wraps the Add and Remove methods.
type ConcurrentBufferInMemory ¶
type ConcurrentBufferInMemory struct {
// contains filtered or unexported fields
}
ConcurrentBufferInMemory is an in-memory implementation of ConcurrentBufferBackend.
func NewConcurrentBufferInMemory ¶
func NewConcurrentBufferInMemory(registry *Registry, ttl time.Duration, clock Clock) *ConcurrentBufferInMemory
NewConcurrentBufferInMemory creates a new instance of ConcurrentBufferInMemory. When the TTL of a key exceeds the key is removed from the buffer. This is needed in case if the process that added that key to the buffer did not call Done() for some reason.
type ConcurrentBufferRedis ¶
type ConcurrentBufferRedis struct {
// contains filtered or unexported fields
}
ConcurrentBufferRedis implements ConcurrentBufferBackend in Redis.
func NewConcurrentBufferRedis ¶
func NewConcurrentBufferRedis(cli *redis.Client, key string, ttl time.Duration, clock Clock) *ConcurrentBufferRedis
NewConcurrentBufferRedis creates a new instance of ConcurrentBufferRedis. When the TTL of a key exceeds the key is removed from the buffer. This is needed in case if the process that added that key to the buffer did not call Done() for some reason.
type DistLocker ¶
type DistLocker interface { // Lock locks the locker. Lock(ctx context.Context) error // Unlock unlocks the previously successfully locked lock. Unlock(ctx context.Context) error }
DistLocker is a context aware distributed locker (interface is similar to sync.Locker).
type FixedWindow ¶
type FixedWindow struct {
// contains filtered or unexported fields
}
FixedWindow implements a Fixed Window rate limiting algorithm.
Simple and memory efficient algorithm that does not need a distributed lock. However it may be lenient when there are many requests around the boundary between 2 adjacent windows.
func NewFixedWindow ¶
func NewFixedWindow(capacity int64, rate time.Duration, fixedWindowIncrementer FixedWindowIncrementer, clock Clock) *FixedWindow
NewFixedWindow creates a new instance of FixedWindow. Capacity is the maximum amount of requests allowed per window. Rate is the window size.
type FixedWindowInMemory ¶
type FixedWindowInMemory struct {
// contains filtered or unexported fields
}
FixedWindowInMemory is an in-memory implementation of FixedWindowIncrementer.
func NewFixedWindowInMemory ¶
func NewFixedWindowInMemory() *FixedWindowInMemory
NewFixedWindowInMemory creates a new instance of FixedWindowInMemory.
type FixedWindowIncrementer ¶
type FixedWindowIncrementer interface { // Increment increments the request counter for the window and returns the counter value. // TTL is the time duration before the next window. Increment(ctx context.Context, window time.Time, ttl time.Duration) (int64, error) }
FixedWindowIncrementer wraps the Increment method.
type FixedWindowRedis ¶
type FixedWindowRedis struct {
// contains filtered or unexported fields
}
FixedWindowRedis implements FixedWindow in Redis.
func NewFixedWindowRedis ¶
func NewFixedWindowRedis(cli *redis.Client, prefix string) *FixedWindowRedis
NewFixedWindowRedis returns a new instance of FixedWindowRedis. Prefix is the key prefix used to store all the keys used in this implementation in Redis.
type LeakyBucket ¶
type LeakyBucket struct {
// contains filtered or unexported fields
}
LeakyBucket implements the https://en.wikipedia.org/wiki/Leaky_bucket#As_a_queue algorithm.
func NewLeakyBucket ¶
func NewLeakyBucket(capacity int64, rate time.Duration, locker DistLocker, leakyBucketStateBackend LeakyBucketStateBackend, clock Clock, logger Logger) *LeakyBucket
NewLeakyBucket creates a new instance of LeakyBucket.
func (*LeakyBucket) Limit ¶
Limit returns the time duration to wait before the request can be processed. If the last request happened earlier than the rate this method returns zero duration. It returns ErrLimitExhausted if the the request overflows the bucket's capacity. In this case the returned duration means how long it would have taken to wait for the request to be processed if the bucket was not overflowed.
type LeakyBucketEtcd ¶
type LeakyBucketEtcd struct {
// contains filtered or unexported fields
}
LeakyBucketEtcd is an etcd implementation of a LeakyBucketStateBackend. See the TokenBucketEtcd description for the details on etcd usage.
func NewLeakyBucketEtcd ¶
func NewLeakyBucketEtcd(cli *clientv3.Client, prefix string, ttl time.Duration, raceCheck bool) *LeakyBucketEtcd
NewLeakyBucketEtcd creates a new LeakyBucketEtcd instance. Prefix is used as an etcd key prefix for all keys stored in etcd by this algorithm. TTL is a TTL of the etcd lease used to store all the keys.
If raceCheck is true and the keys in etcd are modified in between State() and SetState() calls then ErrRaceCondition is returned.
func (*LeakyBucketEtcd) SetState ¶
func (l *LeakyBucketEtcd) SetState(ctx context.Context, state LeakyBucketState) error
SetState updates the state of the bucket in etcd.
func (*LeakyBucketEtcd) State ¶
func (l *LeakyBucketEtcd) State(ctx context.Context) (LeakyBucketState, error)
State gets the bucket's current state from etcd. If there is no state available in etcd then the initial bucket's state is returned.
type LeakyBucketInMemory ¶
type LeakyBucketInMemory struct {
// contains filtered or unexported fields
}
LeakyBucketInMemory is an in-memory implementation of LeakyBucketStateBackend.
func NewLeakyBucketInMemory ¶
func NewLeakyBucketInMemory() *LeakyBucketInMemory
NewLeakyBucketInMemory creates a new instance of LeakyBucketInMemory.
func (*LeakyBucketInMemory) SetState ¶
func (l *LeakyBucketInMemory) SetState(ctx context.Context, state LeakyBucketState) error
SetState sets the current state of the bucket.
func (*LeakyBucketInMemory) State ¶
func (l *LeakyBucketInMemory) State(ctx context.Context) (LeakyBucketState, error)
State gets the current state of the bucket.
type LeakyBucketRedis ¶
type LeakyBucketRedis struct {
// contains filtered or unexported fields
}
LeakyBucketRedis is a Redis implementation of a LeakyBucketStateBackend.
func NewLeakyBucketRedis ¶
func NewLeakyBucketRedis(cli *redis.Client, prefix string, ttl time.Duration, raceCheck bool) *LeakyBucketRedis
NewLeakyBucketRedis creates a new LeakyBucketRedis instance. Prefix is the key prefix used to store all the keys used in this implementation in Redis. TTL is the TTL of the stored keys.
If raceCheck is true and the keys in Redis are modified in between State() and SetState() calls then ErrRaceCondition is returned.
func (*LeakyBucketRedis) SetState ¶
func (t *LeakyBucketRedis) SetState(ctx context.Context, state LeakyBucketState) error
SetState updates the state in Redis. The provided fencing token is checked on the Redis side before saving the keys.
func (*LeakyBucketRedis) State ¶
func (t *LeakyBucketRedis) State(ctx context.Context) (LeakyBucketState, error)
State gets the bucket's state from Redis.
type LeakyBucketState ¶
type LeakyBucketState struct { // Last is the Unix timestamp in nanoseconds of the most recent request. Last int64 }
LeakyBucketState represents the state of a LeakyBucket.
func (LeakyBucketState) IzZero ¶
func (s LeakyBucketState) IzZero() bool
IzZero returns true if the bucket state is zero valued.
type LeakyBucketStateBackend ¶
type LeakyBucketStateBackend interface { // State gets the current state of the LeakyBucket. State(ctx context.Context) (LeakyBucketState, error) // SetState sets (persists) the current state of the LeakyBucket. SetState(ctx context.Context, state LeakyBucketState) error }
LeakyBucketStateBackend interface encapsulates the logic of retrieving and persisting the state of a LeakyBucket.
type LockConsul ¶
type LockConsul struct {
// contains filtered or unexported fields
}
LockConsul is a wrapper around github.com/hashicorp/consul/api.Lock that implements the DistLocker interface.
func NewLockConsul ¶
func NewLockConsul(lock *api.Lock) *LockConsul
NewLockConsul creates a new LockConsul instance.
type LockEtcd ¶
type LockEtcd struct {
// contains filtered or unexported fields
}
LockEtcd implements the DistLocker interface using etcd.
func NewLockEtcd ¶
NewLockEtcd creates a new instance of LockEtcd.
type LockNoop ¶
type LockNoop struct { }
LockNoop is a no-op implementation of the DistLocker interface. It should only be used with the in-memory backends as they are already thread-safe and don't need distributed locks.
type LockZookeeper ¶
type LockZookeeper struct {
// contains filtered or unexported fields
}
LockZookeeper is a wrapper around github.com/samuel/go-zookeeper/zk.Lock that implements the DistLocker interface.
func NewLockZookeeper ¶
func NewLockZookeeper(lock *zk.Lock) *LockZookeeper
NewLockZookeeper creates a new instance of LockZookeeper.
func (*LockZookeeper) Lock ¶
func (l *LockZookeeper) Lock(_ context.Context) error
Lock locks the lock in Zookeeper. TODO: add context aware support once https://github.com/samuel/go-zookeeper/pull/168 is merged.
func (*LockZookeeper) Unlock ¶
func (l *LockZookeeper) Unlock() error
Unlock unlocks the lock in Zookeeper.
type Logger ¶
type Logger interface {
// Log logs the given arguments.
Log(v ...interface{})
}
Logger wraps the Log method for logging.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is a garbage-collectable registry of values.
func (*Registry) DeleteExpired ¶
DeleteExpired deletes expired items from the registry and returns the number of deleted items.
func (*Registry) GetOrCreate ¶
func (r *Registry) GetOrCreate(key string, value func() interface{}, ttl time.Duration, now time.Time) interface{}
GetOrCreate gets an existing value by key and updates its expiration time. If the key lookup fails it creates a new value by calling the provided value closure and puts it on the queue.
type SlidingWindow ¶
type SlidingWindow struct {
// contains filtered or unexported fields
}
SlidingWindow implements a Sliding Window rate limiting algorithm.
It does not require a distributed lock and uses a minimum amount of memory, however it will disallow all the requests in case when a client is flooding the service with requests. It's the client's responsibility to handle the disallowed request and wait before making a new request again.
func NewSlidingWindow ¶
func NewSlidingWindow(capacity int64, rate time.Duration, slidingWindowIncrementer SlidingWindowIncrementer, clock Clock, epsilon float64) *SlidingWindow
NewSlidingWindow creates a new instance of SlidingWindow. Capacity is the maximum amount of requests allowed per window. Rate is the window size. Epsilon is the max-allowed range of difference when comparing the current weighted number of requests with capacity.
type SlidingWindowInMemory ¶
type SlidingWindowInMemory struct {
// contains filtered or unexported fields
}
SlidingWindowInMemory is an in-memory implementation of SlidingWindowIncrementer.
func NewSlidingWindowInMemory ¶
func NewSlidingWindowInMemory() *SlidingWindowInMemory
NewSlidingWindowInMemory creates a new instance of SlidingWindowInMemory.
type SlidingWindowIncrementer ¶
type SlidingWindowIncrementer interface { // Increment increments the request counter for the current window and returns the counter values for the previous // window and the current one. // TTL is the time duration before the next window. Increment(ctx context.Context, prev, curr time.Time, ttl time.Duration) (prevCount, currCount int64, err error) }
SlidingWindowIncrementer wraps the Increment method.
type SlidingWindowRedis ¶
type SlidingWindowRedis struct {
// contains filtered or unexported fields
}
SlidingWindowRedis implements SlidingWindow in Redis.
func NewSlidingWindowRedis ¶
func NewSlidingWindowRedis(cli *redis.Client, prefix string) *SlidingWindowRedis
NewSlidingWindowRedis creates a new instance of SlidingWindowRedis.
type StdLogger ¶
type StdLogger struct{}
StdLogger implements the Logger interface.
func NewStdLogger ¶
func NewStdLogger() *StdLogger
NewStdLogger creates a new instance of StdLogger.
type SystemClock ¶
type SystemClock struct { }
SystemClock implements the Clock interface by using the real system clock.
func NewSystemClock ¶
func NewSystemClock() *SystemClock
NewSystemClock creates a new instance of SystemClock.
func (*SystemClock) Now ¶
func (c *SystemClock) Now() time.Time
Now returns the current system time.
func (*SystemClock) Sleep ¶
func (c *SystemClock) Sleep(d time.Duration)
Sleep blocks (sleeps) for the given duration.
type TokenBucket ¶
type TokenBucket struct {
// contains filtered or unexported fields
}
TokenBucket implements the https://en.wikipedia.org/wiki/Token_bucket algorithm.
func NewTokenBucket ¶
func NewTokenBucket(capacity int64, refillRate time.Duration, locker DistLocker, tokenBucketStateBackend TokenBucketStateBackend, clock Clock, logger Logger) *TokenBucket
NewTokenBucket creates a new instance of TokenBucket.
func (*TokenBucket) Take ¶
Take takes tokens from the bucket.
It returns a zero duration and a nil error if the bucket has sufficient amount of tokens.
It returns ErrLimitExhausted if the amount of available tokens is less than requested. In this case the returned duration is the amount of time to wait to retry the request.
type TokenBucketEtcd ¶
type TokenBucketEtcd struct {
// contains filtered or unexported fields
}
TokenBucketEtcd is an etcd implementation of a TokenBucketStateBackend.
See https://github.com/etcd-io/etcd/blob/master/Documentation/learning/data_model.md
etcd is designed to reliably store infrequently updated data, thus it should only be used for the API endpoints which are accessed less frequently than it can be processed by the rate limiter.
Aggressive compaction and defragmentation has to be enabled in etcd to prevent the size of the storage to grow indefinitely: every change of the state of the bucket (every access) will create a new revision in etcd.
It probably makes it impractical for the high load cases, but can be used to reliably and precisely rate limit an access to the business critical endpoints where each access must be reliably logged.
func NewTokenBucketEtcd ¶
func NewTokenBucketEtcd(cli *clientv3.Client, prefix string, ttl time.Duration, raceCheck bool) *TokenBucketEtcd
NewTokenBucketEtcd creates a new TokenBucketEtcd instance. Prefix is used as an etcd key prefix for all keys stored in etcd by this algorithm. TTL is a TTL of the etcd lease in seconds used to store all the keys: all the keys are automatically deleted after the TTL expires.
If raceCheck is true and the keys in etcd are modified in between State() and SetState() calls then ErrRaceCondition is returned. It does not add any significant overhead as it can be trivially checked on etcd side before updating the keys.
func (*TokenBucketEtcd) SetState ¶
func (t *TokenBucketEtcd) SetState(ctx context.Context, state TokenBucketState) error
SetState updates the state of the bucket.
func (*TokenBucketEtcd) State ¶
func (t *TokenBucketEtcd) State(ctx context.Context) (TokenBucketState, error)
State gets the bucket's current state from etcd. If there is no state available in etcd then the initial bucket's state is returned.
type TokenBucketInMemory ¶
type TokenBucketInMemory struct {
// contains filtered or unexported fields
}
TokenBucketInMemory is an in-memory implementation of TokenBucketStateBackend.
The state is not shared nor persisted so it won't survive restarts or failures. Due to the local nature of the state the rate at which some endpoints are accessed can't be reliably predicted or limited.
Although it can be used as a global rate limiter with a round-robin load-balancer.
func NewTokenBucketInMemory ¶
func NewTokenBucketInMemory() *TokenBucketInMemory
NewTokenBucketInMemory creates a new instance of TokenBucketInMemory.
func (*TokenBucketInMemory) SetState ¶
func (t *TokenBucketInMemory) SetState(ctx context.Context, state TokenBucketState) error
SetState sets the current bucket's state.
func (*TokenBucketInMemory) State ¶
func (t *TokenBucketInMemory) State(ctx context.Context) (TokenBucketState, error)
State returns the current bucket's state.
type TokenBucketRedis ¶
type TokenBucketRedis struct {
// contains filtered or unexported fields
}
TokenBucketRedis is a Redis implementation of a TokenBucketStateBackend.
Redis is an in-memory key-value data storage which also supports persistence. It is a better choice for high load cases than etcd as it does not keep old values of the keys thus does not need the compaction/defragmentation.
Although depending on a persistence and a cluster configuration some data might be lost in case of a failure resulting in an under-limiting the accesses to the service.
func NewTokenBucketRedis ¶
func NewTokenBucketRedis(cli *redis.Client, prefix string, ttl time.Duration, raceCheck bool) *TokenBucketRedis
NewTokenBucketRedis creates a new TokenBucketRedis instance. Prefix is the key prefix used to store all the keys used in this implementation in Redis. TTL is the TTL of the stored keys.
If raceCheck is true and the keys in Redis are modified in between State() and SetState() calls then ErrRaceCondition is returned. This adds an extra overhead since a Lua script has to be executed on the Redis side which locks the entire database.
func (*TokenBucketRedis) SetState ¶
func (t *TokenBucketRedis) SetState(ctx context.Context, state TokenBucketState) error
SetState updates the state in Redis.
func (*TokenBucketRedis) State ¶
func (t *TokenBucketRedis) State(ctx context.Context) (TokenBucketState, error)
State gets the bucket's state from Redis.
type TokenBucketState ¶
type TokenBucketState struct { // Last is the last time the state was updated (Unix timestamp in nanoseconds). Last int64 // Available is the number of available tokens in the bucket. Available int64 }
TokenBucketState represents a state of a token bucket.
type TokenBucketStateBackend ¶
type TokenBucketStateBackend interface { // State gets the current state of the TokenBucket. State(ctx context.Context) (TokenBucketState, error) // SetState sets (persists) the current state of the TokenBucket. SetState(ctx context.Context, state TokenBucketState) error }
TokenBucketStateBackend interface encapsulates the logic of retrieving and persisting the state of a TokenBucket.