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.
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).
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.
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⌉.
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!