### Limit.String() Example Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/types.md Demonstrates how to use the String() method to get a formatted string of a rate limit. Shows the output for a per-second limit. ```go limit := redis_rate.PerSecond(10) fmt.Println(limit.String()) // Output: "10 req/s (burst 10)" ``` -------------------------------- ### Initialize Go Module Source: https://github.com/go-redis/redis_rate/blob/v10/README.md Before installing the package, initialize a Go module for your project. ```shell go mod init github.com/my/repo ``` -------------------------------- ### Limit.IsZero() Example Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/types.md Illustrates how to check if a Limit is unconfigured using the IsZero() method. Prints a message when the limit is not set. ```go zeroLimit := redis_rate.Limit{} if zeroLimit.IsZero() { fmt.Println("Limit not configured") } ``` -------------------------------- ### Consume and Peek with AllowN Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/api-reference-limiter.md Example demonstrating how to consume multiple tokens and how to peek at the current state without consuming any tokens using the AllowN method. ```go package main import ( "context" "fmt" "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) func main() { ctx := context.Background() rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) limiter := redis_rate.NewLimiter(rdb) // Consume 5 tokens from the rate limit result, err := limiter.AllowN(ctx, "api-key:xyz", redis_rate.PerSecond(1000), 5) if err != nil { panic(err) } fmt.Printf("Allowed: %d tokens\n", result.Allowed) fmt.Printf("Remaining: %d tokens\n", result.Remaining) // Peek at current state without consuming (n=0) peekResult, err := limiter.AllowN(ctx, "api-key:xyz", redis_rate.PerSecond(1000), 0) if err != nil { panic(err) } fmt.Printf("Current remaining without consuming: %d\n", peekResult.Remaining) } ``` -------------------------------- ### Usage Examples Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/INDEX.md Provides over 15 real-world code patterns and runnable examples for various use cases, including HTTP middleware, per-endpoint limiting, batch processing, and error handling. ```APIDOC ## Usage Examples This section offers practical code examples for common scenarios. ### Basic Setup **Description:** Demonstrates how to set up the limiter for single-node and ring configurations. ### HTTP Middleware **Description:** A complete example of integrating the rate limiter into an HTTP server. ### Per-Endpoint Rate Limiting **Description:** Shows how to apply rate limits to specific API endpoints. ### Batch Processing with AllowAtMost **Description:** Illustrates using `AllowAtMost` for consuming multiple tokens in a single operation. ### Multi-Token Consumption **Description:** Examples of consuming more than one token at a time. ### Peeking Without Consumption **Description:** Demonstrates how to check rate limits without consuming tokens. ### Resetting Rate Limits **Description:** Examples of how to manually reset rate limit counters. ### Error Handling with Graceful Degradation **Description:** Strategies for handling rate limiting errors and implementing fallback mechanisms. ### Custom Rate Limits **Description:** Examples of defining and using custom rate limit configurations. ### Multiple Limits Per User **Description:** How to apply multiple distinct rate limits to a single user or entity. ### 15+ Complete, Runnable Examples **Description:** A collection of diverse and practical examples covering various aspects of the library. ``` -------------------------------- ### Install redis_rate Package Source: https://github.com/go-redis/redis_rate/blob/v10/README.md Install the redis_rate package, ensuring to include the version suffix in the import path. ```shell go get github.com/go-redis/redis_rate/v10 ``` -------------------------------- ### Example Usage of NewLimiter Source: https://github.com/go-redis/redis_rate/blob/v10/README.md Demonstrates how to create a new rate limiter and check if an action is allowed within the specified rate limits. It flushes the database before creating the limiter. ```go package redis_rate_test import ( "context" "fmt" "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) func ExampleNewLimiter() { ctx := context.Background() rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) _ = rdb.FlushDB(ctx).Err() limiter := redis_rate.NewLimiter(rdb) res, err := limiter.Allow(ctx, "project:123", redis_rate.PerSecond(10)) if err != nil { panic(err) } fmt.Println("allowed", res.Allowed, "remaining", res.Remaining) // Output: allowed 1 remaining 9 } ``` -------------------------------- ### Example Usage of AllowAtMost Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/api-reference-limiter.md Demonstrates how to use the AllowAtMost method to request up to 10 batch items, consuming only available capacity. The result.Allowed will be between 0 and 10. ```go package main import ( "context" "fmt" "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) func main() { ctx := context.Background() rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) limiter := redis_rate.NewLimiter(rdb) // Request up to 10 batch items, but only consume what's available result, err := limiter.AllowAtMost(ctx, "batch:process", redis_rate.PerSecond(100), 10) if err != nil { panic(err) } // result.Allowed will be between 0 and 10 fmt.Printf("Processing %d items (requested up to 10) ", result.Allowed) if result.Allowed == 0 { fmt.Printf("No capacity. Retry after: %v\n", result.RetryAfter) } } ``` -------------------------------- ### Rate Limiter Usage Example Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/types.md Demonstrates how to use the redis_rate limiter to check if a request is allowed, and how to interpret the Result. Includes handling rate-limited responses and displaying retry/reset times. ```go package main import ( "context" "fmt" "net/http" "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) func main() { ctx := context.Background() rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) limiter := redis_rate.NewLimiter(rdb) result, err := limiter.Allow(ctx, "user:123", redis_rate.PerSecond(10)) if err != nil { panic(err) } if result.Allowed > 0 { fmt.Printf("Request allowed. %d remaining.\n", result.Remaining) } else { // Rate limited fmt.Printf("Rate limited. Retry after: %v\n", result.RetryAfter) // In HTTP handler: w.Header().Set("Retry-After", fmt.Sprintf("%d", int(result.RetryAfter.Seconds()))) } fmt.Printf("Reset after: %v\n", result.ResetAfter) } ``` -------------------------------- ### Verify Redis and Go Versions Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/quick-start.md Ensure you have Redis 3.2+ and Go 1.19+ installed. Run these commands in your terminal. ```bash redis-cli --version # Redis 3.2 or later go version # Go 1.19 or later ``` -------------------------------- ### HTTP Middleware for Rate Limiting Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/README.md Integrate rate limiting into an HTTP server using middleware. This example shows how to block requests when the rate limit is exceeded. ```go func middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { result, _ := limiter.Allow(r.Context(), userID, redis_rate.PerSecond(100)) if result.Allowed == 0 { http.Error(w, "Too Many Requests", http.StatusTooManyRequests) return } next.ServeHTTP(w, r) }) } ``` -------------------------------- ### GCRA Algorithm: Theoretical Arrival Time (TAT) Calculation Example Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/advanced-topics.md Illustrates the step-by-step calculation of TAT for a series of requests, showing how the burst window affects immediate allowance and when a request might be denied and require a Retry-After value. ```text Rate: 10 req/s Period: 1s Emission Interval: 1s / 10 = 0.1s Burst: 10 Request 1 (t=0.0s): TAT was: 0 new_TAT = 0 + 0.1 = 0.1s Allow: yes (within burst window) Request 2 (t=0.02s): TAT was: 0.1s new_TAT = 0.1 + 0.1 = 0.2s Allow: yes (within burst window) ... Request 10 (t=0.18s): TAT was: 0.9s new_TAT = 0.9 + 0.1 = 1.0s Allow: yes (at edge of burst window) Request 11 (t=0.19s): TAT was: 1.0s new_TAT = 1.0 + 0.1 = 1.1s Allow: no (outside burst window, needs to wait) Retry-After: 1.1 - 0.19 = 0.91s ``` -------------------------------- ### Initialize Rate Limiter with Redis Ring (Cluster) Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/usage-examples.md Sets up a rate limiter using a Redis ring for a sharded Redis setup. Each key is consistently routed to the same Redis node. ```go package main import ( "context" "log" "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) func main() { ctx := context.Background() // Redis Ring for sharded Redis setup ring := redis.NewRing(&redis.RingOptions{ Addrs: map[string]string{ "server1": "redis1.example.com:6379", "server2": "redis2.example.com:6379", "server3": "redis3.example.com:6379", }, }) // Create limiter limiter := redis_rate.NewLimiter(ring) // Each key is consistently routed to the same Redis node result, _ := limiter.Allow(ctx, "user:456", redis_rate.PerSecond(50)) log.Printf("Allowed: %d\n", result.Allowed) } ``` -------------------------------- ### Per-Endpoint Rate Limiting with Different Limits Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/usage-examples.md This example demonstrates how to apply different rate limits to different API endpoints based on user ID. It shows strict limits for creation, generous limits for reads, and very strict limits for expensive operations. ```go package main import ( "context" "net/http" "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) type API struct { limiter *redis_rate.Limiter } func (api *API) CreateResource(w http.ResponseWriter, r *http.Request) { ctx := r.Context() userID := extractUserID(r) // Strict limit for creation (10 per minute) result, err := api.limiter.Allow( ctx, "create:"+userID, redis_rate.PerMinute(10), ) if err != nil || result.Allowed == 0 { http.Error(w, "Rate limited", http.StatusTooManyRequests) return } // Process creation w.WriteHeader(http.StatusCreated) } func (api *API) ReadResource(w http.ResponseWriter, r *http.Request) { ctx := r.Context() userID := extractUserID(r) // Generous limit for reads (1000 per second) result, err := api.limiter.Allow( ctx, "read:"+userID, redis_rate.PerSecond(1000), ) if err != nil || result.Allowed == 0 { http.Error(w, "Rate limited", http.StatusTooManyRequests) return } // Process read w.WriteHeader(http.StatusOK) } func (api *API) ExpensiveOperation(w http.ResponseWriter, r *http.Request) { ctx := r.Context() userID := extractUserID(r) // Very strict for expensive operations (5 per hour) result, err := api.limiter.Allow( ctx, "expensive:"+userID, redis_rate.PerHour(5), ) if err != nil || result.Allowed == 0 { http.Error(w, "Rate limited", http.StatusTooManyRequests) return } // Process expensive operation w.WriteHeader(http.StatusOK) } ``` -------------------------------- ### Check Rate Limit for a Request Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/quick-start.md Use the Allow method to check if a request is within the defined rate limit. This example checks against a limit of 100 requests per second for a specific user ID. ```go result, err := limiter.Allow(ctx, "user:123", redis_rate.PerSecond(100)) if err != nil { panic(err) } if result.Allowed > 0 { // Process request } else { // Return HTTP 429: Too Many Requests } ``` -------------------------------- ### HTTP Rate Limiting Middleware in Go Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/overview.md Provides an example of an HTTP middleware function for rate limiting incoming requests. It extracts a user ID, checks the rate limit, and sets appropriate response headers (X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After). ```go func rateLimitMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userID := extractUserID(r) result, err := limiter.Allow(r.Context(), "user:"+userID, redis_rate.PerSecond(100)) w.Header().Set("X-RateLimit-Limit", "100") w.Header().Set("X-RateLimit-Remaining", strconv.Itoa(result.Remaining)) if result.Allowed == 0 { w.Header().Set("Retry-After", strconv.Itoa(int(result.RetryAfter.Seconds()))) http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests) return } next.ServeHTTP(w, r) }) } ``` -------------------------------- ### Process Items in Batches Based on Rate Limit Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/quick-start.md Determine how many items can be processed in a batch based on the current rate limit. This example allows processing up to 100 items if the rate limit permits. ```go // Try to process up to 100 items result, _ := limiter.AllowAtMost(ctx, "batch:id", redis_rate.PerSecond(1000), 100) processItems(items[:result.Allowed]) ``` -------------------------------- ### Define Events Per Minute Limit Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/exported-symbols.md Create a rate limit for events occurring per minute. This example sets a limit of 5000 requests per minute. ```go limit := redis_rate.PerMinute(5000) // 5000 req/min ``` -------------------------------- ### Configure Redis Connection Pool Size Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/README.md Example of configuring Redis client options to set the connection pool size and minimum idle connections. This is useful for optimizing performance based on application concurrency. ```go redis.Options{ PoolSize: 10, MinIdleConns: 5, } ``` -------------------------------- ### Define Events Per Second Limit Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/exported-symbols.md Create a rate limit for events occurring per second. This example sets a limit of 100 requests per second. ```go limit := redis_rate.PerSecond(100) // 100 req/s ``` -------------------------------- ### Implement Cost-Based Limiting for Operations Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/advanced-topics.md Cost-Based Limiting assigns different costs to various operations and checks if the total cost fits within a rate limit. This example defines operation costs and uses AllowN to check limits. ```Go type OperationCost struct { name string cost int } var operationCosts = map[string]int{ "list": 1, "get": 2, "export": 50, "delete_all": 1000, } func (limiter *Limiter) CheckOperationLimit(ctx context.Context, userID, operation string) bool { cost := operationCosts[operation] result, err := limiter.AllowN(ctx, userID, redis_rate.PerSecond(100), cost) return err == nil && result.Allowed > 0 } ``` -------------------------------- ### Create Redis Client and Rate Limiter Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/00-START-HERE.txt Demonstrates how to create a Redis client and initialize the redis_rate Limiter. Ensure Redis is running and accessible at the specified address. ```Go import ( "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) // Create limiter rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) limiter := redis_rate.NewLimiter(rdb) ``` -------------------------------- ### Get Human-Readable Limit String Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/exported-symbols.md The String method provides a human-readable representation of a Limit, including the rate and burst capacity. ```go limit.String() ``` -------------------------------- ### Initialize Global Singleton Rate Limiter Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/README.md Demonstrates how to create and use a global singleton instance of the Redis rate limiter. This pattern is suitable for application-wide rate limiting. ```go var rateLimiter = redis_rate.NewLimiter(redisClient) // Use everywhere in application ``` -------------------------------- ### Verify Redis Version Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/integration-guide.md Check your installed Redis version using the redis-cli command. The library requires Redis 3.2 or newer. ```bash redis-cli --version ``` -------------------------------- ### Basic Redis Rate Limiter Usage Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/exported-symbols.md Demonstrates how to create a limiter, check rate limits for a given key and limit, and process the result. ```go // Create limiter with Redis client limiter := redis_rate.NewLimiter(redisClient) // Check rate limit result, err := limiter.Allow(ctx, "user:123", redis_rate.PerSecond(100)) // Process result if err != nil { // Handle Redis error } if result.Allowed > 0 { // Request allowed - process normally } else { // Rate limited - return 429 with Retry-After header } ``` -------------------------------- ### Load Script and Cache SHA Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/advanced-topics.md Demonstrates the first step in script caching: loading the Lua script once and storing its SHA hash for future use. ```go sha := limiter.loadScript(allowNScript) // sha = "abc123def456..." ``` -------------------------------- ### Define Events Per Hour Limit Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/exported-symbols.md Create a rate limit for events occurring per hour. This example sets a limit of 100,000 requests per hour. ```go limit := redis_rate.PerHour(100000) // 100k req/hour ``` -------------------------------- ### Basic Rate Limiting with Go Redis Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/overview.md Demonstrates how to initialize a rate limiter and check if a request is allowed. Handles potential Redis errors and rate-limited responses. ```go ctx := context.Background() rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) limiter := redis_rate.NewLimiter(rdb) result, err := limiter.Allow(ctx, "user:123", redis_rate.PerSecond(10)) if err != nil { // Handle Redis error } if result.Allowed == 0 { // Rate limited - return HTTP 429 } ``` -------------------------------- ### Initialize Global Rate Limiter Instance Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/integration-guide.md Set up a global redis_rate.Limiter instance for centralized rate limiting in your application. This pattern is recommended for medium to large applications. ```go package config import ( "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) var RateLimiter *redis_rate.Limiter func Init() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) RateLimiter = redis_rate.NewLimiter(rdb) } ``` ```go func handleRequest(w http.ResponseWriter, r *http.Request) { result, err := config.RateLimiter.Allow(r.Context(), "user:123", redis_rate.PerSecond(100)) // ... handle result } ``` -------------------------------- ### Implement Tiered Rate Limits Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/quick-start.md Use this pattern to apply different rate limits based on user tiers (e.g., free, pro). The appropriate limit is selected before calling Allow. ```go var limit redis_rate.Limit switch userTier { case "free": limit = redis_rate.PerSecond(10) case "pro": limit = redis_rate.PerSecond(1000) } limiter.Allow(ctx, userID, limit) ``` -------------------------------- ### Import Necessary Packages Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/quick-start.md Import the redis and redis_rate packages. Ensure you have the correct versions imported for your project. ```go import ( "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) ``` -------------------------------- ### Echo Framework Rate Limiting Middleware Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/integration-guide.md Apply rate limiting to your Echo web server using a custom middleware function. This example shows how to access context and request details within the Echo framework. ```go import "github.com/labstack/echo/v4" func rateLimitMiddleware(limiter *redis_rate.Limiter) echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { ctx := c.Request().Context() userID := c.Get("user_id").(string) result, err := limiter.Allow(ctx, "user:"+userID, redis_rate.PerSecond(100)) if err != nil { return c.JSON(http.StatusServiceUnavailable, map[string]string{"error": "Service unavailable"}) } c.Response().Header().Set("X-RateLimit-Remaining", strconv.Itoa(result.Remaining)) if result.Allowed == 0 { c.Response().Header().Set("Retry-After", strconv.Itoa(int(result.RetryAfter.Seconds()))) return c.JSON(http.StatusTooManyRequests, map[string]string{"error": "Rate limit exceeded"}) } return next(c) } } } ``` -------------------------------- ### Set Context Timeout for Rate Limit Checks Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/README.md Demonstrates how to create a context with a specific timeout for rate limiting operations. This helps prevent operations from blocking indefinitely. ```go ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) ``` -------------------------------- ### Initialize Rate Limiter with Single Redis Node Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/usage-examples.md Connects to a single Redis instance and creates a rate limiter. Verifies the connection and then uses the limiter to check if a request is allowed. ```go package main import ( "context" "log" "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) func main() { ctx := context.Background() // Connect to Redis rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) // Verify connection if err := rdb.Ping(ctx).Err(); err != nil { log.Fatal("Redis connection failed:", err) } // Create rate limiter limiter := redis_rate.NewLimiter(rdb) // Use limiter result, err := limiter.Allow(ctx, "user:123", redis_rate.PerSecond(100)) if err != nil { log.Fatal(err) } if result.Allowed > 0 { log.Println("Request allowed") } else { log.Printf("Rate limited. Retry after: %v\n", result.RetryAfter) } } ``` -------------------------------- ### Create and Use a Rate Limiter Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/exported-symbols.md Instantiate a new Limiter with a Redis client and use the Allow method to check rate limits for a given key and limit. Safe for concurrent use. ```go limiter := redis_rate.NewLimiter(redisClient) result, err := limiter.Allow(ctx, "user:123", redis_rate.PerSecond(100)) ``` -------------------------------- ### Custom Rate Limits with Non-Standard Periods Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/usage-examples.md Demonstrates how to define and use custom rate limits with periods that are not standard (e.g., milliseconds, minutes, hours). This is useful for scenarios requiring fine-grained control over rate limiting intervals. ```go package main import ( "context" "log" "time" "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) func main() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) limiter := redis_rate.NewLimiter(rdb) ctx := context.Background() // Custom limits with unusual periods limits := map[string]redis_rate.Limit{ "burst": redis_rate.Limit{Rate: 100, Period: 100 * time.Millisecond, Burst: 100}, "per_5min": redis_rate.Limit{Rate: 500, Period: 5 * time.Minute, Burst: 500}, "per_6hour": redis_rate.Limit{Rate: 10000, Period: 6 * time.Hour, Burst: 10000}, "very_strict": redis_rate.Limit{Rate: 1, Period: 24 * time.Hour, Burst: 1}, } for name, limit := range limits { result, err := limiter.Allow(ctx, "test:"+name, limit) if err != nil { log.Fatal(err) } log.Printf("%s: %s - allowed=%d, remaining=%d\n", name, limit.String(), result.Allowed, result.Remaining) } } ``` -------------------------------- ### API Reference: Limiter Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/INDEX.md Provides complete method documentation for the Limiter type, including constructors, methods like Allow(), AllowN(), AllowAtMost(), and Reset(). Each method includes its full signature, parameters, return values, error conditions, and detailed code examples. ```APIDOC ## API Reference: Limiter This section details the `Limiter` type and its associated methods. ### NewLimiter() **Description:** Constructor for creating a new `Limiter` instance. ### Allow() **Description:** Checks if a single request is allowed based on the defined rate limits. ### AllowN() **Description:** Checks if multiple requests are allowed. ### AllowAtMost() **Description:** Attempts to consume a specified number of tokens, returning an error if not enough are available. ### Reset() **Description:** Resets the rate limit counters for the limiter. ``` -------------------------------- ### Get Redis Server Time and Adjust Epoch Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/advanced-topics.md This Lua script retrieves the current Redis server time and adjusts it relative to a specific epoch (January 1, 2017) to maintain floating-point precision and handle clock skew. It ensures that time-based calculations are consistent across distributed clients by using a single, authoritative time source. ```lua local now = redis.call("TIME") now = (now[1] - jan_1_2017) + (now[2] / 1000000) ``` ```lua tat = math.max(tat, now) ``` ```lua local jan_1_2017 = 1483228800 -- Unix timestamp ~ 1.7 billion -- TAT relative to epoch ~ 1.7 billion -- Could lose precision near 16 digits -- Adjusted timestamp ~ 250 million (within 16-digit precision) -- TAT relative to epoch ~ 250 million -- Precise until year 2048 ``` -------------------------------- ### Initialize Redis Rate Limiter with Cluster Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/integration-guide.md This snippet shows how to initialize the rate limiter with a Redis Cluster client. Provide the addresses of your Redis cluster nodes. ```go package config import ( "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) func InitWithCluster() *redis_rate.Limiter { clusterClient := redis.NewClusterClient(&redis.ClusterOptions{ Addrs: []string{"node1:6379", "node2:6379", "node3:6379"}, }) return redis_rate.NewLimiter(clusterClient) } ``` -------------------------------- ### Check Rate Limit with Allow Method Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/00-START-HERE.txt Shows how to check if a single request is allowed using the Allow method. It also demonstrates how to access the remaining requests and the retry-after duration if the request is rate-limited. ```Go // Check rate limit result, err := limiter.Allow(ctx, "user:123", redis_rate.PerSecond(100)) if result.Allowed > 0 { // Request allowed fmt.Printf("Remaining: %d\n", result.Remaining) } else { // Rate limited fmt.Printf("Retry after: %v\n", result.RetryAfter) } ``` -------------------------------- ### Create New Limiter Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/api-reference-limiter.md Instantiates a new Limiter with a given Redis client. Use this to set up rate limiting for your application. ```go package main import ( "context" "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) func main() { ctx := context.Background() rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) limiter := redis_rate.NewLimiter(rdb) // Use limiter to check rate limits result, err := limiter.Allow(ctx, "user:123", redis_rate.PerSecond(100)) if err != nil { panic(err) } if result.Allowed > 0 { // Request is allowed } } ``` -------------------------------- ### Initialize Redis Rate Limiter with Sentinel Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/integration-guide.md Use this snippet to initialize the rate limiter with a Redis Sentinel client for high availability. Ensure Sentinel addresses and master name are correctly configured. ```go package config import ( "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) func InitWithSentinel() *redis_rate.Limiter { failoverClient := redis.NewFailoverClient(&redis.FailoverOptions{ MasterName: "mymaster", SentinelAddrs: []string{"sentinel1:26379", "sentinel2:26379", "sentinel3:26379"}, SentinelPassword: "sentinel_password", DB: 0, }) return redis_rate.NewLimiter(failoverClient) } ``` -------------------------------- ### NewLimiter Initializes with Script Caching Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/advanced-topics.md Constructor for the Limiter, which initializes the Redis client and immediately loads all necessary scripts to cache their SHAs. ```go func NewLimiter(rdb rediser) *Limiter { l := &Limiter{rdb: rdb} l.loadScripts() return l } ``` -------------------------------- ### Create a New Rate Limiter Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/exported-symbols.md Instantiate a new rate limiter with a Redis client. Ensure the Redis client is properly configured. ```go rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) limiter := redis_rate.NewLimiter(rdb) ``` -------------------------------- ### Multiple Rate Limits for the Same User Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/usage-examples.md Shows how to track and enforce different quotas (e.g., per second, per minute, per day) for a single user. This is essential for implementing complex API usage policies. ```go package main import ( "context" "log" "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) type UserQuotas struct { userID string limiter *redis_rate.Limiter } func (uq *UserQuotas) CheckAllLimits(ctx context.Context) bool { userID := uq.userID // Check various quotas for the same user quotas := map[string]redis_rate.Limit{ "requests_per_second": redis_rate.PerSecond(100), "requests_per_minute": redis_rate.PerMinute(5000), "api_calls_per_day": redis_rate.PerHour(100000), // ~100k per 24h } for quotaName, limit := range quotas { result, err := uq.limiter.Allow(ctx, userID+":"+quotaName, limit) if err != nil { log.Printf("Quota check failed: %s - %v\n", quotaName, err) return false } if result.Allowed == 0 { log.Printf("Quota exceeded: %s - retry after %v\n", quotaName, result.RetryAfter) return false } log.Printf("✓ %s: %d remaining\n", quotaName, result.Remaining) } return true } func main() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) userQuotas := &UserQuotas{ userID: "user:123", limiter: redis_rate.NewLimiter(rdb), } ctx := context.Background() allowed := userQuotas.CheckAllLimits(ctx) if allowed { log.Println("All quotas available, request allowed") } else { log.Println("One or more quotas exceeded") } } ``` -------------------------------- ### Reset Rate Limits for a User Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/usage-examples.md Demonstrates how to administratively reset a user's rate limit, making their full quota available again. This is useful for scenarios requiring a hard reset. ```go package main import ( "context" "log" "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" ) func main() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) limiter := redis_rate.NewLimiter(rdb) ctx := context.Background() userID := "user:123" limit := redis_rate.PerSecond(10) // Use up tokens for i := 0; i < 10; i++ { limiter.Allow(ctx, userID, limit) } // Check status result, _ := limiter.Allow(ctx, userID, limit) log.Printf("Rate limited: %d allowed, retry after %v\n", result.Allowed, result.RetryAfter) // Reset the user's rate limit if err := limiter.Reset(ctx, userID); err != nil { log.Fatal("Reset failed:", err) } log.Println("✓ Rate limit reset for user") // Now full quota is available result, _ = limiter.Allow(ctx, userID, limit) log.Printf("After reset: %d allowed, %d remaining\n", result.Allowed, result.Remaining) } ``` -------------------------------- ### Import Redis Rate Limiter Package Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/exported-symbols.md Import the necessary package to use the Redis rate limiter functionality. ```go import "github.com/go-redis/redis_rate/v10" ``` -------------------------------- ### Batch Processing with AllowAtMost in Go Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/overview.md Shows how to process a batch of items, consuming only the available tokens up to a specified maximum. Useful for scenarios where you want to process as much as possible without exceeding a limit. ```go // Process up to 100 items, but consume only what's available result, _ := limiter.AllowAtMost(ctx, "batch:id", redis_rate.PerSecond(1000), 100) items := processItems(itemSource, result.Allowed) ``` -------------------------------- ### Project Structure of redis_rate Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/overview.md Overview of the directory structure for the redis_rate Go package. ```tree redis_rate/ ├── rate.go # Core Limiter and Limit types ├── lua.go # GCRA Lua scripts for Redis ├── rate_test.go # Unit tests ├── example_test.go # Example usage ├── go.mod # Module definition └── README.md # Documentation ``` -------------------------------- ### Initialize Redis Rate Limiter with Connection Pooling Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/integration-guide.md Configure connection pooling for performance by setting PoolSize, MinIdleConns, and MaxRetries in the Redis Options. This helps manage connections efficiently. ```go func InitWithPooling() *redis_rate.Limiter { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", PoolSize: 10, // Number of connections in pool MinIdleConns: 5, // Minimum idle connections MaxRetries: 3, // Max retries for failed commands }) return redis_rate.NewLimiter(rdb) } ``` -------------------------------- ### Peek Rate Limiter Capacity in Go Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/overview.md Illustrates how to check the remaining capacity of a rate limiter without consuming any tokens. This is useful for displaying current limits or making decisions based on available capacity. ```go // Check remaining capacity without consuming tokens result, _ := limiter.AllowN(ctx, "key", limit, 0) fmt.Printf("Available: %d tokens\n", result.Remaining) ``` -------------------------------- ### Mock Redis for Rate Limiter Testing Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/quick-start.md Create a mock implementation of the `rediser` interface to test the rate limiter logic without requiring a live Redis connection. ```go // Implement rediser interface for testing type mockRedis struct{} func (m *mockRedis) Eval(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd { // Mock implementation } // ... implement other interface methods limiter := redis_rate.NewLimiter(&mockRedis{}) ``` -------------------------------- ### Test Rate Limiter with Real Redis Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/quick-start.md Write integration tests for the rate limiter using a live Redis instance. Ensure to clear the database before the test to guarantee consistent results. ```go func TestRate(t *testing.T) { rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) rdb.FlushDB(context.Background()) // Clear for test limiter := redis_rate.NewLimiter(rdb) result, _ := limiter.Allow(context.Background(), "test", redis_rate.PerSecond(10)) if result.Allowed != 1 || result.Remaining != 9 { t.Fatal("Unexpected result") } } ``` -------------------------------- ### Dependency Injection for Rate Limiter Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/README.md Shows how to inject a Redis rate limiter instance into a struct. This promotes modularity and testability by decoupling dependencies. ```go type Server struct { limiter *redis_rate.Limiter } // Pass limiter to constructors ``` -------------------------------- ### Implement Rate Limiter with Dependency Injection Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/integration-guide.md Use dependency injection to provide a redis_rate.Limiter to your components. This pattern promotes testable code and is suitable for applications with multiple interacting parts. ```go package api type Server struct { limiter *redis_rate.Limiter // ... other fields } func NewServer(limiter *redis_rate.Limiter) *Server { return &Server{ limiter: limiter, // ... } } func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) { result, err := s.limiter.Allow(r.Context(), "user:123", redis_rate.PerSecond(100)) // ... handle result } ``` -------------------------------- ### Basic Rate Limiting with go-redis/redis_rate Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/README.md Initialize a new rate limiter and check if a request is allowed. Use this for simple rate limiting checks. ```go limiter := redis_rate.NewLimiter(redisClient) result, err := limiter.Allow(ctx, "user:123", redis_rate.PerSecond(100)) if result.Allowed > 0 { // Request allowed } ``` -------------------------------- ### Implement Time-Based Decay Rate Limiter Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/advanced-topics.md Use TimeDecayLimit when older activity should not count towards the current rate limit. It decays the base rate over time based on a decay rate per hour. ```Go type TimeDecayLimit struct { baseLimit redis_rate.Limit decayRate float64 // Decay per hour } func (tdl *TimeDecayLimit) computeLimit(timeSinceLastReset time.Duration) redis_rate.Limit { decayFactor := math.Exp(-tdl.decayRate * timeSinceLastReset.Hours()) newRate := int(float64(tdl.baseLimit.Rate) * decayFactor) return redis_rate.Limit{ Rate: newRate, Period: tdl.baseLimit.Period, Burst: newRate, } } ``` -------------------------------- ### Wrapper with Custom Logic for Rate Limiter Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/README.md Illustrates creating a custom wrapper around the Redis rate limiter to implement specific error handling or retry logic. The `failOpen` field controls behavior on errors. ```go type SafeLimiter struct { limiter *redis_rate.Limiter failOpen bool } // Custom error handling, retry logic ``` -------------------------------- ### Create Redis Rate Limiter Instance Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/implementation-details.md Helper function to create a new Redis rate limiter instance for testing. It establishes a Redis connection and flushes the database to ensure a clean state before each test. ```go func rateLimiter() *redis_rate.Limiter { ring := redis.NewRing(&redis.RingOptions{ Addrs: map[string]string{"server0": ":6379"}, }) if err := ring.FlushDB(context.TODO()).Err(); err != nil { panic(err) } return redis_rate.NewLimiter(ring) } ``` -------------------------------- ### Rate Limiter Benchmark Results Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/advanced-topics.md Benchmark results from rate_test.go indicate high performance for Allow and AllowAtMost functions, handling over 100,000 operations per second. ```text BenchmarkAllow-8 100,000+ ops BenchmarkAllowAtMost-8 100,000+ ops ``` -------------------------------- ### Implement Per-IP Rate Limit Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/quick-start.md Use this pattern to limit requests based on client IP address. Ensure the clientIP is correctly captured. ```go limiter.Allow(ctx, "ip:"+clientIP, redis_rate.PerMinute(1000)) ``` -------------------------------- ### Create Rate Limiter Wrapper with Custom Logic Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/integration-guide.md Build a wrapper around redis_rate.Limiter to encapsulate custom error handling and fallback behavior, such as failing open or closed. This is useful for consistent error management. ```go package ratelimit import ( "context" "log" "github.com/go-redis/redis_rate/v10" ) type LimitChecker struct { limiter *redis_rate.Limiter failOpen bool } func (lc *LimitChecker) Check( ctx context.Context, key string, limit redis_rate.Limit, ) bool { result, err := lc.limiter.Allow(ctx, key, limit) if err != nil { if lc.failOpen { log.Printf("Rate limit error (failing open): %v\n", err) return true } log.Printf("Rate limit error (failing closed): %v\n", err) return false } return result.Allowed > 0 } ``` -------------------------------- ### Handling Redis Errors in Go Rate Limiter Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/overview.md Demonstrates how to handle errors returned by rate limiter methods, specifically checking for context cancellation or timeouts. Allows for flexible error handling strategies like failing open or closed. ```go result, err := limiter.Allow(ctx, "key", limit) if err != nil { if errors.Is(err, context.DeadlineExceeded) { log.Warn("rate limit check timeout") // Decide: fail open or fail closed } else { log.Error("redis error", err) } } ``` -------------------------------- ### Implement Per-User Rate Limit Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/quick-start.md Use this pattern to limit requests for individual users. Ensure the userID is unique. ```go limiter.Allow(ctx, "user:"+userID, redis_rate.PerSecond(100)) ``` -------------------------------- ### Limit.String() Method Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/types.md Provides a human-readable string representation of a Limit. Useful for logging or display purposes. ```go func (l Limit) String() string ``` -------------------------------- ### Limit.String Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/exported-symbols.md Returns a string representation of the Limit configuration. ```APIDOC ## Limit.String ### Description Returns a string representation of the rate limit configuration. ### Signature `func (l Limit) String() string` ### Returns - **string** - A string describing the rate limit (e.g., "100/s"). ``` -------------------------------- ### Integration Test with Real Redis Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/integration-guide.md This snippet demonstrates integration testing with a live Redis server. Ensure Redis is running on localhost:6379. It tests the `Allow` functionality, including request allowance and remaining counts. ```go package mypackage import ( "context" "testing" "time" "github.com/redis/go-redis/v9" "github.com/go-redis/redis_rate/v10" "github.com/stretchr/testify/require" ) func setupTestRedis(t *testing.T) *redis.Client { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) err := rdb.FlushDB(context.Background()).Err() require.NoError(t, err) return rdb } func TestAllow(t *testing.T) { rdb := setupTestRedis(t) limiter := redis_rate.NewLimiter(rdb) ctx := context.Background() // Test first request allowed result, err := limiter.Allow(ctx, "test_key", redis_rate.PerSecond(10)) require.NoError(t, err) require.Equal(t, 1, result.Allowed) require.Equal(t, 9, result.Remaining) // Test reset err = limiter.Reset(ctx, "test_key") require.NoError(t, err) result, err = limiter.Allow(ctx, "test_key", redis_rate.PerSecond(10)) require.NoError(t, err) require.Equal(t, 1, result.Allowed) require.Equal(t, 9, result.Remaining) } ``` -------------------------------- ### Define Pre-defined Rate Limits Source: https://github.com/go-redis/redis_rate/blob/v10/_autodocs/quick-start.md Create rate limits using convenient pre-defined functions for common intervals like per second, minute, or hour. ```go redis_rate.PerSecond(100) // 100 requests per second ``` ```go redis_rate.PerMinute(1000) // 1000 requests per minute ``` ```go redis_rate.PerHour(10000) // 10000 requests per hour ```