definition

What tail latency is

Tail latency refers to the slowest fraction of requests in a latency distribution. The "tail" is the long right side of a histogram — most requests finish quickly, but a small percentage take much longer. p99 tail latency means the slowest 1% of requests. p999 means the slowest 0.1%.

At 1,000 requests per second, p99 tail latency affects 10 requests every second. At 10,000 req/s, 100 requests per second hit the tail. These aren't edge cases — they're a constant stream of degraded user experiences.

the problem with averages

Why averages hide the damage

A service with a 20ms average response time sounds fast. But if 1% of responses take 10 seconds, the average is dragged down by the 99% majority and tells you nothing about the worst experiences.

// 1000 requests: 990 at 10ms, 10 at 10,000ms
average = (990 * 10 + 10 * 10000) / 1000
        = (9900 + 100000) / 1000
        = 109.9ms  // looks OK

p99 = 10000ms     // 1% of users wait 10 seconds

An average of 110ms might be acceptable. A p99 of 10 seconds is not. Use percentiles, not averages, to understand latency.

fan-out amplification

How tail latency spreads across your system

When a request fans out to multiple downstream services in parallel, the overall latency is the maximum of all the downstream latencies — you have to wait for the slowest one. This amplifies tail latency dramatically.

// Each downstream call has 1% chance of hitting p99 tail
// Probability that all N calls are fast:
P(all fast) = 0.99 ^ N

// Probability that at least one hits the tail:
P(any slow) = 1 - 0.99 ^ N

// For N = 10:  1 - 0.99^10  = 9.6%  of requests see tail latency
// For N = 50:  1 - 0.99^50  = 39.5%
// For N = 100: 1 - 0.99^100 = 63.4%

A page that makes 100 parallel API calls — common in microservice architectures — will hit the tail latency of at least one dependency on 63% of requests, even though each individual service has a 1% tail.

availability

Slow is the new down

From a user's perspective, a request that takes 10 seconds is functionally equivalent to a request that failed. Most applications implement timeouts to avoid hanging forever — which means tail-latency events convert directly into errors.

If your timeout is 5 seconds and your p99 is 4.9 seconds, you have essentially zero margin. Any increase in p99 — a momentary GC pause, a noisy neighbour, a cold cache — pushes responses past the timeout and turns them into 500 errors.

Timeout calibration

Your timeout should be set to roughly p999 of the service you're calling — covering 99.9% of legitimate responses while cutting off true hangs. Calibrating without realistic latency data means guessing.

mitigation

Strategies for handling tail latency

strategyhow it workstradeoff
Hedged requestsSend a duplicate request after a short delay; use whichever responds firstDoubles load on the downstream at tail percentiles
Timeouts + retryCut off slow requests and retry on a different instanceRequires idempotency; can cause retry storms
Circuit breakerFail-fast when downstream is consistently slowRequires fallback logic; needs careful threshold tuning
Shorter fan-outReduce the number of parallel calls per requestArchitectural change; may not be possible
CachingServe from cache to avoid hitting the slow dependencyStale data risk; cache invalidation complexity

slowdep lets you test all of these strategies by simulating the realistic latency distribution they need to handle. A circuit breaker tested against only fast responses hasn't been tested at all.

further reading

Related topics