DEV Community

Cover image for Fixed Window Rate Limiting: Concept, Examples, and Java Implementation

Fixed Window Rate Limiting: Concept, Examples, and Java Implementation

πŸ“Œ What Is Fixed Window Rate Limiting?

Fixed Window Rate Limiting is a straightforward algorithm that controls request rates by dividing time into fixed intervals (windows) and allowing a maximum number of requests per window.

Example:

If an API allows 100 requests per minute:

  • The counter resets at the start of each minute.
  • A user making 100 requests at 00:59:59 can immediately make 100 more after 01:00:00. This can cause sudden bursts at window boundaries.

πŸ“ Example Scenario

  • Use Case: Login API with a limit of 10 attempts per minute.
  • Behavior:
    • A user can try 10 times in the current minute.
    • After the window resets, the counter refreshes, allowing another 10 attempts.

βœ… Benefits

  • Simple and easy to implement.
  • Minimal overhead and resource usage.
  • Easy to debug and understand.

⚠️ Limitations

  • Burstiness: Allows spikes at window boundaries.
  • Precision: Less smooth than sliding window methods.
  • Distributed Challenges: Single in-memory counter won’t work across multiple instances without central coordination.

πŸ’» Single-Machine Implementation (Thread-Safe Java)

When to Use:

  • Small-scale services.
  • Non-critical endpoints.
  • Internal services where distributed coordination isn’t needed.

Code for Single-Machine Fixed Window Rate Limiter:

import java.time.Duration; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; /** * Single-machine Fixed Window Rate Limiter. * Thread-safe implementation for small-scale services. */ public class FixedRateLimiter implements IRateLimiter { private final Timer timer; // Provides current time (injectable for testing) private final Map<String, FixedWindow> map; // Stores request counts per user/requestId private final Duration windowSize; // Size of fixed time window private final int capacity; // Max requests per window public FixedRateLimiter(Duration windowSize, int capacity, Timer timer) { this.timer = timer; this.map = new ConcurrentHashMap<>(); this.windowSize = windowSize; this.capacity = capacity; } /** * Checks if a request is allowed for the given requestId. * * @param requestId Unique identifier for a client/user * @return true if allowed, false if rate limit exceeded */ @Override public boolean isAllowed(String requestId) { long currentTimeMillis = timer.currentTimeMillis(); // Get or create FixedWindow for this requestId FixedWindow fixedWindow = map.computeIfAbsent( requestId, e -> new FixedWindow(new AtomicInteger(capacity), currentTimeMillis) ); // Reset window if current time exceeds previous window if (currentTimeMillis - fixedWindow.lastAccessTime > windowSize.toMillis()) { synchronized (fixedWindow) { if (currentTimeMillis - fixedWindow.lastAccessTime > windowSize.toMillis()) { fixedWindow.lastAccessTime = currentTimeMillis; fixedWindow.requestCount.set(capacity); // Reset request count } } } // Allow request if capacity remains int remaining = fixedWindow.requestCount.get(); if (remaining > 0) { fixedWindow.requestCount.decrementAndGet(); return true; } return false; // Deny if limit reached } /** * Internal class to hold request count and last access time per window. */ private static class FixedWindow { final AtomicInteger requestCount; // Tracks remaining requests volatile long lastAccessTime; // Timestamp of window start FixedWindow(AtomicInteger requestCount, long lastAccessTime) { this.requestCount = requestCount; this.lastAccessTime = lastAccessTime; } } /** * Timer abstraction for easy testing (injectable current time provider) */ public record Timer() { public long currentTimeMillis() { return System.currentTimeMillis(); } } } 
Enter fullscreen mode Exit fullscreen mode

🌐 Distributed Implementation (Redis + Java)

When to Use:

  • High-scale APIs across multiple instances.
  • Requires central coordination to prevent users from exceeding limits globally.

Code for Redis-Based Fixed Window Rate Limiter:

import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; /** * Distributed Fixed Window Rate Limiter using Redis. * Suitable for multi-instance applications. */ public class RedisFixedRateLimiter { private final JedisPool jedisPool; // Redis connection pool private final String rateLimitKeyPrefix; // Prefix for Redis keys, e.g., "rate_limit:" private final int maxRequestsPerWindow; // Max requests allowed per window private final int windowDurationInSeconds; // Window size in seconds public RedisFixedRateLimiter(JedisPool jedisPool, String rateLimitKeyPrefix, int maxRequestsPerWindow, int windowDurationInSeconds) { this.jedisPool = jedisPool; this.rateLimitKeyPrefix = rateLimitKeyPrefix; this.maxRequestsPerWindow = maxRequestsPerWindow; this.windowDurationInSeconds = windowDurationInSeconds; } /** * Checks if a request is allowed for a given userId. * * @param userId Unique identifier for the client/user * @return true if request is allowed, false if rate limit exceeded */ public boolean isRequestAllowed(String userId) { String key = rateLimitKeyPrefix + userId; try (Jedis jedis = jedisPool.getResource()) { // Atomically increment the request count in Redis long currentCount = jedis.incr(key); // Set expiration only for the first request in the window if (currentCount == 1) { jedis.expire(key, windowDurationInSeconds); } // Allow if count does not exceed the maximum return currentCount <= maxRequestsPerWindow; } catch (Exception e) { // Fail-open: allow requests if Redis is unavailable e.printStackTrace(); return true; } } } 
Enter fullscreen mode Exit fullscreen mode

⚑ Fault Tolerance for Redis

  • Redis Down: Implement a fail-open policy (allow requests) or local fallback counters.
  • Circuit Breaker: Temporarily stop excessive traffic when Redis is unreachable.
  • Graceful Degradation: Use in-memory counters with a short TTL to limit impact until Redis recovers.

βœ… Summary

  • Fixed Window is simple, efficient, and effective for many straightforward use cases.
  • Main limitation: bursts at window boundaries.
  • Single-machine approach: Suitable for low-traffic, non-critical services.
  • Redis-based approach: Ideal for distributed high-traffic environments but requires fault-tolerance planning.

Top comments (0)