Hammer.Redis.FixWindow (hammer_backend_redis v7.0.2)
View SourceThis module implements the Fix Window algorithm.
The fixed window algorithm works by dividing time into fixed intervals or "windows" of a specified duration (scale). Each window tracks request counts independently.
For example, with a 60 second window:
- Window 1: 0-60 seconds
- Window 2: 60-120 seconds
- And so on...
The algorithm:
- When a request comes in, we:
- Calculate which window it belongs to based on current time
- Increment the counter for that window
- Store expiration time as end of window
- To check if rate limit is exceeded:
- If count <= limit: allow request
- If count > limit: deny and return time until window expires
- Old windows are automatically cleaned up after expiration
This provides simple rate limiting but has edge cases where a burst of requests spanning a window boundary could allow up to 2x the limit in a short period. For more precise limiting, consider using the sliding window algorithm instead.
The fixed window algorithm is a good choice when:
- You need simple, predictable rate limiting with clear time boundaries
- The exact precision of the rate limit is not critical
- You want efficient implementation with minimal storage overhead
- Your use case can tolerate potential bursts at window boundaries
Common use cases include:
- Basic API rate limiting where occasional bursts are acceptable
- Protecting backend services from excessive load
- Implementing fair usage policies
- Scenarios where clear time-based quotas are desired (e.g. "100 requests per minute")
The main tradeoff is that requests near window boundaries can allow up to 2x the intended limit in a short period. For example with a limit of 100 per minute:
- 100 requests at 11:59:59
- Another 100 requests at 12:00:01
This results in 200 requests in 2 seconds, while still being within limits. If this behavior is problematic, consider using the sliding window algorithm instead.
Example usage:
defmodule MyApp.RateLimit do
use Hammer, backend: Hammer.Redis, algorithm: :fix_window
end
MyApp.RateLimit.start_link(clean_period: :timer.minutes(1))
# Allow 10 requests per second
MyApp.RateLimit.hit("user_123", 1000, 10)