Why Servers Crash Under High Traffic: The TCP TIME_WAIT Trap
When a backend service or microservice opens thousands of outbound HTTP connections per second, CPU utilization might sit at 10%, yet the server suddenly crashes with:
connect() failed (99: Cannot assign requested address)
The culprit is not a memory leak or a slow database query. It is the operating system's TCP TIME_WAIT state.
⚡ The TCP 4-Way Teardown Flow
When an endpoint initiates an active close:
Client (Active Close) Server (Passive Close)
│ │
│─────── FIN (seq=100) ────────────────>│ (CLOSE_WAIT)
│<────── ACK (ack=101) ─────────────────│
│ │
│<────── FIN (seq=300) ─────────────────│ (LAST_ACK)
│─────── ACK (ack=301) ────────────────>│ (CLOSED)
│
[TIME_WAIT] ⏳ (Holds socket for 2 × MSL: ~60–120s)
│
(CLOSED)
The host that sends the final ACK does NOT immediately close the socket. It enters TIME_WAIT and holds the (Source IP, Source Port, Dest IP, Dest Port) tuple in the kernel socket table for 2 × MSL (Maximum Segment Lifetime).
🧠 Why Does TIME_WAIT Exist?
It serves two fundamental protocol guarantees:
-
Clean Final ACK Delivery: If the client's final ACK drops on the wire, the server retransmits its
FIN. If the client closed instantly, it would reply with anRST(connection reset error) instead of completing an orderly shutdown. -
Ghost Packet Draining: Old TCP segments delayed in slow router buffers could arrive minutes later. If the OS reassigned that identical
IP:Portpair immediately, ghost packets from the previous session would corrupt the new data stream!
🛠️ The Ephemeral Port Exhaustion Trap
Linux provides a limited pool of ephemeral outbound ports (typically ~28,000 ports between 32768 and 60999).
If your microservice opens 500 connections/sec without connection reuse:
Sockets in TIME\_WAIT = 500 × 60 seconds = 30,000 sockets!
All local outbound ports are exhausted!
The Production Fix:
-
Enable HTTP Keep-Alive: Reuse persistent connections via connection pooling (HTTP clients like Axios, Go's
http.Transport, or Pythonrequests.Session). - Tune Linux Socket Reuse:
sysctl -w net.ipv4.tcp_tw_reuse=1
🚀 Trace the Full TCP State Machine Interactively
You can test state transitions, inject lost packets, and simulate the 2MSL timer interactively on Binary Dose.
Top comments (0)