Appearance
Anatomy of a Check
For every check processed by latency a timeline of segments (spans of time) are recorded: each segment has a name, a start offset, and a duration. Those segments are what you chart, alert on, and drill into to diagnose issues.
We say timeline and not "waterfall" on purpose. A basic http 1.1 request where one phase starting exactly where the last ended, is only the simple case. Real requests overlap and skip phases (see What is TTFB and conditions that bend the timeline).
The headline number
An http check defaults to ttfb_us — time to the first response byte (DNS + connect + TLS + wait). total_us adds the body transfer. Both roll up the segment timeline, stored in microseconds. Each check has its own total, see the primary metric.
The phases
dns — resolve the name. Trade api.acme.io for an IP: one round trip if the answer is cached upstream, several if the resolver walks root → TLD → authoritative. dns_us covers the whole detour, CNAMEs included; it is 0 for an IP literal.
tcpConnect — open the socket. SYN, SYN-ACK, ACK. One round trip and no work, so connect_us is your cleanest read on raw network distance — a probe in Frankfurt hitting Virginia pays ~90ms here no matter how fast the server is.
tls — prove identity, agree on keys. ClientHello → certificate → Finished. A slow tls_us is almost always a fat certificate chain or a cold connection with no session resumption. Plain http skips it; HTTP/3 fuses it (see below).
wait — the server thinks. The request is on the wire, then silence while the backend routes, queries and renders. server_us is "request sent → first byte" — the place backend slowness hides.
response — the body arrives. transfer_us is how long the bytes take to land, a function of payload size and bandwidth, not the server's brain.
TTFB is a roll-up, not a segment
ttfb_us = dns + connect + tls + wait — the whole stretch to the first byte, the honest "how long until the server started answering." To find why it is slow, read the leaves underneath. When a check hangs, the segment it died in is marked failed or timeout, so the timeline points straight at the break.
The primary metric
Every monitor has a total latency — the whole time to a useful response.
| Check | Primary (default) | The total is |
|---|---|---|
http | Latency (TTFB) = ttfb_us | DNS + connect + TLS + wait (total excluding transfer). |
tcp | Latency (total) = latency_us | DNS + the TCP connect handshake. |
tls | Latency (total) = latency_us | DNS + the connect and TLS handshake. |
icmp / udp | Latency (total) = latency_us | DNS + the echo / datagram round trip. |
dns | Latency (total) = latency_us | The resolution itself. |
Swap it for any phase (make server_us the headline for a backend-bound API), and alert on any of them — including deviations (deviates_pct)
Conditions that bend the timeline
The serial picture above is one case. Real requests take shortcuts and detours — the reason every segment carries an explicit start and duration instead of chaining off the previous one. In terms of the Resource Timing model:
- Redirects. Each
3xxis a full extra round trip with its own DNS, connect, TLS and wait. We follow them and sum every phase across the chain (dns_usis the whole chain, not just the final URL), nested under aredirectsegment per hop. Point the monitor at the final URL to skip them. - HTTP/3 (QUIC). Transport and crypto are a single handshake over UDP, so you get one
quicHandshakesegment instead oftcpConnect+tls. (Beta.) - A warm / reused connection. DNS, TCP and TLS happened on an earlier request, so those segments are simply absent and
waitis the whole story — zeros mean "reused," not "instant." (Our probe opens a fresh connection each check, so you'll always see them; browsers and keep-alive clients won't.) - Happy Eyeballs (RFC 8305). A and AAAA resolve in parallel and IPv4/IPv6 connects race, so several
tcpConnectsegments run at once — the winner isok, the loserscancelled. - TCP Fast Open / TLS 1.3 0-RTT. The request rides the first flight, so
requestoverlaps the handshake — it starts before TLS finishes. - Early Hints (
103). The server may send an interim103before the real response, so the "first byte" that endswaitcan be that hint, not the final headers. - Cache & service workers. In a browser, an HTTP cache or service worker can short-circuit the network entirely. An availability probe deliberately doesn't cache — every check measures the real path. (The browser-only Processing and Load phases in the reference diagram don't apply to a server-side check.)
- Multiplexing (HTTP/2 & /3). Many requests share one connection, so their
responsespans interleave on the wire.
So the strict dns + connect + tls + wait = ttfb identity only holds for the clean serial case. What is always true is interval math:
ttfb_usis the wall-clock envelope from the first activity to the first response byte. Each phase metric is the sum of its segments' durations — and because segments can overlap, they can sum to more than the envelope, equal only in the serial case.
Types of metrics
| Metric | Kind | The question it answers |
|---|---|---|
latency_us | roll-up (default primary) | The kind's total — its "now" number. |
ttfb_us | roll-up (http default) | End to end up to the first byte. |
total_us | roll-up | End to end, transfer included. |
dns_us | segment | Is name resolution the bottleneck? |
connect_us | segment | How far away is the server, really? |
tls_us | segment | Is the certificate / crypto exchange slow? |
server_us | segment | Is the backend slow (pure think time)? |
transfer_us | segment | Is the payload / bandwidth the cost? |
rtt_us | segment | How long is the echo / datagram round trip? |
Any of these can be a monitor's primary or the subject of an alert rule.
The segments
Each check stores its timeline as a small array of segments:
| Field | Meaning |
|---|---|
index | its position in the array (its identity within the check) |
parentIndex | the segment that contains it, or -1 for a top-level phase |
kind | dns, tcpConnect, tls, quicHandshake, request, wait, response, redirect |
startUs | offset from the start of the check, in microseconds |
durationUs | how long it took, in microseconds |
status | ok, failed, timeout, or cancelled (a raced-but-lost attempt) |
Nesting via parentIndex carries the structure: a redirect chain is one redirect parent per hop, with that hop's own dns/connect/tls/wait segments beneath it.
Why microseconds
latency is unlike other monitors, every timing is stored and computed as an integer number of microseconds (µs). Here's the difference in units:
| Unit | Symbol | In one second |
|---|---|---|
| second | s | 1 |
| millisecond | ms | 1,000 |
| microsecond | µs | 1,000,000 |
A millisecond is one thousandth of a second, the human friendly unit for latency (aka "180 ms"). A microsecond is one thousandth of a millisecond, a millionth of a second. So 1 ms = 1,000 µs, and a 10-second timeout is 10,000,000 µs.
Probes measure with a high-resolution clock, which returns fractional milliseconds, sub-millisecond precision. Real phases live down there: a warm DNS answer is around 300 µs (0.3 ms), a local TCP connect can be around 40 µs. Round those to whole milliseconds and they collapse to 0, and the breakdown that makes these metrics useful is lost.
So Latency picks µs as its canonical unit and refuses to convert early:
- Capture — the probe's fractional-ms measurement is turned into an integer µs once, at the moment it is recorded.
- Storage — ClickHouse holds each phase (and each segment's start and duration) as a
UInt32of µs. Integer, compact, exact. - Rules and the wire — the alerting engine compares in µs and the API serves the same. No floating-point drift to creep in.
- Display — the only place µs becomes ms or s is the screen and you can always expand for detail.