Rewrite 7 crate READMEs to reflect current architecture: correct model types (DQN/PPO/TFT/Mamba2), AtomicKillSwitch, real EnsembleConfig source from ml, actual data crate purpose, web-dashboard project details, ml_training_service ports. Fix 5 api_gateway/TLI docs: strip swarm agent framing, update service endpoints to api_gateway:50050, remove deleted dashboard references and hardcoded paths. Add missing web-gateway/README.md documenting 24 REST endpoints, WebSocket support, JWT auth, and 3-tier rate limiting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9.4 KiB
Rate Limiter Implementation
Note: HTTP-level rate limiting for the web dashboard is now handled by
web-gateway(3 tiers: auth 10/min, trading 200/min, compute 30/min). This document describes the gRPC-level rate limiting in api_gateway.
Overview
High-performance token bucket rate limiting system with Redis backend and in-memory caching optimized for HFT requirements.
Architecture
Token Bucket Algorithm
The rate limiter uses the token bucket algorithm which provides:
- Smooth rate limiting - requests consume tokens from a bucket
- Burst handling - bucket capacity allows short bursts up to limit
- Automatic refill - tokens refill at a constant rate over time
Two-Tier Architecture
┌─────────────────────────────────────────────────────────┐
│ Rate Limiter │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. In-Memory Cache (LRU, 10,000 entries) │
│ └─ HashMap<String, TokenBucket> │
│ └─ Target: <50ns per check (CACHE HIT) │
│ └─ TTL: 1 second │
│ │
│ 2. Redis Backend (Lua scripts) │
│ └─ Atomic token bucket operations │
│ └─ Target: <500μs per check (CACHE MISS) │
│ └─ Distributed rate limiting across instances │
│ │
└─────────────────────────────────────────────────────────┘
Performance Characteristics
Measured Performance
Based on standalone benchmarks:
| Operation | Target | Measured | Status |
|---|---|---|---|
| Cache Hit | <50ns | 25ns | ✓ EXCELLENT |
| Token Bucket | N/A | 625ns | ✓ |
| Burst Handling | N/A | 41ns/req | ✓ |
| HFT Scenario | N/A | 27ns/check | ✓ |
Performance Breakdown
-
In-Memory Cache Hit: 25-41ns
- HashMap lookup: ~5ns
- Token bucket check: ~10ns
- Refill calculation: ~10ns
- Lock overhead: ~10ns
-
Redis Backend: <500μs (estimated)
- Network RTT: ~100-200μs (same AZ)
- Lua script execution: ~50-100μs
- Serialization: ~50μs
- Connection pool overhead: ~50μs
Implementation Details
Rate Limit Configurations
Pre-configured limits for common endpoints:
// Trading endpoints (high frequency)
trading.submit_order:
- Capacity: 100 tokens
- Refill rate: 100 tokens/second
- Burst size: 10 requests
// Configuration updates (moderate frequency)
config.update:
- Capacity: 10 tokens
- Refill rate: 10 tokens/second
- Burst size: 2 requests
// Backtesting (low frequency)
backtesting.run:
- Capacity: 5 tokens
- Refill rate: 5 tokens/60 seconds (5/min)
- Burst size: 1 request
// Default (for unlisted endpoints)
default:
- Capacity: 50 tokens
- Refill rate: 50 tokens/second
- Burst size: 5 requests
Redis Lua Script
Atomic token bucket implementation in Redis:
local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local refill_rate = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
-- Get current bucket state
local bucket = redis.call('HGETALL', key)
local tokens = capacity
local last_refill = now
-- Parse existing state
if #bucket > 0 then
for i = 1, #bucket, 2 do
if bucket[i] == 'tokens' then
tokens = tonumber(bucket[i + 1])
elseif bucket[i] == 'last_refill' then
last_refill = tonumber(bucket[i + 1])
end
end
end
-- Refill tokens based on elapsed time
local elapsed = now - last_refill
tokens = math.min(capacity, tokens + (elapsed * refill_rate))
-- Check if request is allowed
if tokens >= 1 then
tokens = tokens - 1
redis.call('HSET', key, 'tokens', tokens, 'last_refill', now)
redis.call('EXPIRE', key, 300) -- 5 minute TTL
return 1
else
redis.call('HSET', key, 'tokens', tokens, 'last_refill', now)
redis.call('EXPIRE', key, 300)
return 0
end
LRU Cache Management
// Cache configuration
max_cache_size: 10,000 entries
cache_ttl: 1 second
// Eviction policy
- When cache reaches 10,000 entries
- Evict oldest 10% (1,000 entries)
- Based on last_access timestamp
- Automatic on cache miss
Integration Points
AuthInterceptor Integration
// In services/api_gateway/src/auth/interceptor.rs
impl AuthInterceptor {
pub async fn intercept(&self, request: Request<()>) -> Result<Request<()>, Status> {
// ... JWT validation ...
// Layer 6: Rate Limiting (<50ns)
let endpoint = extract_endpoint(&request);
if !self.rate_limiter
.check_limit(&claims.user_id, endpoint)
.await
.map_err(|_| Status::internal("Rate limit check failed"))?
{
return Err(Status::resource_exhausted("Rate limit exceeded"));
}
// ... continue processing ...
}
}
Configuration Loading
Rate limit configurations are loaded from PostgreSQL:
-- Example: Update rate limit for endpoint
UPDATE rate_limit_config
SET capacity = 200, refill_rate = 200
WHERE endpoint = 'trading.submit_order';
-- Triggers PostgreSQL NOTIFY for hot-reload
NOTIFY config_updates, 'rate_limit_config';
Testing
Unit Tests
# Run rate limiter unit tests
cargo test -p api_gateway rate_limiter
# Expected output:
# test rate_limiter::tests::test_token_bucket_basic ... ok
# test rate_limiter::tests::test_token_bucket_refill ... ok
# test rate_limiter::tests::test_rate_limit_configs ... ok
Benchmarks
# Run performance benchmarks
rustc services/api_gateway/benches/rate_limiter_bench.rs -O && ./rate_limiter_bench
# Expected output:
# Cache hit: 25 ns (target <50ns) ✓
# Token bucket: 625 ns ✓
# Burst handling: 41 ns ✓
# HFT scenario: 27 ns ✓
Integration Testing
# Start Redis for testing
docker run -d -p 6379:6379 redis:7-alpine
# Run integration tests (when implemented)
cargo test -p api_gateway --test rate_limiter_integration
Redis Setup
Development
# Docker
docker run -d -p 6379:6379 --name foxhunt-redis redis:7-alpine
# Or local Redis
redis-server --port 6379
Production
# Environment variables
export REDIS_URL="redis://redis-cluster.internal:6379"
export RATE_LIMITER_CACHE_SIZE=10000
export RATE_LIMITER_CACHE_TTL_SECONDS=1
Monitoring
Metrics to Track
-
Cache Hit Rate
- Target: >95% hit rate
- Monitor:
cache_hits / (cache_hits + cache_misses)
-
Latency Distribution
- p50: <30ns (cache hits)
- p95: <100ns (cache hits)
- p99: <1μs (Redis hits)
-
Rate Limit Violations
- Track denied requests per endpoint
- Alert on unusual patterns
Cache Statistics
// Get current cache statistics
let stats = rate_limiter.get_cache_stats().await;
println!("Cache size: {}/{}", stats.size, stats.max_size);
println!("Cache TTL: {} seconds", stats.ttl_seconds);
Future Enhancements
-
Dynamic Configuration
- Load rate limits from PostgreSQL config table
- Hot-reload on configuration changes via NOTIFY/LISTEN
-
Advanced Metrics
- Prometheus metrics integration
- Per-endpoint rate limit statistics
- User-level quota tracking
-
Sliding Window Algorithm
- Optional sliding window rate limiting
- More accurate but slightly higher overhead
-
Distributed Cache
- Redis as primary cache (shared across instances)
- Local in-memory as L2 cache
- Eventual consistency acceptable for rate limiting
Files Created
services/api_gateway/src/routing/
├── mod.rs # Module exports
└── rate_limiter.rs # Token bucket implementation
services/api_gateway/benches/
└── rate_limiter_bench.rs # Performance benchmarks
docs/
└── RATE_LIMITER_IMPLEMENTATION.md # This file
Performance Validation
Benchmark Results
Rate Limiter Performance Benchmarks
========================================
Benchmark 1: Cache Hit Performance (in-memory)
Total time: 25.167394ms
Operations: 1000000
Time per operation: 25 ns
Target: <50ns ✓
Benchmark 2: Token Bucket Refill Overhead
Total time: 62.599479ms
Operations: 100000
Time per operation: 625 ns
(includes 10μs sleeps every 100 operations)
Benchmark 3: Burst Handling (100 requests at once)
Total time: 4.177µs
Allowed requests: 100/100
Average per request: 41 ns
Benchmark 4: HFT Scenario (10,000 requests, 100 req/sec limit)
Total time: 272.411µs
Allowed: 100/10000 requests
Denied: 9900 requests
Average per check: 27 ns
========================================
Performance Summary:
- Cache hit: 25 ns (target <50ns) ✓
- Token bucket: 625 ns ✓
- Burst handling: 41 ns ✓
- HFT scenario: 27 ns ✓
Status
Implementation complete. Key files:
services/api_gateway/src/routing/rate_limiter.rs- Token bucket implementationservices/api_gateway/benches/rate_limiter_bench.rs- Performance benchmarks- Unit tests included in
rate_limiter.rs