A distributed rate limiter with an AI-powered anomaly detection layer that dynamically adjusts client limits based on suspicious traffic patterns.
Implemented so far:
- FastAPI gateway with a
/healthand a rate-limited/api/pingendpoint - Token Bucket algorithm implemented as an atomic Redis Lua script (avoids race conditions under concurrent requests)
- Centralized, environment-based configuration
Unlike a naive "max N requests per minute" counter, Token Bucket allows short bursts of traffic while still enforcing a long-term average rate — this is the same approach used by AWS, Stripe, and GitHub's public APIs.
Client → FastAPI (/api/ping) → rate_limiter() dependency → Redis (Lua script)
↓
allowed / rejected (429)
- Install Redis (see setup instructions)
- Create a virtual environment and install dependencies:
python3 -m venv venv source venv/bin/activate pip install -r requirements.txt - Copy
.env.exampleto.env - Run the app:
uvicorn app.main:app --reload - Test it:
curl http://127.0.0.1:8000/health curl http://127.0.0.1:8000/api/ping
- Week 1: Token Bucket rate limiter (FastAPI + Redis, atomic Lua script)
- Week 2: Fixed Window + Sliding Window algorithms, load-test comparison
- Week 3: Async request logging + anomaly detection worker
- Week 4: Dynamic policy adjustment based on detected anomalies
- Week 5: Grafana dashboard
- Week 6: Deployment + demo video
To validate the theoretical tradeoffs between the three algorithms, I ran a controlled load test: 60 requests fired ~0.15s apart (≈6.7 req/sec) against each algorithm, with a 3-second window / 10-request limit.
Token Bucket — allowed the entire burst. At this arrival rate, the 5 tokens/sec refill rate combined with the 20-token starting capacity was enough to keep up without ever fully depleting within the 9-second test. It degrades gracefully rather than hard-cutting traffic.
Fixed Window — shows the classic "boundary burst" problem clearly: requests are allowed right after each window reset (dashed lines), then hard-rejected for the rest of that window, repeating every 3 seconds. A client can exploit this by timing bursts around window boundaries.
Sliding Window — avoids hard resets. Rejections start slightly before each boundary and allowances resume slightly after it, because the window is recalculated continuously from the current moment rather than snapping to a fixed clock tick. The effect is subtler than Fixed Window's but reflects the same underlying fix: no fixed edge to exploit.
Takeaway: Fixed Window is simplest but exploitable at window edges. Sliding Window fixes this at the cost of storing one entry per request per client. Token Bucket is the industry-standard middle ground — it permits natural bursts while enforcing a true long-term average rate, which is why it's the default algorithm in this project.
