Generated from: “The median looks fine but people complain checkout is slow — show the latency picture per endpoint.”
AI-generated example · 2026-09-13. The source passes chiltepin check. System details and measurements are illustrative; review them before adapting this document.
Edited 2026-09-14: Corrected the call count, removed the unsupported tail-probability calculation, and separated sample observations from causal claims.
View the Markdown
```meta
title: Checkout latency — the tail per endpoint
subtitle: Why the median passes and the users still wait.
tag: PERF
```
This example follows five calls in one checkout: cart, validation, shipping quote,
payment, and order creation. Each endpoint's median is below 300 ms, while
shipping, payments, and order creation exceed the 300 ms p99 target.
The values are illustrative samples, not production measurements. Endpoint
percentiles do not determine the distribution of the full checkout: calls may
share slow dependencies, and their durations can be correlated. Measure
end-to-end checkout latency separately.
## Compare endpoint latency
```percentiles
id: endpoints
title: Checkout endpoints — p50 to max
unit: ms
slo: 300
rows:
- { label: GET /cart, p50: 18, p90: 35, p95: 48, p99: 90, max: 410 }
- { label: POST /cart/validate, p50: 42, p90: 80, p95: 110, p99: 260, max: 1200 }
- { label: POST /shipping/quote, p50: 65, p90: 140, p95: 210, p99: 540, max: 2800, accent: amber }
- { label: POST /payments, p50: 240, p90: 380, p95: 470, p99: 900, max: 3100, accent: red }
- { label: POST /orders, p50: 55, p90: 95, p95: 130, p99: 310, max: 1400, accent: amber }
```
Payments has the highest p99 in this sample. Shipping and order creation also
exceed the target; cart and validation meet it at p99. The maximum column shows
that even those two endpoints have slower outliers. These values identify where
to investigate; they do not explain the cause.
## Inspect one slow checkout
```spans
id: slow-trace
title: Example checkout — 1 880 ms end to end
unit: ms
spans:
- web/checkout: POST /checkout · 0 · 1880
- api/cart: GET /cart · 6 · 22 · checkout
- api/validate: POST /cart/validate · 30 · 48 · checkout
- api/quote: POST /shipping/quote · 80 · 560 · checkout
- { id: carrier, service: carrier, name: "rate lookup (UPS)", start: 92, duration: 540, parent: quote, kind: client, note: "cache miss in this example" }
- api/pay: POST /payments · 650 · 1120 · checkout
- { id: auth1, service: card-provider, name: "authorize (attempt 1)", start: 660, duration: 500, parent: pay, kind: client, error: true, note: "timeout at 500 ms" }
- { id: auth2, service: card-provider, name: "authorize (attempt 2)", start: 1165, duration: 580, parent: pay, kind: client }
- api/order: POST /orders · 1780 · 90 · checkout
- { id: db, service: postgres, name: "INSERT order + lines", start: 1790, duration: 70, parent: order, kind: db }
```
This trace contains a payment timeout followed by a retry, plus a slow carrier
lookup. Together they occupy most of this request. A single trace cannot show
how often either path occurs; compare more traces before choosing a change.
```callout
tone: note
title: Next investigation
body: "Group slow traces by dependency, retry count, and cache outcome. Check payment retry and idempotency behavior before changing timeouts. Evaluate whether shipping quotes can be cached without returning stale prices. Re-measure the full checkout after each change."
```