When your compute tier can't keep up with incoming load
Problem
Two API servers, each provisioned for 2,000 QPS, receive 2,500 QPS apiece from a load balancer handling 5,000 QPS of total traffic. Neither server has any headroom — both run at 125% at the same time. Each completes 2,000 requests per second and sheds the other 500, while its request queue grows without bound and latency spikes across the entire user base.
Symptoms in Production
- CPU pegged at 95–100% across all instances simultaneously
- Latency climbs from ~50 ms without limit as the M/M/1 queue grows (the simulator shows ∞ once a server reaches 100%)
- Throughput plateaus even as traffic continues to climb
- Load balancer health checks start failing, causing instance flapping
- HTTP 5xx error rates rise as servers begin rejecting or timing out requests
Root Cause
Each server handles 2,500 QPS against a ceiling of 2,000 — 125% utilization. It can complete only 2,000 per second, so 500 per second (20%) are shed; together with the configured 2% errors, 21.6% of requests fail. Under M/M/1 queueing theory, utilization at or above 100% means the queue never drains, so latency is unbounded. With both servers saturated and no autoscaling, there is no path to recovery without intervention.
Mitigations
- Horizontal scale: add server instances until aggregate capacity exceeds peak QPS by ≥ 30% — set Instances to 2 and each server drops to 62.5%
- Autoscaling: configure CPU-based HPA in Kubernetes to scale out before utilization hits 80%
- Request shedding: return HTTP 429 at ~75% utilization to signal backpressure upstream
- Async offloading: push expensive synchronous work into a background queue so the hot path stays lean
- Profiling: identify hot code paths — often a single expensive operation (N+1 query, JSON serialization) drives most CPU usage