Redis Backend Integration
Relevant source files
The following files were used as context for generating this wiki page:
The Redis Backend Integration serves as the persistence and atomic execution layer for the FastLimit library. It is designed to handle high-throughput rate limiting by offloading complex logic to Redis via Lua scripts, ensuring atomicity across distributed application instances.
This integration manages connection pooling, script caching (EVALSHA), and provides a unified interface for the different rate-limiting algorithms: Fixed Window, Token Bucket, and Sliding Window. By utilizing integer-based arithmetic (1000x multiplier) within the backend, it maintains high precision for floating-point rates without the overhead of floating-point operations in Redis.
Sources: fastlimit/backends/redis.py:27-40, README.md:12-25
Architecture and Components
The backend architecture centers around the RedisBackend class, which acts as a bridge between the high-level RateLimiter and the Redis storage. It manages the lifecycle of Redis connections and the registration of Lua scripts.
Backend Workflow
The following diagram illustrates the flow from a rate limit check request to the execution of optimized Lua scripts in Redis.
The integration uses a failover mechanism where it attempts to use EVALSHA for performance; if the script is missing from the Redis cache (NoScriptError), it falls back to a standard EVAL call.
Sources: fastlimit/backends/redis.py:165-185, fastlimit/limiter.py:145-160
Connection Management
The backend utilizes redis.asyncio for non-blocking operations and implements connection pooling to optimize resource usage.
| Feature | Description |
|---|---|
| Connection Pooling | Configurable via max_connections (default 50). |
| Timeouts | Supports socket_connect_timeout and socket_timeout. |
| Security | Automatically redacts passwords from Redis URLs in logs. |
| Time Sync | Uses get_redis_time() to fetch server time for window consistency. |
Sources: fastlimit/backends/redis.py:110-130, fastlimit/backends/redis.py:460-485, README.md:200-210
Atomic Algorithm Execution
To prevent race conditions in distributed environments, all core rate-limiting logic is implemented in Lua scripts executed within Redis.
1. Fixed Window
The fixed window implementation uses the INCRBY command followed by EXPIREAT to manage window boundaries. It aligns windows to the Unix epoch to prevent boundary burst issues.
# Fixed window preparation in limiter.py
current_time = redis_time_seconds
time_window = get_time_window(window_seconds, current_time)
window_end = int(time_window) + window_seconds
full_key = generate_key(prefix, key, tenant, time_window)
Sources: fastlimit/limiter.py:164-175, fastlimit/backends/redis.py:55-80
2. Token Bucket
The token bucket algorithm stores its state (tokens and last_refill_ms) in a Redis Hash. The backend calculates the refill amount based on the elapsed time since the last request using millisecond precision.
3. Sliding Window
The sliding window requires the backend to manage two keys (current and previous windows). The Lua script performs a weighted average calculation to determine if the request is within the limit.
Sources: fastlimit/algorithms/sliding_window.py:75-100, fastlimit/backends/redis.py:320-350
Key Generation and Sanitization
Redis keys are generated using a hierarchical structure to ensure isolation and prevent collisions.
Key Structure
Keys follow the pattern: {prefix}:{identifier}:{tenant_type}:{window_info}.
- Prefix: Namespace for the application (default:
ratelimit). - Identifier: URL-encoded unique ID (IP, User ID).
- Tenant Type: Isolated tier (e.g.,
free,premium). - Window Info: Epoch-aligned timestamp or static suffix like
bucket.
Sources: fastlimit/utils.py:80-105, fastlimit/utils.py:125-150
Sanitization and Optimization
The backend applies specific safety measures to identifiers:
- URL Encoding: Colons (
:), spaces, and Redis special characters (*,?,[) are encoded to prevent key injection or parsing errors. - Hash Optimization: If a generated key exceeds 200 characters, it is hashed using SHA-256 to maintain Redis performance while preserving a readable prefix for debugging.
Sources: fastlimit/utils.py:125-145%20are%20encoded%20to%20prevent%20key%20injection%20or%20parsing%20errors.%0A2.%20Hash%20Optimization%3A%20If%20a%20generated%20key%20exceeds%20200%20characters%2C%20it%20is%20hashed%20using%20SHA-256%20to%20maintain%20Redis%20performance%20while%20preserving%20a%20readable%20prefix%20for%20debugging.%0A%0ASources%3A%20%5Bfastlimit/utils.py%3A125-145), fastlimit/utils.py:185-210
Internal Data Models
The backend utilizes specific data structures to communicate check results and usage statistics back to the application.
RateLimitResult (NamedTuple)
Used internally between RedisBackend and RateLimiter.
allowed(bool): Whether the request passed.remaining(int): Tokens/requests left (with 1000x multiplier).retry_after(int): Milliseconds until the next allowed request.
Redis Time Handling
To account for clock skew between multiple application servers, the backend explicitly requests the current time from Redis:
get_redis_time(): Returns(seconds, microseconds).get_redis_time_ms(): Returns total milliseconds.
Sources: fastlimit/backends/redis.py:18-24, fastlimit/backends/redis.py:435-455
Usage and Health Monitoring
The backend provides methods to inspect the state of rate limits without consuming tokens.
| Method | Algorithm | Returned Data |
|---|---|---|
get_usage | Fixed Window | Current count and TTL. |
get_token_bucket_usage | Token Bucket | Current tokens and last refill timestamp. |
health_check | All | Boolean status via Redis PING. |
Sources: fastlimit/backends/redis.py:290-310, fastlimit/backends/redis.py:410-430, fastlimit/limiter.py:420-440
Conclusion
The Redis Backend Integration is the performance-critical core of FastLimit. By combining atomic Lua script execution with intelligent key sanitization and server-side time synchronization, it provides a robust foundation for distributed rate limiting. The use of integer-only math ensures that precision is maintained across all algorithms without sacrificing the low-latency requirements of high-performance Python applications.