How It Works
The Fixed Window Counter is the most straightforward rate limiting algorithm. It divides time into fixed windows (e.g., 60 seconds) and counts requests within each window.
Algorithm Steps
- Define a time window (e.g., 60 seconds) and a request limit (e.g., 5 requests)
- When a request arrives, increment the counter for the current window
- If counter ≤ limit, allow the request
- If counter > limit, block the request
- When the window expires, reset the counter to 0
Implementation
In Redis, this is typically implemented using a simple key-value pair where:
- The key is the identifier (e.g.,
rate:api_key_123) - The value is the request count
- An expiry (TTL) is set for the duration of the window
const WINDOW = 60; // seconds
const LIMIT = 5;
async function fixedWindowLimiter(apiKey: string) {
const key = `rate:${apiKey}`;
const count = await redis.incr(key);
if (count === 1) {
await redis.expire(key, WINDOW);
}
return count <= LIMIT;
}Advantages
- Simple implementation: Just a counter and expiry
- Memory efficient: Only stores one integer per key
- Fast: Single Redis operation per request
- Easy to reason about: Clear boundaries and limits
Disadvantages
- Boundary problem: Users can make 2× the limit by timing requests at window boundaries (e.g., 5 requests at 0:59, then 5 more at 1:00)
- Traffic spikes: All quota resets at once, potentially causing synchronized bursts
- Unfair distribution: Early requests in a window have advantage over later ones
Best Use Cases
- Internal APIs where boundary exploitation isn't a concern
- Systems with relatively low traffic where precision isn't critical
- Scenarios where simplicity and performance are prioritized
- Rate limiting for non-critical resources
Real-World Example
Imagine an API that allows 100 requests per minute. With Fixed Window:
- Window starts at 12:00:00
- User makes 100 requests by 12:00:30
- All subsequent requests are blocked until 12:01:00
- At 12:01:00, counter resets and user has full 100 requests again
The problem: user could make 100 requests at 12:00:59 and another 100 at 12:01:00, effectively getting 200 requests in 2 seconds.
Variations
Some implementations use multiple windows with different limits:
- 10 requests per second
- 100 requests per minute
- 1000 requests per hour
This provides protection at multiple time scales but doesn't solve the boundary problem.