Loss is TCP's operating condition, not its failure mode. The sender must detect that a segment vanished, decide which one, and resend it — and the difference between the good recovery path and the bad one is measured in RTTs. This trace follows a single lost segment.
The sender transmitted segments 100–109; segment 103 dies in the network (a full buffer somewhere, a flaky radio, a checksum failure). The receiver still gets 104–109, and since those are out of order, its kernel ACKs the last in-order byte — 103 — producing duplicate ACKs. The receiver keeps ACKing 103 on every subsequent segment. Meanwhile the sender's retransmission timer — set when 103 went out — is ticking.
If the RTO expires before any ACK arrives, the sender retransmits the oldest unacknowledged segment and — critically — doubles the RTO for the next try: 1s → 2s → 4s → 8s (capped at 120s by default). This is the slow path: timeout recovery stalls the connection for the full backoff interval. A single lost packet on a 40 ms RTT path can cost a 1-second stall. This is what "the connection just froze" means at the kernel level.
The fast path: the third duplicate ACK (three dup-ACKs total) triggers fast retransmit — the sender retransmits segment 103 immediately, without waiting for the timer. Recovery starts at ~1 RTT instead of 1+ seconds. This works because dup-ACKs prove the network is still delivering — the loss was isolated, not catastrophic. This is why TCP can't fast-retransmit on the first dup-ACK: one ACK can be reordered by the network itself.
nstat -az | grep -E "TcpRetransSegs|TcpLostRetransmit|TcpOutSegs"With SACK negotiated at handshake time, the receiver's ACKs carry block information: "I have 100–102, 104–109, missing 103." The sender now knows exactly what to resend — one segment, not the whole window. Without SACK (or with a burst loss), the sender falls back to retransmitting everything from the lost byte forward, wasting bandwidth and reordering the stream. Modern kernels enable SACK by default; net.ipv4.tcp_sack should never be off.
Recovery closes by halving the window — the congestion response we traced in tcp-congestion-trace: ssthresh = cwnd / 2, sender enters fast recovery, and the ACK for the retransmitted segment returns the connection to congestion avoidance. The full ledger for this trace: 1 lost segment → ~1 RTT of stall, one halved window, one retransmitted packet. The timeout path: 1 RTT → seconds of stall, backoff-doubled, same window penalty — the same packet, ten to a hundred times the cost.
What the machine actually does when a packet dies is a race between two timers — the RTO and the dup-ACK counter — with the sender's entire latency reputation on the line. The kernel chooses whichever evidence arrives first, and the gap between the good outcome (~1 RTT) and the bad one (seconds) is the single largest latency cliff in all of TCP.