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>
358 lines
9.4 KiB
Markdown
358 lines
9.4 KiB
Markdown
# 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
|
|
|
|
1. **In-Memory Cache Hit**: 25-41ns
|
|
- HashMap lookup: ~5ns
|
|
- Token bucket check: ~10ns
|
|
- Refill calculation: ~10ns
|
|
- Lock overhead: ~10ns
|
|
|
|
2. **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:
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```lua
|
|
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
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```sql
|
|
-- 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# Docker
|
|
docker run -d -p 6379:6379 --name foxhunt-redis redis:7-alpine
|
|
|
|
# Or local Redis
|
|
redis-server --port 6379
|
|
```
|
|
|
|
### Production
|
|
|
|
```bash
|
|
# 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
|
|
|
|
1. **Cache Hit Rate**
|
|
- Target: >95% hit rate
|
|
- Monitor: `cache_hits / (cache_hits + cache_misses)`
|
|
|
|
2. **Latency Distribution**
|
|
- p50: <30ns (cache hits)
|
|
- p95: <100ns (cache hits)
|
|
- p99: <1μs (Redis hits)
|
|
|
|
3. **Rate Limit Violations**
|
|
- Track denied requests per endpoint
|
|
- Alert on unusual patterns
|
|
|
|
### Cache Statistics
|
|
|
|
```rust
|
|
// 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
|
|
|
|
1. **Dynamic Configuration**
|
|
- Load rate limits from PostgreSQL config table
|
|
- Hot-reload on configuration changes via NOTIFY/LISTEN
|
|
|
|
2. **Advanced Metrics**
|
|
- Prometheus metrics integration
|
|
- Per-endpoint rate limit statistics
|
|
- User-level quota tracking
|
|
|
|
3. **Sliding Window Algorithm**
|
|
- Optional sliding window rate limiting
|
|
- More accurate but slightly higher overhead
|
|
|
|
4. **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 implementation
|
|
- `services/api_gateway/benches/rate_limiter_bench.rs` - Performance benchmarks
|
|
- Unit tests included in `rate_limiter.rs`
|
|
|