Documentation
¶
Overview ¶
Package ratelimiter provides token bucket rate limiting with memory storage and HTTP middleware.
The package implements a token bucket algorithm that allows burst traffic up to a configured capacity while maintaining a steady refill rate. It includes an in-memory storage backend with automatic cleanup and HTTP middleware for easy integration into web applications.
Basic Usage ¶
Create a rate limiter with a memory store:
config := ratelimiter.Config{ Capacity: 100, // Maximum tokens (burst capacity) RefillRate: 10, // Tokens added per interval RefillInterval: time.Second, // Refill frequency } store := ratelimiter.NewMemoryStore() defer store.Close() limiter, err := ratelimiter.NewBucket(store, config) if err != nil { log.Fatal(err) } // Check if a request is allowed result, err := limiter.Allow(ctx, "user:123") if err != nil { // Handle error return } if !result.Allowed() { // Rate limit exceeded, retry after result.RetryAfter() return }
HTTP Middleware ¶
Use the provided middleware for HTTP rate limiting:
// Simple IP-based rate limiting keyFunc := func(r *http.Request) string { return r.RemoteAddr } middleware := ratelimiter.Middleware(limiter, keyFunc) handler := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello, World!")) })) http.ListenAndServe(":8080", handler)
The middleware automatically sets standard rate limit headers:
- X-RateLimit-Limit: Maximum tokens
- X-RateLimit-Remaining: Tokens remaining
- X-RateLimit-Reset: Unix timestamp of next refill
Composite Key Functions ¶
Combine multiple key extractors for complex rate limiting scenarios:
keyFunc := ratelimiter.Composite( func(r *http.Request) string { return r.Header.Get("X-API-Key") }, func(r *http.Request) string { return r.RemoteAddr }, )
Keys longer than 64 characters are automatically hashed using FNV-1a to prevent unbounded storage growth.
Advanced Operations ¶
Consume multiple tokens at once:
result, err := limiter.AllowN(ctx, "user:123", 5) if err != nil { return err }
Check bucket status without consuming tokens:
result, err := limiter.Status(ctx, "user:123") if err != nil { return err } fmt.Printf("Remaining: %d, Reset at: %v\n", result.Remaining, result.ResetAt)
Reset a bucket (useful for administrative operations):
err := limiter.Reset(ctx, "user:123") if err != nil { return err }
Memory Management ¶
The MemoryStore automatically cleans up stale buckets to prevent memory leaks:
store := ratelimiter.NewMemoryStore( ratelimiter.WithCleanupInterval(10 * time.Minute), )
Buckets are considered stale if they haven't been accessed for 1 hour. Disable cleanup by setting the interval to 0.
Custom Error Handling ¶
Customize error responses in the HTTP middleware:
errorResponder := func(w http.ResponseWriter, r *http.Request, result *ratelimiter.Result, err error) { if err != nil { http.Error(w, "Service temporarily unavailable", http.StatusServiceUnavailable) return } if result != nil && !result.Allowed() { retryAfter := int(result.RetryAfter().Seconds()) w.Header().Set("Retry-After", strconv.Itoa(retryAfter)) http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests) } } middleware := ratelimiter.Middleware(limiter, keyFunc, ratelimiter.WithErrorResponder(errorResponder), )
Error Types ¶
The package defines several error types for different failure scenarios:
if errors.Is(err, ratelimiter.ErrInvalidConfig) { // Configuration validation failed } if errors.Is(err, ratelimiter.ErrInvalidTokenCount) { // Token count must be positive } if errors.Is(err, ratelimiter.ErrStoreUnavailable) { // Storage backend is unavailable } if errors.Is(err, ratelimiter.ErrContextCancelled) { // Operation cancelled due to context }
Thread Safety ¶
All operations are thread-safe and can be used concurrently across multiple goroutines. The MemoryStore uses read-write mutexes for optimal performance with concurrent access.
Token Bucket Algorithm ¶
The implementation uses the standard token bucket algorithm:
- Tokens are added to the bucket at the configured RefillRate and RefillInterval
- Each request consumes one or more tokens
- If insufficient tokens are available, the request is denied
- The bucket capacity limits the maximum burst size
This provides smooth rate limiting with burst tolerance, making it suitable for web APIs, user rate limiting, and resource protection scenarios.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidConfig indicates that the provided configuration is invalid. ErrInvalidConfig = errors.New("invalid configuration") // ErrInvalidTokenCount indicates that the requested token count is invalid. ErrInvalidTokenCount = errors.New("invalid token count") // ErrContextCancelled indicates that the operation was cancelled due to context. ErrContextCancelled = errors.New("context cancelled") ErrStoreUnavailable = errors.New("store unavailable") )
Package-level error definitions for rate limiter operations.
Functions ¶
func Middleware ¶
func Middleware(limiter RateLimiter, keyFunc KeyFunc, opts ...MiddlewareOption) func(http.Handler) http.Handler
Middleware creates an HTTP middleware for rate limiting.
Types ¶
type Bucket ¶
type Bucket struct {
// contains filtered or unexported fields
}
Bucket implements a token bucket rate limiter.
type Config ¶
type Config struct { Capacity int // Maximum tokens (burst capacity) RefillRate int // Tokens added per interval RefillInterval time.Duration // How frequently tokens are added }
Config defines the token bucket rate limiting parameters.
type ErrorResponder ¶
ErrorResponder handles error responses for rate limiting. If err is not nil, it indicates an internal error. If err is nil and result.Allowed() is false, the rate limit was exceeded.
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore implements Store interface using in-memory storage.
func NewMemoryStore ¶
func NewMemoryStore(opts ...MemoryStoreOption) *MemoryStore
NewMemoryStore creates a new in-memory store with optional cleanup.
func (*MemoryStore) Close ¶
func (ms *MemoryStore) Close()
Close stops the cleanup goroutine. Safe to call multiple times.
type MemoryStoreOption ¶
type MemoryStoreOption func(*MemoryStore)
MemoryStoreOption configures a MemoryStore.
func WithCleanupInterval ¶
func WithCleanupInterval(interval time.Duration) MemoryStoreOption
WithCleanupInterval sets the cleanup interval for removing stale buckets. Set to 0 to disable automatic cleanup.
type MiddlewareOption ¶
type MiddlewareOption func(*middlewareConfig)
MiddlewareOption configures the rate limiting middleware.
func WithErrorResponder ¶
func WithErrorResponder(responder ErrorResponder) MiddlewareOption
WithErrorResponder sets a custom error responder.
type RateLimiter ¶
type RateLimiter interface { Allow(ctx context.Context, key string) (*Result, error) AllowN(ctx context.Context, key string, n int) (*Result, error) }
RateLimiter defines the interface for rate limiting implementations.
type Result ¶
type Result struct { Limit int // Maximum tokens (bucket capacity) Remaining int // Tokens remaining (negative means denied) ResetAt time.Time // When next token refill occurs }
Result contains the result of a rate limit check.
func (*Result) RetryAfter ¶
RetryAfter returns how long to wait before retry is likely to succeed. Returns 0 if the request was allowed.
type Store ¶
type Store interface { // ConsumeTokens attempts to consume tokens and returns the state after consumption. // If tokens is 0, updates bucket state without consuming (used for status checks). // A negative remaining count indicates the request should be denied. ConsumeTokens(ctx context.Context, key string, tokens int, config Config) (remaining int, resetAt time.Time, err error) Reset(ctx context.Context, key string) error }
Store defines the interface for rate limit storage backends.