⚡ Observability Dashboard · 2026 Node.js Fleet Architecture
📽️ Presentation Deck 📖 Documentation & Overview 🐙 GitHub Repo

Node.js Fleet Overload Protection

Simulating a 6-container Node.js fleet behind an AWS Application Load Balancer (ALB). Demonstrates the effect of event loop overload protection on the 4 Golden Signals, why ALB forwards 503s directly to the client (ALB never retries requests), and how individual containers protect memory and latency against compounded blocking tasks.
📊 SRE Golden Signals (Fleet Telemetry)
1. Latency (Tail) Healthy
p95 2.4 ms
p99 5.8 ms
Rolling 300-request window
2. Traffic (Demand) Target Met
60 req/min
~1 req/sec fleet-wide (0.17 req/s / pod)
3. Errors (503 Rate) 0.0% Shed
0.0% unhandled
0 shed (503) · 0 client retries handled
4. Saturation (Capacity) Normal
22% utilized
📐 Little's Law Live Capacity Planner (L = λ · W) 1 Heavy Crypto (16ms) + 8 Lite (0.5ms) per User Journey
Main-thread service time: 20.0 ms/journey · Target loop utilization: ρ ≤ 0.70
Users Starting Journeys 7 /min ~0.1 new users/sec
Safe Capacity / Pod 2,100 /min 35.0 users/sec (315 req/s)
Containers Needed 1 Container Fleet has 6 pods (5 excess headroom)
Autoscaler & Overload State Healthy Headroom Zero shedding needed · Event loop < 3ms
Rate Multiplier (±50%/min): Surge or drop user journeys incrementally to simulate autoscaler lag:
📖 Guided Architectural Walkthrough Click a scenario to trigger external traffic patterns
Baseline (60 req/min, 2% Crypto): Fleet runs completely healthy at 1 req/sec (~1 request every 6s per pod). Single 16ms tasks have minimal impact (~18ms lag < 42ms limit) and drain in ~200ms. 0% shed, 0% error rate.
Incoming Traffic: 60 req/min
User Journeys: 7 /min
New User Sessions: 2%
Task Impact / Block: 16ms
Tasks per Burst: 4 Tasks
Max Delay Limit: 42ms
🟢 Fleet healthy at 60 req/min (~1 req/s). Protection enabled on all containers.
OVERLOAD-PROTECTION v3.0
AWS Application Load Balancer (ALB) ALB Forwards 503 Directly to Client · All Retries Client-Initiated
Key: ● Fast Transaction · ● Cryptographic Task · ● 503 Returned Straight to Client (Retry-After) · ● Client Retry (200 OK)
AWS ALB Target Group Dispatcher
ALB forwards target 503 directly to client (ALB never retries requests) · Client backs off and sends new retry
Dispatching: Pod #1
Event Loop Delay Over Time (Pods 1–6)
Pod 1
Pod 2
Pod 3
Pod 4
Pod 5
Pod 6
Limit (42ms)

🔐 Configurable Cryptographic Task Blocking

Unlike asynchronous I/O (database queries, network fetch), cryptographic operations (password hashing with bcrypt/argon2, ECDH key exchange, token signing) execute synchronously on the single JavaScript main thread.

Using the controls above, you can configure both the task duration (e.g. 16ms) and the number of tasks. A single task (16ms) produces minimal impact on a healthy container ($\approx 18\text{ms} < 42\text{ms}$ limit). Only when multiple tasks compound ($3 \times 16\text{ms} = 48\text{ms} > 42\text{ms}$) does event loop delay exceed the threshold.

🛡️ Overload Protection ON vs OFF (Per-Container Impact)

Try toggling Overload Protection OFF on an individual container and injecting tasks to see the catastrophic difference on its 4 Golden Signals:

With Protection ON: When loop delay exceeds the limit, overload-protection immediately sheds excess incoming requests with 503 Service Unavailable in <0.05ms. Latency stays bounded, memory stays flat at ~50MB, and the container rapidly recovers.

With Protection OFF (Queue Collapse & 30s ALB Timeout): The event loop does not stop—it spins actively executing callbacks on the main thread. As long as a delayed response emits in < 30s, it returns to the ALB and the client receives 200 OK (with huge tail latency). However, if the response is not emitted within 30s, the ALB target response timeout fires and ALB returns a 503 error directly to the client. Crucially, Node.js does not cancel the task: the task carries on doing the work to completion (wasting CPU cycles and ballooning RSS memory past 300MB+ for a client that already received a 503).

🔄 Client-Initiated Retries (ALB Reality)

A vital reality of cloud architectures: AWS Application Load Balancers do NOT retry 503 responses—all shed requests go straight back to the client.

By including Retry-After: 1, overload-protection instructs the client application (browser, mobile app, API caller) to back off before retrying. When the client initiates a subsequent request, the ALB distributes it across healthy sibling containers in the target group.

📐 Little's Law & Fleet Sizing (L = λ · W)

According to Little's Law, concurrency is L = λ · W. For a single-threaded Node.js event loop, queueing theory dictates that response latency scales with CPU utilization ρ = λ · S as: W = S / (1 - ρ)

For a user journey requiring 1 heavy crypto task (16ms) and 8 lite requests (0.5ms each), the main thread is occupied for S = 16ms + 4ms = 20ms (0.020s) per journey. The single crypto task accounts for 80% of total CPU time!

To keep event loop delay safely below the 42ms limit, we target ρ ≤ 0.70. Hence, 1 container safely sustains: λ = 0.70 / 0.020s = 35 journeys/s = 2,100 user journeys/min (18,900 req/min). For U discrete users starting journeys per minute, required containers = ⌈U / 2,100⌉.

⚡ The ±50%/min Surge & Autoscaler Lag Window

When user journeys suddenly surge by +50%, thread utilization leaps from ρ = 0.70 to ρ = 1.05 > 1.0. In cloud environments (ECS Fargate, EKS, Cloud Run), horizontal pod autoscalers require 60 to 180 seconds to register CloudWatch alarms, provision nodes, pull images, and pass ALB health checks.

During this 1-minute autoscaler lag window, an unprotected container suffers queue explosion: L_q → ∞, RSS memory balloons past 300MB+, and the process crashes from OOM. With overload-protection, the container sheds the excess +50% requests in <0.05ms with 503 + Retry-After, keeping memory flat and loop lag low so the existing fleet survives intact until autoscaling completes!