Packet Loss Buys You a Timeout, Not a Retransmit
A link that drops one packet in a thousand looks almost perfect on an average-latency graph. What it actually does is give one request in a few hundred an extra couple of hundred milliseconds, which is invisible at p50 and dominates p99.
Frame the diagnosis
Performance work starts from a symptom and a signal — never from a resource dashboard.
One lost packet costs a timeout, not a packet
The intuition that loss costs "the time to send one more packet" is wrong by two orders of magnitude. When a packet is lost, the sender does not know immediately. Recovery happens either through duplicate acknowledgements triggering a fast retransmit — relatively quick, roughly an RTT — or, when there is not enough subsequent traffic to generate those duplicates, through a retransmission timeout, which is conservative by design and typically hundreds of milliseconds.
Small requests are the worst case. A large transfer has plenty of packets in flight to trigger fast retransmit; a small request-response may have so few that a single loss can only be recovered by the timeout. So the requests that suffer most from packet loss are frequently the smallest and most latency-sensitive ones, which is precisely the opposite of what most people expect.
The result is a distribution with a small, distant cluster: nearly all requests normal, a few hundreds of milliseconds out. Averages absorb this completely, which is why loss-induced tail latency is one of the clearest illustrations of the argument in The Average Was Fine and Users Were Not.
Why distributed systems see jitter a single machine never does
A function call within one process has essentially no variance. The moment a call crosses a network, its latency becomes a distribution shaped by queueing at every switch and router on the path, by competing traffic from systems you do not control, by scheduling on the receiving host, and by loss and recovery. That variance — jitter — is not noise around a true value; it is the actual behaviour.
Jitter compounds badly with fan-out. If each of seven parallel calls is usually fast but occasionally jittery, the request takes the maximum, so it inherits the worst jitter of the seven on every request. This is the amplification from Fan-Out: Waiting for the Slowest of Seven, now driven by network variance rather than by dependency slowness, and it is why distributed systems have tails that look qualitatively different from monolithic ones.
The diagnostic value of jitter is that it discriminates. Application-level slowness usually correlates with something — an endpoint, a tenant, a payload size, a time of day. Loss-induced tails correlate with a *path*: a particular availability zone pair, a particular provider link, a particular client network. If the slow requests have no application-level pattern but cluster by network path, that is the signal.
| Signal | Value | What it tells you | Verdict |
|---|---|---|---|
| p50 latency | unchanged | The median path is healthy — rules out broad application slowness | normal |
| p99 latency | 110ms → 480ms | A small population of requests is very slow | smoking gun |
| Bandwidth utilisation | 22% | Low. Loss is not caused by saturating your own link | normal |
| TCP retransmission rate | 0.02% → 0.31% | The direct evidence: packets are being lost and recovered | smoking gun |
| RTT variance (jitter) on path | ±3ms → ±140ms | Highly variable path, consistent with queueing or loss upstream | smoking gun |
| Slow requests by endpoint | evenly spread | No application pattern — points away from code and toward the path | suspect |
| Slow requests by AZ pair | 94% on one pair | A path-shaped correlation. This is the actual scope | smoking gun |
What you can and cannot fix
Loss on a link you own or pay for is an operational problem with an operational fix: identify the segment, escalate to whoever runs it, and route around it while they work. Loss on the public internet between your edge and a user's device is not yours to fix at all, and pretending otherwise wastes time. The first useful question is therefore always "which segment", not "how do we fix it".
What you can always do is reduce your exposure. Fewer round trips on the critical path means fewer chances to lose a packet at a moment that costs a timeout — connection reuse, avoiding handshakes on hot paths, and collapsing chatty exchanges all shrink the target. Protocol choice matters too: transports with better loss recovery semantics can avoid head-of-line blocking that makes one lost packet stall unrelated concurrent work.
And you can stop letting a small tail become a large one. A per-call timeout with a fallback bounds the damage from a lost packet to the timeout value rather than to whatever the retransmission timer decides. Retrying is legitimate for idempotent calls, and must be bounded and jittered — a retry on a congested path adds to the congestion that caused the loss, which is the feedback loop in Retry Storms: The Load You Generated Yourself.
| Source | Yours to fix? | What helps |
|---|---|---|
| Your own link saturated | Yes | Reduce traffic, increase capacity, apply backpressure — this is congestion you caused |
| A failing NIC, cable or switch in your infrastructure | Yes | Find the segment, replace it; route around it meanwhile |
| Provider backbone or peering congestion | Partly | Escalate with evidence; shift traffic to another path or region |
| Public internet between edge and user | No | Move the edge closer to the user; reduce round trips; accept the rest |
| Wireless / mobile last mile | No | Fewer round trips, tolerant timeouts, graceful degradation |
Key points
- A lost packet typically costs a retransmission timeout — hundreds of milliseconds — not the time to resend one packet.
- Small request-response exchanges suffer most, because they lack the in-flight packets needed to trigger faster recovery.
- Loss-induced latency is invisible at p50 and mean, and dominates p99; the distribution shows a small distant cluster.
- Jitter correlating with a network path rather than an endpoint or tenant is the signal that separates network causes from application ones.
- Fan-out amplifies jitter: a request waiting on N calls inherits the worst variance of all N.
Follow the diagnosis
The causal chain, hop by hop — and the readings that invite the wrong conclusion.
- 1Client → server: a small request is sent; one packet is lost at a congested hop on the path.
- 2Sender → recovery: too few packets are in flight to generate duplicate acknowledgements, so fast retransmit cannot trigger.
- 3Sender → timer: recovery falls to the retransmission timeout, which is deliberately conservative and costs hundreds of milliseconds.
- 4Request → distribution: this request lands far out in the tail while every other request that second is unaffected.
- 5Fan-out → user: because the page waits on seven such calls, it inherits the worst of them, so the user-visible tail is worse than any single service's.
- • "Bandwidth is fine, so the network is fine." Loss can be high while utilisation is low; the congestion may be someone else's.
- • "Average latency is unchanged, so nothing happened." Loss-induced tails are invisible in the mean by construction.
- • "A lost packet just costs one more packet." It costs a recovery cycle, usually a conservative timeout.
- • "It is intermittent, so it is not reproducible or actionable." It is highly reproducible when segmented by path.
- • "Retries will smooth it out." Unbounded retries on a congested path add to the congestion that caused the loss.
Measure, fix, validate
An optimization is not finished until the metric that motivated it has moved.
- • TCP retransmission rate per path or availability-zone pair, which is the direct evidence of loss.
- • RTT variance (jitter) per path, not just mean RTT — the variance is the diagnostic quantity.
- • The latency distribution shape, looking for an isolated cluster hundreds of milliseconds out rather than a general shift.
- • Correlation of slow requests against network path (AZ pair, provider, client network) versus application dimensions (endpoint, tenant, payload).
- • Bandwidth utilisation, specifically to rule out self-inflicted congestion before escalating to a provider.
- • Establish which segment is losing packets before proposing anything — your link, your hardware, the provider backbone, or the last mile.
- • Reduce round trips on the critical path: reuse connections, avoid repeated handshakes, and collapse chatty exchanges.
- • Bound the damage with per-call timeouts and fallbacks so a lost packet costs the timeout rather than the retransmission timer.
- • Use idempotent, bounded, jittered retries where the call is safe to repeat, and never unbounded ones.
- • Move the edge closer to users so the uncontrollable portion of the path is as short as possible.
- • p99 and p99.9 for the affected path, compared against the same window before — p50 will not move and should not be expected to.
- • Retransmission rate on the path, which should fall if the cause was addressed rather than merely mitigated.
- • The latency distribution shape, confirming the distant cluster shrank rather than shifted.
- • Timeout and fallback rates, so a bounded tail achieved by degrading is visible as such rather than mistaken for a genuine improvement.
- • Aggressive timeouts bound the tail and cause spurious failures for legitimately slow requests.
- • Retries improve success rates on lossy paths and add load to a path that may be lossy because it is congested.
- • Moving the edge closer to users costs infrastructure and adds a distributed state problem.
- • Protocol changes to improve loss behaviour require client support and change the operational and debugging surface.
- • An alert on retransmission rate and RTT variance per path, independent of latency alerts.
- • A p99 SLO per client region or availability-zone pair, so a path degradation is visible without being averaged away.
- • Synthetic probes between critical region pairs, providing a signal that does not depend on organic traffic.
- • A dashboard splitting slow requests by network path alongside the usual application dimensions.
Accuracy
Performance numbers are conditional. These are the conditions.
- ILLUSTRATIVEThe distribution and signal values are invented to show the characteristic shape of a loss-induced tail — a small distant cluster with an unmoved median.
- ENVIRONMENT-SPECIFICRetransmission timeout values, recovery behaviour and jitter depend on the transport implementation, its configuration, and the specific network path. Measure your own paths.