Production-grade JSON-RPC load balancer with intelligent routing, auto-failover, circuit breaking, and observability for Ethereum mainnet RPC providers.
- Overview
- Real-World Analogy
- Business Purpose
- Architecture Choices
- Features
- API Reference
- Local Development
- Docker Compose Setup
- Testing
- Monitoring & Observability
- Requirements Verification
A high-performance load balancer that distributes Ethereum JSON-RPC requests across multiple providers (Infura, Alchemy, etc.) with:
- Intelligent Routing: Round-robin or weighted strategies based on latency
- Auto-Failover: Circuit breakers automatically disable unhealthy providers
- Smart Caching: Redis-backed cache for deterministic RPC calls (finalized blocks, transactions)
- Observability: Prometheus metrics, structured logging, Grafana dashboards
- Operator Dashboard: Real-time monitoring UI for provider analytics and health
Tech Stack: TypeScript, Express, Redis, Prometheus, Grafana, Loki, Tempo
- Runtime: Node.js 18+
- Language: TypeScript 5.9
- Framework: Express 5.x
- Cache: Redis 7.0 (ioredis client)
- Circuit Breaker: Opossum
- HTTP Client: Axios
- Validation: Zod
- Metrics: Prometheus + prom-client
- Dashboards: Grafana
- Logging: Pino (structured JSON logs)
- Log Aggregation: Loki
- Distributed Tracing: Tempo
- Alerting: NodeMailer (email alerts)
- Framework: React 18+ with TypeScript
- Build Tool: Vite
- Styling: TailwindCSS
- Charts: Chart.js / Recharts
- HTTP Client: Axios
- State Management: React Context / Zustand
- Containerization: Docker + Docker Compose
- Testing: Vitest
- Load Testing: Autocannon
- Code Quality: ESLint + Prettier
Problem: At Luganodes, we rely on third-party RPC providers (Infura, Alchemy) to interact with Ethereum. Challenges:
- Cost Optimization: Some providers are expensive; naive round-robin wastes money on slow/unreliable providers
- Reliability: A single provider outage causes service downtime
- Performance: Redundant requests (e.g., fetching the same block 1000 times) are wasteful
Solution: This load balancer:
- Saves Money: Routes traffic to cost-effective, high-performing providers
- Increases Uptime: Auto-failover ensures 99.9%+ availability
- Boosts Speed: Caches ~40-60% of requests, reducing latency by 80%+
- Provides Visibility: Grafana dashboards show which providers are reliable/expensive
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β CLIENT APPLICATIONS β
β (Web3 Apps, dApps, Wallets, Backend Services) β
β β
βββββββββββββββββββββββββββββββββββββββββ¦ββββββββββββββββββββββββββββββββββββββββββ
β
β JSON-RPC Requests
β POST / {"jsonrpc":"2.0", "method":"...", ...}
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ETHEREUM RPC LOAD BALANCER β
β (Port 8080) β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β REQUEST HANDLER (Express.js) β β
β β - Generate Correlation ID (UUID) β β
β β - Validate JSON-RPC payload β β
β β - Structured logging (Pino) β β
β β - CORS & middleware chain β β
β βββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β INTELLIGENT CACHE LAYER β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β Cache Decision Engine β β β
β β β - Is method cacheable? (eth_getBlockByNumber YES, eth_call NO) β β β
β β β - Contains "latest"? -> Skip cache β β β
β β β - Generate cache key: method + params + chain β β β
β β βββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββ β β
β β β β β
β β ββββββββββββββββββββββ΄ββββββββββββββββββββββ β β
β β β β β β
β β Cache HIT Cache MISS β β
β β β β β β
β β β ββββββββββββββββββββββββββββ β β β
β β ββββ>β REDIS CACHE β β β β
β β β (Port 6379) β β β β
β β ββββββββββββββββββββββββββββ€ β β β
β β β Finalized: INF TTL β β β β
β β β Recent: 5min TTL β β β β
β β β Unfinalized: 30s TTL β β β β
β β ββββββββββββββββββββββββββββ β β β
β β β β β β
β β β Return cached β Forward request β β
β β β response βΌ β β
β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β β
β β βΌ β
β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β PROVIDER MANAGER β β β
β β β β β
β β βββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β ROUTING STRATEGY SELECTOR β β β
β β β ββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββ β β β
β β β β Round-Robin β β Weighted (EWMA Latency) β β β β
β β β β Equal distribution β β Faster providers get more load β β β β
β β β ββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββ β β β
β β βββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ β β
β β β β β
β β βββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββ β β
β β β CIRCUIT BREAKER (Opossum) β β β
β β β ββββββββββββββ ββββββββββββββ ββββββββββββββ ββββββββββββββ β β β
β β β β CLOSED β->β OPEN β->β HALF_OPEN β->β CLOSED β β β β
β β β β (Normal) β β (Failed) β β (Testing) β β(Recovered) β β β β
β β β ββββββββββββββ ββββββββββββββ ββββββββββββββ ββββββββββββββ β β β
β β β - Failure threshold: 50% errors in window β β β
β β β - Timeout: 5s per request β β β
β β β - Reset timeout: 30s exponential backoff β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββ¦ββββββββββββββββββββ¦ββββββββββββββββββββ¦βββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββββββββββ ββββββββββββββββββββββ ββββββββββββββββββββ
β INFURA PROVIDER β β ALCHEMY PROVIDER β β QUICKNODE β
β (Ethereum Mainnet) β β (Ethereum Mainnet) β β (Backup) β
βββββββββββββββββββββββββββ’ ββββββββββββββββββββββ’ ββββββββββββββββββββ’
β Status: Healthy β β Status: Healthy β β Status: Healthy β
β Latency: 142ms β β Latency: 98ms β β Latency: 210ms β
β Weight: 32% β β Weight: 46% β β Weight: 22% β
β Requests: 4,521 β β Requests: 6,783 β β Requests: 2,156 β
β Errors: 12 (0.27%) β β Errors: 3 (0.04%) β β Errors: 45 (2%) β
βββββββββββββββββ¦ββββββββββ ββββββββββββ¦ββββββββββ ββββββββββββ¦ββββββββ
β β β
ββββββββββββββββββββββββ©βββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β ETHEREUM MAINNET BLOCKCHAIN β
β (Decentralized Network) β
βββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β OBSERVABILITY & MONITORING STACK β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β β
β βββββββββββββββββββββββ βββββββββββββββββββββββ βββββββββββββββββββββββββββ β
β β PROMETHEUS β β GRAFANA β β LOKI β β
β β (Port 9090) β β (Port 3001) β β (Port 3100) β β
β βββββββββββββββββββββββ€ βββββββββββββββββββββββ€ βββββββββββββββββββββββββββ€ β
β β - Metrics scraping β->β - Live dashboards β β - Log aggregation β β
β β - Time-series DB β β - Visualization β β - Full-text search β β
β β - PromQL queries β β - Alert manager β β - Log retention β β
β β - 15s scrape rate β β - Multi-tenancy β β - JSON parsing β β
β βββββββββββββββββββββββ βββββββββββββββββββββββ βββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β TEMPO (Distributed Tracing - Port 3200) β β
β β - End-to-end request tracing - Latency waterfall visualization β β
β β - Span correlation - Performance bottleneck detection β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β EMAIL ALERTING (NodeMailer) β
β - All providers down (CRITICAL) - Cache hit rate < 30% (WARNING) β
β - Only 1 provider remaining (WARN) - Error rate > 5% (WARNING) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ADMIN & OPERATOR INTERFACE β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β ADMIN API (Port 3000) β β
β β Authentication: X-Admin-Token header β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β Endpoints: β β
β β - POST /admin/providers -> Add new RPC provider β β
β β - DELETE /admin/providers/:id -> Remove provider β β
β β - PATCH /admin/providers/:id -> Update weight/status β β
β β - POST /admin/providers/:id/enable -> Force enable β β
β β - POST /admin/providers/:id/disable -> Force disable β β
β β - GET /admin/cache/stats -> Cache metrics β β
β β - DELETE /admin/cache -> Clear cache (all/provider/pattern) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β OPERATOR DASHBOARD (React + TypeScript + Vite) β β
β β Real-time monitoring interface for DevOps/SRE teams β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β Features: β β
β β - Live provider health status cards β β
β β - Request distribution pie/bar charts (Chart.js) β β
β β - Cache hit/miss rate trends β β
β β - Circuit breaker state visualization β β
β β - Provider latency comparison graphs β β
β β - Error rate alerts & notifications β β
β β - One-click provider enable/disable β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββ
β CLIENT β
β (Web3 Application) β
βββββββββββ―ββββββββββββββ
β
βββββββββββββββββββββΌββββββββββββββββββββ
β 1. POST / (JSON-RPC Request) β
β { β
β "jsonrpc": "2.0", β
β "method": "eth_getBlockByNumber", β
β "params": ["0x12A4B7C", true], β
β "id": 1 β
β } β
βββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 2. REQUEST HANDLER β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β - Generate Correlation ID: "req_abc123xyz" β β
β β - Validate JSON-RPC format β β
β β - Log incoming request β β
β β - Start latency timer β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3. CACHE DECISION ENGINE β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Is method cacheable? β β
β β -> eth_getBlockByNumber YES β β
β β -> eth_blockNumber NO β β
β β β β
β β Contains "latest" parameter? NO β β
β β β β
β β Generate Cache Key: β β
β β "eth:1:getBlockByNumber:0x12A4B7C:true" β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββ΄ββββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββββββββββ βββββββββββββββββββββββββββ
β Cache HIT β β Cache MISS β
β (Redis lookup: found) β β (No cached data) β
βββββββββββββ¬ββββββββββββββ βββββββββββ¬ββββββββββββββββ
β β
β βΌ
β ββββββββββββββββββββββββββββββββββββββββββ
β β 4. PROVIDER SELECTION β
β β ββββββββββββββββββββββββββββββββββββ β
β β β Routing Strategy: WEIGHTED β β
β β β β β
β β β Available Providers: β β
β β β - Infura (142ms) -> 32% load β β
β β β - Alchemy (98ms) -> 46% load β β
β β β - QuickNode (210ms) -> 22% load β β
β β β β β
β β β Random: 0.521 β β
β β β Selected: Alchemy (fastest!) β β
β β ββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββββββββββββ
β β
β βΌ
β ββββββββββββββββββββββββββββββββββββββββββ
β β 5. CIRCUIT BREAKER CHECK β
β β ββββββββββββββββββββββββββββββββββββ β
β β β Provider: Alchemy β β
β β β State: CLOSED (Healthy) β β
β β β Recent Errors: 3/1000 (0.3%) β β
β β β Last Success: 2s ago β β
β β β Result: Allow request to pass β β
β β ββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββββββββββββ
β β
β βΌ
β ββββββββββββββββββββββββββββββββββββββββββ
β β 6. FORWARD TO PROVIDER β
β β POST https://eth-mainnet.g.alchemy... β
β β Timeout: 5000ms β
β ββββββββββββββββββββββββββββββββββββββββββ
β β
β β Latency: 98ms
β βΌ
β ββββββββββββββββββββββββββββββββββββββββββ
β β 7. RESPONSE RECEIVED β
β β { β
β β "jsonrpc": "2.0", β
β β "id": 1, β
β β "result": { ... block data ... } β
β β } β
β ββββββββββββββββββββββββββββββββββββββββββ
β β
β βΌ
β ββββββββββββββββββββββββββββββββββββββββββ
β β 8. CACHE RESPONSE β
β β ββββββββββββββββββββββββββββββββββββ β
β β β Block: 19,400,000 β β
β β β Current: 19,500,000 β β
β β β Diff: 100,000 blocks > 64 β β
β β β Status: FINALIZED β β
β β β TTL: Infinite (1 year) β β
β β β Store in Redis: SUCCESS β β
β β ββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββββββββββββ
β β
βββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 9. RETURN RESPONSE WITH METADATA β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Headers: β β
β β - X-Cache-Hit: false β β
β β - X-Provider-Id: alchemy β β
β β - X-Correlation-Id: req_abc123xyz β β
β β - X-Response-Time: 98ms β β
β β β β
β β Body: { "jsonrpc": "2.0", "result": {...} } β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββ
β CLIENT β
β (Response received) β
βββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 10. METRICS & LOGGING β
β - Prometheus: rpc_requests_total{provider=alchemy}++ β
β - Pino: {"correlationId":"req_abc123xyz", β
β "method":"eth_getBlockByNumber", β
β "cacheHit":false, "latency":98} β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββ
β β
β CLOSED (Healthy) β
β β
β - All requests pass through β
β - Monitor failure rate β
β - Track error count in window β
β - Normal operation mode β
β β
βββββββββββββββ¦βββββββββββββββββββββββββ
β
β Threshold Exceeded
β (e.g., 50% errors in 10s window)
β or 3 consecutive failures
β
βΌ
ββββββββββββββββββββββββββββββββββββββββ
β β
β OPEN (Unhealthy) β
β β
β - Block ALL requests β
β - Fast-fail immediately (no delay) β
β - Return error instantly β
β - Wait for cooldown period β
β - Timer: 30s (exponential backoff) β
β β
βββββββββββββββ¦βββββββββββββββββββββββββ
β
β After resetTimeout
β (30s -> 60s -> 120s -> ...)
β
βΌ
ββββββββββββββββββββββββββββββββββββββββ
β β
β HALF_OPEN (Testing) β
β β
β - Allow limited test requests β
β - Monitor closely for success β
β - One request at a time β
β - Decide: Recover or re-open β
β β
ββββββββ¦βββββββββββββββββββββββ¦βββββββββ
β β
SUCCESS β β FAILURE
(Provider OK) β β (Still broken)
βΌ βΌ
ββββββββββββββββββββββ βββββββββββββββββββββββ
β CLOSED β β OPEN β
β (Auto-recovered) β β (Retry later) β
β Resume normal ops β β Increase backoff β
ββββββββββββββββββββββ βββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CIRCUIT BREAKER CONFIGURATION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Timeout: 5000ms (per request) β
β Error Threshold: 50% (errors in rolling window) β
β Reset Timeout: 30000ms (initial), exponential backoff β
β Rolling Window: 10 seconds β
β Volume Threshold: 10 requests (minimum before triggering) β
β Failure Detector: HTTP 5xx, Timeout, Network Error β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TTL STRATEGY REFERENCE TABLE β
β£ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ«
β β
β ββββββββββββββββββββββββββ¦ββββββββββββββββ¦βββββββββββββββββββββββββββββββ β
β β BLOCK AGE β TTL β REASON β β
β β βββββββββββββββββββββββββ¬ββββββββββββββββ¬βββββββββββββββββββββββββββββββ£ β
β β >64 blocks from head β INF (1 year) β Finalized, immutable β β
β β 13-64 blocks from head β 5 minutes β Likely finalized, safe β β
β β 1-12 blocks from head β 30 seconds β Unfinalized, may reorg β β
β β "latest" parameter β NEVER CACHE β Always refers to chain head β β
β ββββββββββββββββββββββββββ©ββββββββββββββββ©βββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββ¦ββββββββββββββββββββββββββββββββ β
β β METHOD β CACHEABLE? β β
β β βββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ£ β
β β eth_getBlockByNumber (finalized) β YES (with TTL logic) β β
β β eth_getBlockByHash β YES (infinite TTL) β β
β β eth_getTransactionByHash β YES (infinite TTL) β β
β β eth_getTransactionReceipt β YES (infinite TTL) β β
β β eth_blockNumber β NO (always current) β β
β β eth_gasPrice β NO (highly volatile) β β
β β eth_call β NO (state-dependent) β β
β β eth_getBalance (with "latest") β NO (changes every block) β β
β ββββββββββββββββββββββββββββββββββββββββββ©ββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CACHE PERFORMANCE METRICS (Expected) β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β Cache Hit Rate: 40-60% (typical Web3 workload) β
β Latency Reduction: ~80% (500ms -> 100ms for cached requests) β
β Cost Savings: 30-50% reduction in provider API calls β
β Memory Usage (Redis): ~100MB for 50,000 cached blocks β
β Eviction Policy: allkeys-lru (Least Recently Used) β
β Max Memory: 256MB (configurable) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
This project follows a clean layered architecture for maintainability, testability, and separation of concerns:
Benefits:
- Separation of Concerns: Each layer has a single, well-defined responsibility
- Testability: Easy to mock dependencies and write unit tests for each layer
- Maintainability: Changes in one layer don't cascade to others (loose coupling)
- Scalability: Individual layers can be optimized or replaced independently
- Reusability: Service layer logic can be reused across different controllers
- Clarity: New developers can quickly understand the codebase structure
β
HTTP server accepting JSON-RPC requests on port 8080
β
Multiple backend RPC providers (configurable via environment variables)
β
Routing Strategies:
round-robin: Distribute evenly across healthy providersweighted: Route based on EWMA latency (faster providers get more traffic)
β Admin API for provider management (add/remove/enable/disable/update weights)
β
Periodic health checks with staggered jitter (30s cycle)
β
Success/failure rate tracking per provider
β
Circuit Breaker: Disables providers after N failures, auto-recovery with exponential backoff
β
Configurable thresholds (timeout, error threshold, reset timeout)
β
Cacheable Methods: eth_getBlockByNumber, eth_getBlockByHash, eth_getTransactionByHash, eth_getTransactionReceipt
β
Non-Cacheable Methods: eth_blockNumber, eth_gasPrice, eth_call, any call with "latest" parameter
β
TTL Strategy:
- Infinite TTL for finalized blocks (>64 blocks old)
- 5-minute TTL for recent blocks (within 64 blocks)
- 30-second TTL for unfinalized blocks
β Redis-backed with automatic key generation
β
Structured Logging (Pino): Request tracing with correlation IDs
β
Prometheus Metrics:
- Per-provider: request count, success/failure rate, latency, circuit breaker state
- System-wide: cache hit rate, active providers, error rate by type
β Alerting: Email alerts for critical conditions (all providers down, cache issues)
β Grafana Dashboards: Pre-configured dashboards for provider health, request distribution, cache stats
β
Request Retry: Automatically retries with a different provider on failure
β
Docker Compose: Full stack (Redis, Prometheus, Grafana, Loki, Tempo)
β
Load Testing: Autocannon-based load test scripts with realistic traffic patterns
β
Operator Dashboard: React-based UI for real-time monitoring
For detailed verification of all problem statement requirements, see:
π REQUIREMENTS-VERIFICATION.md
Summary: β ALL REQUIREMENTS MET (100%)
- β Core Functionality (9/9)
- β Health Monitoring (7/7)
- β Intelligent Caching (10/10)
- β Observability (9/9)
- β Alerting (6/6)
- β Bonus Features (3/3)
For complete API documentation, see API-REFERENCE.md.
Public Endpoints (Port 8080):
POST /- JSON-RPC proxy for Ethereum requestsGET /providers- View provider statistics (read-only)GET /health- Health checkGET /health/detailed- Detailed health informationGET /metrics- Prometheus metrics
Admin Endpoints (Port 8081) - Basic Auth Required (admin:changeme):
GET /admin/providers- View all providersGET /admin/providers/:id- View specific providerPOST /admin/providers- Add new providerPATCH /admin/providers/:id- Update provider (weight, enable/disable)DELETE /admin/providers/:id- Remove providerPOST /admin/providers/:id/enable- Force enable providerPOST /admin/providers/:id/disable- Force disable providerGET /admin/cache/stats- View cache statisticsDELETE /admin/cache- Clear cache (all/provider/pattern)
Authentication: Admin endpoints use HTTP Basic Authentication (not JWT).
# Example: View all providers
curl -u admin:changeme http://localhost:8081/admin/providers
# Example: JSON-RPC request
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
}'β See API-REFERENCE.md for complete documentation with request/response examples.
- Node.js 18+ (Download)
- Docker & Docker Compose (Download)
- RPC Provider API Keys: Infura, Alchemy, or any Ethereum JSON-RPC provider (optional - public endpoints available)
-
Clone the repository
git clone https://github.com/HarshitPG/Ethereum-RPC-Load-Balancer.git cd Ethereum-RPC-Load-Balancer -
Install backend dependencies
npm install
-
Configure environment variables
cp .env.example .env
Edit
.envwith your configuration. The project includes working public RPC endpoints, or you can add your own API keys:# Public endpoints (works out of the box) INFURA_URL=https://eth.llamarpc.com ALCHEMY_URL=https://ethereum.publicnode.com # Or use your own keys # INFURA_URL=https://mainnet.infura.io/v3/YOUR_API_KEY # ALCHEMY_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY
-
Start all services (Redis, Prometheus, Grafana, Loki, Tempo)
docker-compose up -d
This spins up:
- Redis (Port 6379)
- Prometheus (Port 9090)
- Grafana (Port 3001 - username:
admin, password:admin) - Loki (Port 3100)
- Tempo (Port 3200)
-
Start the backend application
# Development mode (hot reload) npm run dev # Production mode npm run build npm start
Backend runs on:
- Public API: http://localhost:8080
- Admin API: http://localhost:8081
-
Test the backend API
# Health check curl http://localhost:8080/health # JSON-RPC request curl -X POST http://localhost:8080 \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1 }' # View providers curl http://localhost:8080/providers # Admin API (basic auth required) curl http://localhost:8081/admin/providers \ -u admin:changeme
-
Navigate to dashboard directory
cd dashboard -
Install frontend dependencies
npm install
-
Configure API endpoint (if needed)
Edit
dashboard/.envordashboard/src/config.tsto point to your backend:VITE_API_URL=http://localhost:8080 VITE_ADMIN_API_URL=http://localhost:8081
-
Start the frontend development server
npm run dev
Dashboard runs on: http://localhost:5173
-
Build for production
npm run build
Output will be in
dashboard/dist/
- Backend API: http://localhost:8080
- Admin API: http://localhost:8081 (Basic Auth:
admin:changeme) - Frontend Dashboard: http://localhost:5173
- Grafana: http://localhost:3001 (admin:admin)
- Prometheus: http://localhost:9090
- Redis: localhost:6379
To stop all services:
docker-compose downTo view logs:
# All services
docker-compose logs -f
# Specific service
docker-compose logs -f redis
docker-compose logs -f prometheus
docker-compose logs -f grafanaTo reset data (clear cache, metrics):
docker-compose down -v
rm -rf data/Pre-configured dashboards are automatically provisioned:
-
RPC Load Balancer Overview
- Total requests, cache hit rate, error rate
- Provider health status
- Request distribution
-
Provider Analytics
- Per-provider request count, latency, errors
- Circuit breaker state transitions
- Success/failure rates
-
Cache Performance
- Hit/miss rates by method
- TTL distribution
- Memory usage
# Run all tests
npm test
# Run specific test file
npm test test/1-core-functionality.test.ts
# Run with coverage
npm test -- --coverage-
Core Functionality (
1-core-functionality.test.ts)- Provider registration, routing strategies, request handling
-
Health Monitoring (
2-health-monitoring.test.ts)- Health checks, circuit breakers, auto-failover
-
Caching Layer (
3-caching-layer.test.ts)- Cache hit/miss, TTL strategy, invalidation
-
Observability (
4-observability.test.ts)- Logging, metrics, correlation IDs
-
Alerting (
5-alerting.test.ts)- Email alerts, alert conditions
-
Circuit Breaker Integration (
7-circuit-breaker-integration.test.ts)- Failure detection, state transitions
-
Performance & Load Testing (
8-performance-load-testing.test.ts)- Throughput, latency under load
# Minimal load test (30 seconds)
bash scripts/load-test-minimal.sh
# Custom load test with autocannon
npx autocannon -c 50 -d 60 -m POST \
-H "Content-Type: application/json" \
-b '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
http://localhost:8080Expected Results:
- Throughput: 500-1000 req/s (cached), 100-200 req/s (uncached)
- Latency (p95): <100ms (cached), <500ms (uncached)
- Error Rate: <1%