How low CPU can accompany a full queue
Imagine the load balancer opens 10,000 fresh connections in a short burst. Suppose this server's application loop has paused for a runtime stop-the-world event or is accepting sockets serially while processing expensive TLS or initialization work in the same thread. The kernel may complete TCP handshakes faster than the application drains accepted connections. Its accept queue fills. Existing sockets that have already been accepted can continue serving through other workers, so the service looks partially healthy.
The effective arrival rate temporarily exceeds the accept() drain rate. You do not need sustained high CPU to make that happen. One blocked acceptor, bad event-loop scheduling, a lock, or a process briefly descheduled at the wrong moment can cause a burst of connection-establishment failures. The kernel's response when the completed queue is full depends on OS version and settings. Clients might observe delayed connections, retransmitted handshake packets, connection resets or timeouts. Don't teach one universal symptom.
A different pattern occurs when the queue of incomplete handshakes is exhausted. Perhaps a SYN flood or a sudden arrival spike consumes half-open request state. SYN cookies may help under appropriate conditions, but they are not a generic speed upgrade. They change how the system survives an overflowing SYN queue and can have compatibility or diagnostic implications. If the accept queue is the actual bottleneck, enabling SYN cookies doesn't make the application call accept() faster.
A useful diagnosis sequence
I would capture client connection timing separately from request processing and report SYN-to-SYN-ACK and handshake-to-application-accept latency. On the server, inspect listening sockets with ss -lnt, the effective backlog, socket state distributions and OS counters such as TCP listen overflows and listen drops. Interpret queue columns using the installed ss and kernel version, rather than assuming every platform reports them identically.
Then sample the accept loop. Is one listener responsible for thousands of connections? Is TLS termination inside the process or in the load balancer? Are there reactor stalls, GC pauses, a file-descriptor ceiling, a low per-process descriptor limit, or a mutex shared across accepted sockets? Do scheduled load-balancer health checks use persistent connections that avoid the failing path? If so, green health is misleading.
An especially revealing test is to compare a fresh TCP connection against a reused connection to the same target during the incident. If reuses work while fresh connects fail, kernel queues, connection establishment and admission become much more likely suspects than ordinary handler performance. A packet capture at the load balancer and server can separate dropped SYNs from handshakes that completed but waited for application acceptance.
Capacity math for an acceptor
Suppose burst traffic delivers 2,500 completed handshakes per second while the application can accept only 1,500 sockets per second for four seconds. Ignoring other effects, the queue adds (2,500 - 1,500) × 4 = 4,000 waiting connections. An effective accept backlog below that may overflow during the burst even though normal traffic is only 900 connections per second.
The numbers are illustrative, not a promise that precisely 4,000 entries appear in one Linux queue. Real handshakes are staggered and OS behavior can retransmit or shed traffic. The calculation gives a capacity question to measure: how much of a transient arrival spike can the queue absorb, and for how long can the acceptor lag?
If the loop really is single-threaded and slow, I would make it cheap: accept promptly, hand sockets to bounded workers, and keep expensive initialization off the accept path. Depending on the server architecture, multiple acceptors and SO_REUSEPORT can distribute new connections, but I would verify their behavior and failure modes before making that change. A reasonable backlog increase absorbs a short burst while code is repaired. It cannot fix an acceptor that is consistently slower than arrivals. Eventually every finite queue fills.
Connection reuse and load-balancer connection pooling can reduce arrival bursts, but new clients still need a functioning path. Adding application replicas helps only if new connections actually distribute to the new acceptors and the shared load balancer itself is not saturated. I would cap connection admission where appropriate rather than converting the problem into millions of queued sockets and long client hangs.
The interview conclusion is to identify which boundary received the connection and which component failed to hand it forward. Application CPU utilization is not enough to answer that. The next episode follows outbound connections and shows how a different finite network resource can fail even when every service instance is apparently healthy.