Rate
Senior JavaLimiting
Interview Guide
Token Bucket · Sliding Window · Redis Distributed
Token Bucket Sliding Window Fixed Window Leaky Bucket
1. What is Rate Limiting?
Rate limiting controls how many requests a client can make in a given time window. Think of it as a bouncer —
'you can make 100 requests per minute, after that you wait.' It protects your services from abuse, DDoS attacks,
and accidental overload.
Rate limiting = protecting your system from being overwhelmed — intentionally or accidentally.
2. The 4 Algorithms — Tradeoffs
Algorithm Time Space Burst? Best For
Fixed Window O(1) O(1) Yes (boundary!) Simple internal APIs
Sliding Window O(1) O(n) No Public APIs, accuracy needed
Token Bucket O(1) O(1) Yes (controlled) Microservices, most common
Leaky Bucket O(1) O(n) No (queue drops) Stream processing
Token Bucket is the most common interview answer — it's what AWS, Stripe, and most production systems use.
8 Years Java · Microservices · Fintech / Healthcare Token Bucket · Sliding Window · Redis
Rate Limiting in Java — Senior Interview Guide Page 2
3. Level 1 — Simple In-Memory (Single Instance)
Start here in the interview. Show you understand the core mechanics before jumping to Redis.
TokenBucket core logic:
public class RateLimiter {
private final int maxRequests;
private final long refillRateMs;
private final ConcurrentHashMap<String, TokenBucket> buckets
= new ConcurrentHashMap<>();
public boolean isAllowed(String clientId) {
TokenBucket bucket = [Link](
clientId, id -> new TokenBucket(maxRequests));
return [Link]();
}
private class TokenBucket {
private final AtomicLong tokens;
private volatile long lastRefillTime;
synchronized boolean tryConsume() {
refill();
if ([Link]() > 0) {
[Link]();
return true; // ALLOWED
}
return false; // RATE LIMITED
}
private void refill() {
long now = [Link]();
long elapsed = now - lastRefillTime;
long tokensToAdd = elapsed / refillRateMs;
if (tokensToAdd > 0) {
[Link]([Link](maxRequests,
[Link]() + tokensToAdd));
lastRefillTime = now;
}
}
}
}
Usage:
// Allow 10 requests, refill 1 token every 100ms (= 10 req/sec)
RateLimiter limiter = new RateLimiter(10, 100);
if ([Link]("user-123")) {
// process request
} else {
throw new TooManyRequestsException("Rate limit exceeded");
}
4. Level 2 — Spring Boot Filter
8 Years Java · Microservices · Fintech / Healthcare Token Bucket · Sliding Window · Redis
Rate Limiting in Java — Senior Interview Guide Page 3
Wire the rate limiter into your HTTP pipeline as a servlet filter. Returns 429 Too Many Requests with proper
headers.
@Component
@Order(1)
public class RateLimitFilter implements Filter {
private final RateLimiter rateLimiter;
@Override
public void doFilter(ServletRequest req,
ServletResponse res,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String clientId = extractClientId(request);
if () {
[Link](429); // 429 Too Many Requests
[Link]("Retry-After", "1");
[Link]("X-RateLimit-Limit", "100");
[Link]().write("Rate limit exceeded.");
return;
}
[Link](req, res);
}
private String extractClientId(HttpServletRequest req) {
String apiKey = [Link]("X-API-Key");
if (apiKey != null) return apiKey;
String auth = [Link]("Authorization");
if (auth != null) return auth;
return [Link](); // fallback to IP
}
}
8 Years Java · Microservices · Fintech / Healthcare Token Bucket · Sliding Window · Redis
Rate Limiting in Java — Senior Interview Guide Page 4
5. Level 3 — Redis Distributed (Senior Answer)
This is the answer that wins senior interviews — thread-safe, distributed, production-grade.
Single-instance rate limiting is useless if you have 10 pods. Redis gives you a shared, atomic counter across all
instances using a Lua script to ensure no race conditions.
Sliding window with Redis Sorted Set + Lua script:
@Service
public class DistributedRateLimiter {
private final StringRedisTemplate redis;
private final int maxRequests;
private final int windowSeconds;
public boolean isAllowed(String clientId) {
String key = "rate_limit:" + clientId;
long now = [Link]();
long windowStart = now - (windowSeconds * 1000L);
// Lua = atomic: no race conditions between pods
String lua = """
local key = KEYS[1]
local now = tonumber(ARGV[1])
local windowStart = tonumber(ARGV[2])
local maxRequests = tonumber(ARGV[3])
local windowSecs = tonumber(ARGV[4])
-- Remove old timestamps outside window
[Link]('ZREMRANGEBYSCORE', key, 0, windowStart)
local count = [Link]('ZCARD', key)
if count < maxRequests then
[Link]('ZADD', key, now, now)
[Link]('EXPIRE', key, windowSecs)
return 1 -- allowed
end
return 0 -- blocked""";
Long result = [Link](
new DefaultRedisScript<>(lua, [Link]),
[Link](key),
[Link](now),
[Link](windowStart),
[Link](maxRequests),
[Link](windowSeconds)
);
return [Link](1).equals(result);
}
}
8 Years Java · Microservices · Fintech / Healthcare Token Bucket · Sliding Window · Redis
Rate Limiting in Java — Senior Interview Guide Page 5
6. Interviewer Follow-up Questions
Be ready for these — they separate senior from mid-level answers.
Q: Why Lua script in Redis?
ZREMRANGEBYSCORE + ZCARD + ZADD are three separate operations. Without Lua, a race condition between two pods
Q: What HTTP status code for rate limiting?
429 Too Many Requests with a Retry-After header telling the client when to retry. Never use 503 — that implies the service is
Q: How do you handle distributed rate limiting across datacenters?
Use Redis Cluster with cross-datacenter replication. Accept eventual consistency — a small overage is acceptable vs. the lat
Q: How do you rate limit at different levels?
Tiered limits: per IP (strictest), per API key, per user plan (free/pro/enterprise). Each has its own bucket with different capacit
Q: Why not just use a database counter?
Database round-trips add 5-20ms per request. Redis operations are sub-millisecond. At high traffic, the DB becomes the bott
Q: Token bucket vs sliding window — which to use?
Token bucket: allows short bursts, great for microservices. Sliding window: no bursts possible, best for public APIs where fair
7. Quick Reference
Concept Answer
HTTP status for rate limit 429 Too Many Requests
Header for retry timing Retry-After: <seconds>
Best algorithm for interview Token Bucket
Thread safety (single node) synchronized + AtomicLong
Distributed safety Redis Lua script (atomic)
Redis data structure Sorted Set (ZADD / ZCARD)
Real-world libraries Resilience4j, Guava RateLimiter, Bucket4j
Spring integration point Filter, Interceptor, or AOP @Aspect
Senior tip: always mention Redis Lua script for distributed safety — that single detail separates mid-level from senior answers
8 Years Java · Microservices · Fintech / Healthcare Token Bucket · Sliding Window · Redis