Documentation
¶
Overview ¶
Package rate provides rate limiting primitives for controlling request flow, preventing service overload, and implementing backoff strategies. It includes several rate limiters like token buckets and AIMD (Additive Increase Multiplicative Decrease) algorithms with thread-safe operations.
Index ¶
- type AIMDTokenBucketLimiter
- func (a *AIMDTokenBucketLimiter) CheckToken(id []byte) bool
- func (a *AIMDTokenBucketLimiter) CheckTokens(id []byte, n uint8) bool
- func (a *AIMDTokenBucketLimiter) DecreaseRate(id []byte) float64
- func (a *AIMDTokenBucketLimiter) IncreaseRate(id []byte) float64
- func (a *AIMDTokenBucketLimiter) Rate(id []byte) float64
- func (a *AIMDTokenBucketLimiter) TakeToken(id []byte) bool
- func (a *AIMDTokenBucketLimiter) TakeTokens(id []byte, n uint8) bool
- type Limiter
- type RotatingTokenBucketLimiter
- func (r *RotatingTokenBucketLimiter) CheckToken(id []byte) bool
- func (r *RotatingTokenBucketLimiter) CheckTokens(id []byte, n uint8) bool
- func (r *RotatingTokenBucketLimiter) RotationInterval() time.Duration
- func (r *RotatingTokenBucketLimiter) TakeToken(id []byte) bool
- func (r *RotatingTokenBucketLimiter) TakeTokens(id []byte, n uint8) bool
- type TokenBucketLimiter
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AIMDTokenBucketLimiter ¶
type AIMDTokenBucketLimiter struct {
// contains filtered or unexported fields
}
AIMDTokenBucketLimiter wraps a TokenBucketLimiter to implement Additive Increase Multiplicative Decrease (AIMD) rate limiting. This provides a dynamic rate limiting strategy that gradually increases token rates during successful operations and quickly reduces rates when encountering failures or congestion. This is similar to the congestion control algorithm used in TCP.
func NewAIMDTokenBucketLimiter ¶
func NewAIMDTokenBucketLimiter( numBuckets uint, burstCapacity uint8, rateMin float64, rateMax float64, rateInit float64, rateAdditiveIncrease float64, rateMultiplicativeDecrease float64, rateUnit time.Duration, ) (*AIMDTokenBucketLimiter, error)
NewAIMDTokenBucketLimiter creates a new AIMD token bucket limiter with the given parameters:
- numBuckets: number of token buckets (automatically rounded up to the nearest power of two if not already a power of two, for efficient hashing)
- burstCapacity: max number of tokens that can be consumed at once
- rateMin: minimum token refill rate (must be positive and finite)
- rateMax: maximum token refill rate (must be greater than or equal to rateMin)
- rateInit: initial token refill rate (must be between rateMin and rateMax)
- rateAdditiveIncrease: amount to increase rate by on success (typically a small value)
- rateMultiplicativeDecrease: factor to decrease rate by on failure (typically a value like 2.0 meaning "divide by 2")
- rateUnit: time unit for rate calculations (e.g., time.Second, must be positive)
All rates are expressed in tokens per rateUnit.
Input validation:
- If rateInit is not a positive, finite number (e.g., negative, zero, NaN, or infinity), returns an error with message "refillRate must be a positive, finite number"
- If rateUnit is not a positive duration, returns an error with message "refillRateUnit must represent a positive duration"
- If the product of rateInit and rateUnit (in nanoseconds) exceeds maximum representable value, returns an error with message "refillRate per duration is too large"
- If rateMin is not a positive, finite number (e.g., negative, zero, NaN, or infinity), returns an error with message "rateMin must be a positive, finite number"
- If rateMax is not a positive, finite number (e.g., negative, zero, NaN, or infinity), returns an error with message "rateMax must be a positive, finite number"
- If rateMin is greater than rateMax, returns an error with message "rateMin must be less than equal to rateMax"
- If rateInit is not between rateMin and rateMax (inclusive), returns an error with message "rateInit must be a positive, finite number between rateMin and rateMax"
- If rateAdditiveIncrease is not a positive, finite number (e.g., negative, zero, NaN, or infinity), returns an error with message "rateAdditiveIncrease must be a positive, finite number"
- If rateMultiplicativeDecrease is not a finite number greater than or equal to 1.0 (e.g., NaN, infinity, or less than 1.0), returns an error with message "rateMultiplicativeDecrease must be a finite number greater than or equal to 1.0"
- If the product of rateMin, rateMax, or rateAdditiveIncrease with rateUnit (in nanoseconds) exceeds maximum representable value, returns an error with message "[parameter] per duration is too large"
func (*AIMDTokenBucketLimiter) CheckToken ¶ added in v0.2.0
func (a *AIMDTokenBucketLimiter) CheckToken(id []byte) bool
CheckToken returns whether a token would be available for the given ID without actually taking it. This is useful for preemptively checking if an operation would be rate limited before attempting it. Returns true if a token would be available, false otherwise. This method is thread-safe and can be called concurrently from multiple goroutines.
func (*AIMDTokenBucketLimiter) CheckTokens ¶ added in v0.2.0
func (a *AIMDTokenBucketLimiter) CheckTokens(id []byte, n uint8) bool
CheckTokens returns whether n tokens would be available for the given ID without actually taking them. This is useful for preemptively checking if an operation would be rate limited before attempting it. Returns true if all n tokens would be available, false otherwise. This method is thread-safe and can be called concurrently from multiple goroutines.
func (*AIMDTokenBucketLimiter) DecreaseRate ¶
func (a *AIMDTokenBucketLimiter) DecreaseRate(id []byte) float64
DecreaseRate multiplicatively decreases the rate for the bucket associated with the given ID. This implements the "multiplicative decrease" part of the AIMD algorithm, typically called when congestion or failures occur. The rate is decreased by dividing the distance from rateMin by rateMD, calculated as: rateMin + (currentRate - rateMin) / rateMD. This ensures more gradual decreases near the minimum rate. The rate will not go below the minimum rate (rateMin). This method is thread-safe and uses atomic operations to ensure consistency.
Returns the current rate (in tokens per rateUnit) for the bucket before the decrease was applied.
func (*AIMDTokenBucketLimiter) IncreaseRate ¶
func (a *AIMDTokenBucketLimiter) IncreaseRate(id []byte) float64
IncreaseRate additively increases the rate for the bucket associated with the given ID. This implements the "additive increase" part of the AIMD algorithm, typically called on successful operations. The rate is increased by rateAI up to the maximum rate (rateMax). This method is thread-safe and uses atomic operations to ensure consistency.
Returns the current rate (in tokens per rateUnit) for the bucket before the increase was applied.
func (*AIMDTokenBucketLimiter) Rate ¶ added in v0.1.9
func (a *AIMDTokenBucketLimiter) Rate(id []byte) float64
Rate returns the current token rate for the bucket associated with the given ID. The rate is expressed in tokens per rateUnit (e.g., tokens per second if rateUnit is time.Second). This method is thread-safe and can be called concurrently from multiple goroutines.
func (*AIMDTokenBucketLimiter) TakeToken ¶
func (a *AIMDTokenBucketLimiter) TakeToken(id []byte) bool
TakeToken attempts to take a token for the given ID from the appropriate bucket. It returns true if a token was successfully taken, false otherwise. This method is thread-safe and can be called concurrently from multiple goroutines.
func (*AIMDTokenBucketLimiter) TakeTokens ¶ added in v0.2.0
func (a *AIMDTokenBucketLimiter) TakeTokens(id []byte, n uint8) bool
TakeTokens attempts to take n tokens for the given ID. It returns true if all n tokens were successfully taken, false if the operation should be rate limited. This method is thread-safe and can be called concurrently from multiple goroutines. The operation is atomic: either all n tokens are taken, or none are taken.
type RotatingTokenBucketLimiter ¶ added in v0.2.0
type RotatingTokenBucketLimiter struct {
// contains filtered or unexported fields
}
RotatingTokenBucketLimiter implements a collision-resistant token bucket rate limiter. It maintains two TokenBucketLimiters with different hash seeds and rotates between them periodically. This approach minimizes the impact of hash collisions on rate limiting accuracy.
The limiter works by:
- Maintaining two TokenBucketLimiters with different hash seeds
- Using one as the primary "checked" limiter for rate limiting decisions
- Using the other as an "ignored" limiter to maintain state
- Periodically rotating roles and generating new seeds
- Always checking both limiters but only using the "checked"
result
This design ensures that hash collisions between different IDs only last for the duration of the rotation period, providing better fairness and accuracy compared to a single TokenBucketLimiter.
func NewRotatingTokenBucketLimiter ¶ added in v0.1.15
func NewRotatingTokenBucketLimiter( numBuckets uint, burstCapacity uint8, refillRate float64, refillRateUnit time.Duration, ) (*RotatingTokenBucketLimiter, error)
NewRotatingTokenBucketLimiter creates a new collision-resistant token bucket rate limiter with the specified parameters:
- numBuckets: number of token buckets per limiter (automatically rounded up to the nearest power of two if not already a power of two, for efficient hashing)
- burstCapacity: maximum number of tokens that can be consumed at once
- refillRate: rate at which tokens are refilled (must be positive and finite)
- refillRateUnit: time unit for refill rate calculations (e.g., time.Second, must be a positive duration)
The rotation interval is automatically calculated to ensure 99.99% statistical convergence of all token buckets to steady state before rotation occurs. This guarantees correctness by eliminating state inconsistency issues when hash mappings change during rotation.
The calculation is: rotationInterval = (burstCapacity/refillRate * refillRateUnit) * 5.0
The limiter creates two identical TokenBucketLimiters with different hash seeds and rotates between them at the calculated interval to minimize the impact of hash collisions. When rotation occurs:
- The current "ignored" limiter becomes the new "checked" limiter
- A copy of the new "checked" limiter is made with a fresh hash seed to become the new "ignored" limiter
- Both limiters are consulted on every operation, but only the "checked" result determines the rate limiting decision
This design ensures that:
- Hash collisions between different IDs only persist for one rotation period
- State inconsistency is eliminated through steady-state convergence
- Better fairness and accuracy than a single TokenBucketLimiter
Input validation follows the same rules as NewTokenBucketLimiter.
Returns a new RotatingTokenBucketLimiter instance and any error that occurred during creation.
func (*RotatingTokenBucketLimiter) CheckToken ¶ added in v0.2.0
func (r *RotatingTokenBucketLimiter) CheckToken(id []byte) bool
CheckToken returns whether a token would be available for the given ID without actually taking it. This is useful for preemptively checking if an operation would be rate limited before attempting it.
The method operates on both the "checked" and "ignored" limiters using the same timestamp to maintain consistency. The "ignored" limiter is checked to keep its state synchronized, but its result is discarded. Only the result from the "checked" limiter determines the return value.
This dual-checking approach ensures that state is maintained in both limiters during rotation periods, preventing token loss and maintaining collision resistance.
Returns true if a token would be available, false otherwise. This method is thread-safe and can be called concurrently from multiple goroutines.
func (*RotatingTokenBucketLimiter) CheckTokens ¶ added in v0.2.0
func (r *RotatingTokenBucketLimiter) CheckTokens(id []byte, n uint8) bool
CheckTokens returns whether n tokens would be available for the given ID without actually taking them. This is useful for preemptively checking if an operation would be rate limited before attempting it.
The method operates on both the "checked" and "ignored" limiters using the same timestamp to maintain consistency. The "ignored" limiter is checked to keep its state synchronized, but its result is discarded. Only the result from the "checked" limiter determines the return value.
Returns true if all n tokens would be available, false otherwise. This method is thread-safe and can be called concurrently from multiple goroutines.
func (*RotatingTokenBucketLimiter) RotationInterval ¶ added in v0.2.0
func (r *RotatingTokenBucketLimiter) RotationInterval() time.Duration
RotationInterval returns the automatically calculated rotation interval duration. This interval ensures 99.99% statistical convergence of all token buckets to steady state before rotation occurs, guaranteeing correctness by eliminating state inconsistency issues.
The rotation interval is calculated as: (burstCapacity / refillRate * refillRateUnit) * 5.0
This method is thread-safe and can be called concurrently from multiple goroutines.
func (*RotatingTokenBucketLimiter) TakeToken ¶ added in v0.2.0
func (r *RotatingTokenBucketLimiter) TakeToken(id []byte) bool
TakeToken attempts to take a token for the given ID. It returns true if a token was successfully taken, false if the operation should be rate limited.
The method operates on both the "checked" and "ignored" limiters using the same timestamp to maintain consistency. Both limiters consume tokens to keep their state synchronized, but only the result from the "checked" limiter determines the return value.
This design intentionally consumes 2x tokens per call to provide collision resistance. The trade-off is:
Cost: 2x token consumption rate Benefit: Hash collisions only persist for one rotation period
This dual-operation approach ensures that:
- State is maintained in both limiters during rotation periods
- When rotation occurs, the "ignored" limiter (with different hash seed) becomes the new "checked" limiter, breaking hash collisions
- Hash collisions only affect accuracy for the duration of rotationRate
- The system provides better fairness than a single limiter
This method is thread-safe and can be called concurrently from multiple goroutines.
func (*RotatingTokenBucketLimiter) TakeTokens ¶ added in v0.2.0
func (r *RotatingTokenBucketLimiter) TakeTokens(id []byte, n uint8) bool
TakeTokens attempts to take n tokens for the given ID. It returns true if all n tokens were successfully taken, false if the operation should be rate limited.
The method operates on both the "checked" and "ignored" limiters using the same timestamp to maintain consistency. Both limiters consume tokens to keep their state synchronized, but only the result from the "checked" limiter determines the return value.
This design intentionally consumes 2x tokens per call to provide collision resistance. The operation is atomic: either all n tokens are taken from both limiters, or none are taken.
This method is thread-safe and can be called concurrently from multiple goroutines.
type TokenBucketLimiter ¶
type TokenBucketLimiter struct {
// contains filtered or unexported fields
}
TokenBucketLimiter implements the token bucket algorithm for rate limiting. It maintains multiple buckets to distribute load and reduce contention. Each bucket has a fixed capacity and refills at a specified rate.
func NewTokenBucketLimiter ¶
func NewTokenBucketLimiter( numBuckets uint, burstCapacity uint8, refillRate float64, refillRateUnit time.Duration, ) (*TokenBucketLimiter, error)
NewTokenBucketLimiter creates a new token bucket rate limiter with the specified parameters:
- numBuckets: number of token buckets (automatically rounded up to the nearest power of two if not already a power of two, for efficient hashing)
- burstCapacity: maximum number of tokens that can be consumed at once
- refillRate: rate at which tokens are refilled (must be positive and finite)
- refillRateUnit: time unit for refill rate calculations (e.g., time.Second, must be a positive duration)
Returns a new TokenBucketLimiter instance and any error that occurred during creation. The numBuckets parameter is automatically rounded up to the nearest power of two if not already a power of two, for efficient hashing.
Input validation:
- If refillRate is not a positive, finite number (e.g., negative, zero, NaN, or infinity), returns an error with message "refillRate must be a positive, finite number"
- If refillRateUnit is not a positive duration, returns an error with message "refillRateUnit must represent a positive duration"
- If the product of refillRate and refillRateUnit (in nanoseconds) exceeds maximum representable value, returns an error with message "refillRate per duration is too large"
func (*TokenBucketLimiter) CheckToken ¶ added in v0.2.0
func (t *TokenBucketLimiter) CheckToken(id []byte) bool
CheckToken returns whether a token would be available for the given ID without actually taking it. This is useful for preemptively checking if an operation would be rate limited before attempting it. Returns true if a token would be available, false otherwise.
func (*TokenBucketLimiter) CheckTokens ¶ added in v0.2.0
func (t *TokenBucketLimiter) CheckTokens(id []byte, n uint8) bool
CheckTokens returns whether n tokens would be available for the given ID without actually taking them. This is useful for preemptively checking if an operation would be rate limited before attempting it. Returns true if all n tokens would be available, false otherwise.
func (*TokenBucketLimiter) TakeToken ¶
func (t *TokenBucketLimiter) TakeToken(id []byte) bool
TakeToken attempts to take a token for the given ID. It returns true if a token was successfully taken, false if the operation should be rate limited. This method is thread-safe and can be called concurrently from multiple goroutines.
func (*TokenBucketLimiter) TakeTokens ¶ added in v0.2.0
func (t *TokenBucketLimiter) TakeTokens(id []byte, n uint8) bool
TakeTokens attempts to take n tokens for the given ID. It returns true if all n tokens were successfully taken, false if the operation should be rate limited. This method is thread-safe and can be called concurrently from multiple goroutines. The operation is atomic: either all n tokens are taken, or none are taken.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package time56 provides a specialized 56-bit timestamp implementation for space-efficient, high-resolution timing within a limited time range.
|
Package time56 provides a specialized 56-bit timestamp implementation for space-efficient, high-resolution timing within a limited time range. |