A production-grade, self-contained 1 Million Requests-Per-Second (1M RPS) architecture laboratory. This project lets you build, horizontally scale, load test, and observe a high-concurrency microservices architecture entirely inside Docker without modifying your host Linux environment.
βββββββββββββββββββββββββββββ
β k6 Distributed Load β
β Tester (Dockerized) β
βββββββββββββββ¬ββββββββββββββ
β HTTP Ingress (Keep-Alive Pools)
βΌ
βββββββββββββββββββββββββββββ
β HAProxy 3.x (LB) β
β - Dynamic DNS Discovery β
β - Built-in Prometheus β
β - Health-Checked Routing β
βββββββββββββββ¬ββββββββββββββ
β Round-Robin Load Distribution
βββββββββββββββββββββββΌββββββββββββββββββββββ
βΌ βΌ βΌ
βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ
β Fastify API β β Fastify API β β Fastify API β
β (Node.js 22) β β (Node.js 22) β β (Node.js 22) β
β Worker #1 β β Worker #2 β β Worker #N β
βββββββββ¬ββββββββ βββββββββ¬ββββββββ βββββββββ¬ββββββββ
β β β
βββββββββββββββββββββββΌββββββββββββββββββββββ
β
ββββββββββββββββββββββ΄βββββββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ
β Redis 7 Cache β β Prometheus β
β (In-Memory β β (Scrapes API β
β Fast-Path) β β & HAProxy) β
βββββββββββββββββββ ββββββββββ¬βββββββββ
β
βΌ
βββββββββββββββββββ
β Grafana Live β
β Dashboard β
β (Auto-Loaded) β
βββββββββββββββββββ
- Fastify API Cluster (
api/): High-performance Node.js v22 runtime with zero unnecessary logging overhead in benchmark mode, efficient JSON serialization, custom Prometheus instrumentation (prom-client), and connection keep-alive pooling. - HAProxy Layer 7 Load Balancer (
haproxy/): Configured withmaxconn 1,000,000, dynamic Docker DNS discovery (127.0.0.11) supporting auto-scaling up to 20 replicas without restart, and native Prometheus metrics exposition. - Redis Caching Layer (
redis/): In-memory cache demonstrating sub-millisecond fast-path retrieval and database offloading. - Prometheus Monitoring (
prometheus/): Telemetry server scraping HAProxy frontend/backend stats and Fastify per-worker metrics at 2-second intervals. - Grafana Real-Time Dashboard (
grafana/): Pre-provisioned, zero-configuration live telemetry visualizing throughput (RPS), P50/P95/P99 latency percentiles, error rates, per-node traffic distribution, and memory/CPU utilization. - k6 Load Generator (
k6/): Arrival-rate and staged ramping load test suites built to stress-test the cluster and identify the exact saturation ceiling of the underlying hardware.
Start all services with 3 API replicas with a single command:
docker compose up -d --build --scale api=3(or use make up)
| Service | URL | Purpose |
|---|---|---|
| API Traffic Ingress | http://localhost:8080/api/test | High-throughput endpoint load balanced by HAProxy |
| Grafana Telemetry | http://localhost:3000 | Live real-time telemetry dashboard (No login required) |
| HAProxy Live Stats | http://localhost:8404/stats | HAProxy backend server status and session table |
| Prometheus Web UI | http://localhost:9090 | PromQL query engine and target scraper status |
Verify the HAProxy load balancer and API nodes are healthy:
curl http://localhost:8080/healthExpected response:
{ "status": "ok", "pid": 1, "uptime": 12.4, "redis": "connected" }Test round-robin distribution across workers:
for i in {1..5}; do curl -s http://localhost:8080/api/test; echo; doneExecute a 10-second sanity check at 100 RPS:
make smoke(or docker compose --profile loadtest run --rm k6 run /scripts/smoke.js)
Run a steady-state constant arrival-rate test (e.g., 10,000 RPS for 30 seconds):
make load TARGET_RPS=10000 DURATION=30sOpen http://localhost:3000 in your browser to watch the real-time throughput and latency graphs during the run.
To discover your hardware's exact saturation ceiling, execute the staged ramp-up test:
make rampThe test ramps progressively:
1,000 RPS β 5,000 RPS β 10,000 RPS β 25,000 RPS β 50,000 RPS β 100,000 RPS β 250,000 RPS β 500,000 RPS β 1,000,000 RPS
Note on Target vs. Achieved RPS: Requesting 1,000,000 RPS with k6 tells the load tester to attempt that arrival rate. On a standard laptop/workstation, CPU and context switching will saturate earlier (e.g., 30kβ70k RPS). The key learning is measuring the exact point where P95 latency and error rate deviate from linear behavior.
Benchmark how an in-memory caching layer protects backend systems under heavy load:
make cache-testThis runs against /api/product/:id with a 90% cache-hit distribution, demonstrating sub-millisecond response times for cached records.
To prove horizontal scaling efficiency, run the automated benchmark runner:
./benchmark.sh(or make bench)
This script automatically scales the API from 1 to 4 replicas, executes benchmark suites, and produces a Markdown performance table:
=================================================================
1M RPS Lab: Horizontal Scaling Benchmark Runner
=================================================================
API Replicas Target RPS Achieved RPS P95 Latency P99 Latency Errors
--------------------------------------------------------------------------------------
1 15000 req/s 11420 req/s 8.42ms 14.10ms 0.00%
2 15000 req/s 14980 req/s 2.15ms 4.80ms 0.00%
3 15000 req/s 15000 req/s 1.32ms 2.40ms 0.00%
4 15000 req/s 15000 req/s 0.94ms 1.80ms 0.00%
--------------------------------------------------------------------------------------
Results are saved automatically to benchmark_results.md.
Grafana is pre-configured to automatically load the 1M RPS Architecture Telemetry dashboard on startup:
- Ingress Rate (HAProxy): Total incoming HTTP requests per second (
sum(rate(haproxy_frontend_http_requests_total[10s]))). - API Processed Throughput: Total completed requests across all API workers (
sum(rate(http_requests_total[10s]))). - Latency Percentiles: P50 (median), P95, and P99 latency in milliseconds computed from Prometheus histograms.
- Round-Robin Distribution: Per-node breakdown proving uniform traffic distribution across worker containers.
- HTTP Status Codes: Real-time stacked chart of 2xx, 4xx, and 5xx responses.
- Worker Runtime Telemetry: Resident Memory (RSS), Process CPU utilization, and Event Loop Lag.
1m-req/
βββ docker-compose.yml # Complete containerized service definition
βββ Makefile # Convenient CLI commands
βββ benchmark.sh # Automated scaling matrix benchmark runner
βββ README.md # Project documentation and guide
βββ plan.md # Original architecture design document
βββ api/
β βββ Dockerfile # Node.js 22 alpine optimized image
β βββ package.json # Fastify, prom-client, and ioredis
β βββ server.js # High-throughput API implementation
βββ haproxy/
β βββ haproxy.cfg # HAProxy config with DNS discovery & Prometheus exporter
βββ k6/
β βββ scripts/
β βββ smoke.js # 100 RPS sanity test
β βββ constant.js # Configurable constant-rate load test
β βββ ramp.js # Multi-stage 1M RPS ramp-up stress test
β βββ cache_benchmark.js # Redis cache hit vs miss benchmark
βββ prometheus/
β βββ prometheus.yml # Scrape targets (HAProxy, API pods, Prometheus)
βββ grafana/
βββ dashboards/
β βββ 1m_rps_dashboard.json # Pre-built rich Grafana dashboard
βββ provisioning/
βββ dashboards/dashboards.yaml # Auto-provisioning dashboard provider
βββ datasources/prometheus.yaml # Auto-provisioning Prometheus datasource
| Command | Action |
|---|---|
make up |
Build and start all services with 3 API replicas |
make down |
Stop all services |
make smoke |
Run 10-second smoke test |
make load TARGET_RPS=10000 |
Run steady-state benchmark |
make ramp |
Run staged stress test up to 1M RPS target |
make cache-test |
Run Redis cache benchmark |
make scale REPLICAS=5 |
Scale API cluster to 5 containers dynamically |
make bench |
Run automated horizontal scaling benchmark |
make logs |
Stream logs from all containers |
make clean |
Stop containers and remove volumes |
- Target RPS β Achieved RPS: Load generation tools can request arbitrarily high arrival rates, but true capacity is defined by the server's processed requests and sub-10ms P95 latency bounds.
- Dynamic Service Discovery: HAProxy's DNS resolution allows application nodes to scale out dynamically without reloading or dropping active TCP connections.
- Keep-Alive & Connection Pooling: Reusing established TCP connections between load balancers and upstream workers prevents TCP ephemeral port exhaustion.
- Caching Fast-Paths: Adding an in-memory cache layer (Redis) offloads ~90%+ of repetitive queries, preventing downstream bottlenecks.
- Observability Overhead: Keeping metrics collection lightweight (histogram bucket optimization, disabling verbose HTTP request logging) ensures telemetry does not degrade application throughput.

