Rate Limiting & Throttling
Rate Limiting কেন শিখতে হবে?
আপনার API তে একজন user প্রতি second এ ১০,০০০ request পাঠাচ্ছে। হয় DDoS attack, নাহয় তার buggy code এ infinite loop। Server overload — অন্য সব users blocked।
Rate Limiting হলো সমাধান — একটা user বা client কতটা request করতে পারবেন সেটা restrict করুন। এটা ছাড়া কোনো production API চালানো উচিত না।
Rate Limiting vs Throttling
Rate Limiting: নির্দিষ্ট time window তে max request count। Limit exceed → reject।
Throttling: Request reject না করে slow করুন বা queue করুন — more graceful।
কেন দরকার? — ৪টা কারণ
SECURITY
Brute force attacks রোধ। Login এ ৫ বার fail → block। DDoS mitigation করুন।
BUSINESS
Free tier: ১০০ req/day। Paid: ১০,০০০ req/day। API monetization এর basis।
FAIRNESS
একজন user সব resources নিতে পারবেন না। সব users fair share পাবেন।
STABILITY
System overload prevent। Buggy client থেকে server protect। SLA maintain।
৪টা Rate Limiting Algorithm
Algorithm Overview
Token Bucket
একটা bucket এ tokens। প্রতি request এ ১টা token consume। Constant rate এ refill। Bucket full হলে tokens overflow হয়।
✅ Burst traffic handle করতে পারে
❌ Implementation কিছুটা complex
Leaky Bucket
Queue এর মতো। Requests constant rate এ "leak" হয়। Queue full হলে reject। Output সবসময় smooth।
✅ Consistent output rate
❌ Burst requests drop হতে পারে
Fixed Window
Fixed time window (১ min) এ count। Window শেষে reset। সবচেয়ে simple algorithm।
✅ সহজ implement করা
❌ Window boundary তে ২x spike possible
Sliding Window
প্রতিটা request এর timestamp রাখুন। Window এর বাইরেরগুলো drop। যেকোনো moment এ last N minutes check।
✅ Most accurate, no boundary issue
❌ High memory — প্রতি request store
Token Bucket — সবচেয়ে Popular
Token Bucket কীভাবে কাজ করে
Rate: 10 tokens/sec, Capacity: 20। User ২ seconds idle থাকলে ২০ tokens জমে। একসাথে ২০ requests করতে পারবেন (burst)। এরপর rate limit শুরু। Token না থাকলে 429 Too Many Requests return করুন।
Fixed Window Boundary Problem
Limit: ১০ req/minute। User 12:00:59 তে ১০টা পাঠালো, তারপর 12:01:01 তে আরো ১০টা। মাত্র ২ seconds এ ২০ requests! Fixed Window এটা allow করে কারণ দুটো আলাদা window। Sliding Window এটা prevent করে।
Multiple Servers এ কীভাবে করবেন?
Single server এ in-memory counter সহজ। কিন্তু ১০টা API server থাকলে? প্রতিটার আলাদা counter থাকলে user ১০x request করতে পারবেন। সমাধান: Shared Redis counter।
Architecture: Client → LB → API Servers → Redis
PROBLEM — Local Counter
API Server 1 এর counter: user→47। API Server 2 এর counter: user→52। সব মিলিয়ে ৯৯ requests, কিন্তু প্রতিটা server আলাদাভাবে limit enforce করছে। User ১০x request করতে পারছে!
SOLUTION — Redis Shared Counter
Redis এ একটাই counter: rate:user123 → 47। সব API servers এই একই counter INCR করে। Limit: 100/min। সব servers একই counter দেখে — consistent enforcement।
Redis INCR + EXPIRE — Atomic Operation
INCR rate_limit:user123:202401011430 — atomic, sub-millisecond।প্রথমবার INCR করলেন key নেই → value 1 তৈরি হয়। তারপর EXPIRE দিয়ে TTL set করুন। Race condition নেই কারণ INCR atomic।
Practical Code
Python: Token Bucket + FastAPI Middleware
import time, threading
from collections import defaultdict
from fastapi import FastAPI, Request, HTTPException
class TokenBucket:
def __init__(self, rate: float, capacity: int):
self.rate = rate # tokens/second
self.capacity = capacity # max tokens (burst size)
self.tokens = capacity # start full
self.last_refill = time.monotonic()
self.lock = threading.Lock()
def consume(self) -> bool:
with self.lock:
now = time.monotonic()
# Add new tokens based on elapsed time
elapsed = now - self.last_refill
self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
self.last_refill = now
if self.tokens >= 1:
self.tokens -= 1
return True # allowed
return False # rate limited
app = FastAPI()
# প্রতি user/API-key এর জন্য আলাদা bucket
# Free: 5/sec, Paid: 50/sec
TIERS = {
"free": {"rate": 5, "capacity": 10},
"paid": {"rate": 50, "capacity": 100},
}
buckets = {}
@app.middleware("http")
async def rate_limit(request: Request, call_next):
api_key = request.headers.get("X-API-Key", "anonymous")
tier = "paid" if api_key.startswith("paid_") else "free"
if api_key not in buckets:
cfg = TIERS[tier]
buckets[api_key] = TokenBucket(cfg["rate"], cfg["capacity"])
bucket = buckets[api_key]
if not bucket.consume():
raise HTTPException(
status_code=429,
detail={"error": "Too Many Requests", "tier": tier},
headers={"Retry-After": "1",
"X-RateLimit-Limit": str(TIERS[tier]["rate"]),
"X-RateLimit-Remaining": "0"}
)
response = await call_next(request)
response.headers["X-RateLimit-Remaining"] = str(int(bucket.tokens))
return response
@app.get("/api/data")
async def get_data():
return {"data": "success"}Node.js: Redis Sliding Window Rate Limiter
const Redis = require('ioredis');
const redis = new Redis();
async function isRateLimited(userId, limit = 100, windowMs = 60000) {
const now = Date.now();
const windowStart = now - windowMs;
const key = `rl:${userId}`;
// Atomic pipeline — all 4 ops run together
const [[, count]] = await redis
.pipeline()
.zremrangebyscore(key, 0, windowStart) // remove old
.zadd(key, now, `${now}`) // add current
.zcard(key) // get count
.pexpire(key, windowMs) // set TTL
.exec()
.then(r => r.slice(2, 3));
return { limited: count > limit, remaining: Math.max(0, limit - count) };
}
// Express middleware
const rateLimit = (limit, windowMs) => async (req, res, next) => {
const key = req.headers['x-user-id'] || req.ip;
const { limited, remaining } = await isRateLimited(key, limit, windowMs);
res.set('X-RateLimit-Limit', limit);
res.set('X-RateLimit-Remaining', remaining);
if (limited) return res.status(429).json({
error: 'Too Many Requests',
retry_after: Math.ceil(windowMs / 1000)
});
next();
};
// Different limits for different endpoints
app.use('/api/login', rateLimit(5, 300000)); // 5 per 5 min
app.use('/api/search', rateLimit(30, 60000)); // 30 per min
app.use('/api/', rateLimit(100, 60000)); // 100 per minReal World Rate Limits
| API / Service | Limit | Algorithm | 429 Response |
|---|---|---|---|
| GitHub API | ৬০ req/hr (unauth), ৫০০০ (auth) | Fixed Window | 403 + X-RateLimit headers |
| Twitter API v2 | ১৫ req/15 min (basic) | Sliding Window | 429 + Retry-After |
| Stripe | ১০০ req/sec | Token Bucket | 429 + retry suggestion |
| OpenAI | RPM + TPM (tier based) | Token Bucket | 429 + error.type |
| AWS API Gateway | ১০,০০০ req/sec default | Token Bucket | 429 ThrottlingException |
| Cloudflare WAF | Custom rules | Token Bucket | Block / Challenge / Log |
Production Headers & Tools
STANDARD HEADERS
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1706000000
Retry-After: 30
PRODUCTION TOOLS
nginx: limit_req_zone module
Kong: Rate Limiting plugin
AWS WAF: Rate-based rules
Traefik: RateLimit middleware
Common Interview Questions
Q1: "Design a Rate Limiter" — কীভাবে approach করবেন?
1) Requirements clarify: per user? per IP? per API key? 2) Single server নাকি distributed? 3) Algorithm: Token Bucket (versatile)। 4) Storage: Redis (atomic INCR) 5) Response: 429 + Retry-After + X-RateLimit headers। 6) Edge cases: distributed clocks, Redis failure।
Q2: Fixed Window এর boundary problem কী? কীভাবে solve করবেন?
Window শেষে ও শুরুতে burst possible। User ২ seconds এ ২x limit use করতে পারে। Solution: Sliding Window Log (exact timestamps) অথবা Sliding Window Counter (approximate but fast)।
Q3: Rate Limiting কোথায় implement করা উচিত?
Best: API Gateway বা Load Balancer এ — upstream এ। Backend services পর্যন্ত excess traffic পৌঁছায় না। Also: Per-service level এ different limits। Avoid: Client side (bypass করা যায়)।
Interview এ এটা বলুন
Nginx দিয়ে Rate Limiting Setup
Production এ সবচেয়ে common approach হলো Nginx এ rate limiting configure করা। limit_req_zone এবং limit_req directive ব্যবহার করে সহজেই endpoint-specific limits দেওয়া যায়।
# Rate limit zones define করুন (http block এ)
http {
# Zone 1: login endpoint — per IP, 5 req/5min
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/m;
# Zone 2: API — per user (X-User-Id header), 100/min
limit_req_zone $http_x_user_id zone=api:20m rate=100r/m;
# Zone 3: search — 30/min per IP
limit_req_zone $binary_remote_addr zone=search:10m rate=30r/m;
server {
listen 80;
# Login: strict — 5 per 5min, no burst
location /api/login {
limit_req zone=login burst=5 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
# Search: moderate burst allowed
location /api/search {
limit_req zone=search burst=10;
limit_req_status 429;
proxy_pass http://backend;
}
# General API: 100/min, burst of 20
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
# Custom 429 response
error_page 429 /rate_limit.json;
proxy_pass http://backend;
}
location = /rate_limit.json {
internal;
default_type application/json;
return 429 '{"error":"Too Many Requests","retry_after":60}';
}
}
}Nginx Rate Limiting Tips
burst মানে temporary spike allow। burst=20 মানে extra ২০টা request queue এ রাখা যাবেন। nodelay মানে burst requests queue না করে immediately process করুন। Login এ burst কম রাখুন, general API তে বেশি রাখুন।
Tiered Rate Limiting — API Monetization
Real world API তে different users different limits পায়। Free, Basic, Premium — প্রতিটা tier এর আলাদা rate limit। এটাই API monetization এর মূল ভিত্তি।
| Tier | Rate Limit | Burst | Price |
|---|---|---|---|
| Free | ১০০ req/day | 5 req burst | বিনামূল্যে |
| Basic | ১০,০০০ req/day | 50 req burst | $9/month |
| Premium | ১,০০,০০০ req/day | 200 req burst | $99/month |
| Enterprise | Unlimited | Custom | Custom pricing |
Implementation Pattern
1) X-API-Key header থেকে tier identify করুন (JWT claim বা DB lookup)। 2) Redis key: rate_limit:{api_key} → tier-specific limit apply। 3) Response headers এ remaining count ও reset time জানাও। Stripe, OpenAI, GitHub সবাই এই pattern follow করে।
SUMMARY — আজকে যা শিখলাম
| Concept | এক লাইনে |
|---|---|
| Rate Limiting | Time window এ max request — reject exceed করলেন |
| Token Bucket | Burst allow — API তে সবচেয়ে popular |
| Leaky Bucket | Constant output — smooth traffic |
| Fixed Window | Simple কিন্তু boundary spike problem |
| Sliding Window | Accurate কিন্তু memory intensive |
| Redis INCR | Distributed shared counter — atomic |
| 429 + Retry-After | Standard rate limit response |