Latency: Same Machine to Cross-Continent
Round-trip time spans five orders of magnitude from loopback to cross-continent, every request costs at least one of them and a cold HTTPS request three or four, so a chatty pattern that is invisible in one data centre is a multi-second disaster across an ocean.
The problem
Five rungs of distance
The interactive uses illustrative values chosen to be typical; real numbers depend on hardware, provider, path and load, and the only way to know yours is to measure. With that said, the ladder is remarkably stable across environments because each rung is set by a different physical fact: loopback by kernel stack cost, in-DC by switch hops and cable length, in-region by the distance between availability zones, cross-region and cross-continent by the great-circle distance in fibre.
What matters is not the values but the ratios: each rung is roughly 5–50× the previous. A design that tolerates a thousand round trips at 50 µs (50 ms total) tolerates forty at 1 ms (40 ms) and two at 150 ms. Distance does not make individual requests slower in a way you can optimise; it multiplies the number of round trips by a constant you cannot change.
| Between | Typical RTT | Set by | Round trips in a 100 ms budget |
|---|---|---|---|
| Same machine (loopback) | ~20–50 µs | kernel stack, no NIC | ~2,000–5,000 |
| Same data centre / AZ | ~0.1–0.5 ms | switch hops, tens to hundreds of metres | ~200–1,000 |
| Same region (AZ ↔ AZ) | ~1–2 ms | tens of km of fibre | ~50–100 |
| Cross region (e.g. US East ↔ US West) | ~30–80 ms | thousands of km | ~1–3 |
| Cross continent / ocean | ~100–250 ms | ten thousand+ km, cable routes | 0–1 |
The physics, and why the floor is real
Light in fibre travels at about 200,000 km/s, two thirds of c, because of the glass’s refractive index. A round trip over d kilometres of fibre therefore takes at least 2d/200,000 seconds: 10 µs per kilometre, round trip. New York to London is ~5,600 km on the map, giving a floor of ~56 ms; real cables are longer and add amplifiers and routers, so ~70–75 ms is what you see. Frankfurt to Sydney: ~16,500 km, a floor of 165 ms, ~250 ms in practice through Singapore.
Each rung also adds equipment delay — a switch forwards in a few microseconds, a router in tens, and a queue in front of a congested link in milliseconds (Where the Time Goes: The Request Timeline) — but at continental distance propagation dominates everything. This is a lower bound: no faster server, no better protocol, no more bandwidth reduces the time a photon needs to cross the Atlantic. Faster is only possible by being closer, which is the entire reason a CDN exists (CDNs: The Networking View).
The RTT multiplier
No request costs less than one RTT: the bytes go there and an answer comes back. A cold HTTPS request costs more: a DNS lookup (often cached, otherwise ≥ 1 RTT to the resolver), one RTT for the TCP handshake (The Three-Way Handshake), one for TLS 1.3 (The TLS Handshake; two for TLS 1.2), then one for the request and response — three to four RTTs before the first byte. A warm request over a pooled keep-alive connection (Keep-Alive and Connection Reuse, Connection Pooling) costs one. Across an ocean that is the difference between ~180 ms and ~700 ms for the same call, which is why connection reuse matters more the further away the peer is.
Larger responses add round trips of their own: TCP slow start begins at ~10 segments (~14 kB) and doubles every RTT (Congestion Control: Protecting the Network), so a 1 MB response over a fresh connection needs about seven RTTs of ramp-up regardless of bandwidth — 700 ms at 100 ms RTT, 7 ms at 1 ms. Bandwidth vs Latency works the arithmetic.
same region (1 ms RTT) cross continent (150 ms RTT)
TCP handshake 1 ms 150 ms
TLS 1.3 handshake 1 ms 150 ms
request → first byte 1 ms + server 150 ms + server
───────── ───────────
before first byte ~3 ms + server ~450 ms + server
warm (pooled) request ~1 ms + server ~150 ms + serverN+1 across regions: the classic disaster
The N+1 pattern — one query for a list, then one query per item — costs N+1 round trips. Against a database in the same rack at 0.2 ms, 40 items is 8 ms and nobody notices. Move the database to another region at 100 ms and the identical code takes 4.1 s. The ORM did not change; the SQL did not change; the round-trip count was always 41, and each one is now 500× more expensive. The Database domain treats this as a query-shape bug; from here it is a latency-multiplier bug, and the fix is the same: fewer round trips — one query with a join or an IN list, a batch API, a pipeline that sends several commands without waiting.
The same shape hides in microservice chains (service A calls B calls C, each in turn), in sequential API calls from a browser, in a Redis client that does not pipeline, and in any loop that awaits a remote call per iteration. Concurrency helps only when the calls are independent: 40 parallel calls cost ~1 RTT of wall-clock time plus queueing, 40 sequential ones cost 40. The rule that survives every environment is: count the sequential round trips in the critical path, multiply by the RTT of the farthest hop. That number is your floor, and it is usually the bill.
1// N+1: one list query, then one query per row — 41 sequential round trips2const orders = await db.query('SELECT id, user_id FROM orders WHERE day = $1', [day]);3for (const o of orders) {4 o.user = await db.query('SELECT * FROM users WHERE id = $1', [o.user_id]); // 1 RTT each5}6 7// one round trip: let the database do the join8const rows = await db.query(9 'SELECT o.id, u.* FROM orders o JOIN users u ON u.id = o.user_id WHERE o.day = $1',10 [day],11);12 13// independent calls: parallel costs ~1 RTT, not N14const [profile, cart, recs] = await Promise.all([getProfile(id), getCart(id), getRecs(id)]);Key points
- RTT spans ~20 µs (loopback) to ~250 ms (cross continent); the simulator’s values are illustrative — measure yours.
- Light in fibre is ~200,000 km/s: ~10 µs per km round trip; a cross-ocean RTT is a physical floor.
- Every request costs ≥ 1 RTT; a cold HTTPS request costs 3–4; a large response adds slow-start round trips.
- Connection reuse (keep-alive, pooling) matters more the farther the peer.
- N+1 and sequential call chains multiply the RTT; the fix is fewer sequential round trips, not faster servers.
- Count sequential round trips on the critical path × RTT of the farthest hop: that is the floor.
Why does this exist?
Mechanisms are answers to constraints. Open each question before reading the answer.
▸Why can this not be fixed with more bandwidth?
Bandwidth is how many bytes fit in a second; latency is how long the first byte takes to arrive. A request that waits for an answer is bounded by the trip, and a wider pipe does not shorten the trip. See Bandwidth vs Latency.
▸Why do cold requests cost several RTTs?
TCP and TLS each need an exchange before any application byte can be sent; each exchange is a full trip. Warm connections have already paid for them.
▸Why is geographic distance a distributed-systems concern and not just a web one?
Every consensus round, every synchronous replication acknowledgement, every cross-region lock is a round trip too. A replica 100 ms away cannot acknowledge in less than 100 ms; the same floor bounds replication lag, commit latency and failover.
Latency by distance
speed of light in glass ≈ 200,000 km/s 5,500 km ÷ 200,000 km/s = 27.50 ms one way × 2 = 55.0 ms round trip — before any router, queue or retransmission
How it fails
What the failure looks like from inside real software.
- Endpoint goes from 10 ms to 4 s after "moving the database to the managed service in another region": N+1 queries, each now paying a cross-region RTT.
- p50 fine, p99 terrible from one geography: users far from the origin pay cold-connection RTTs; no CDN or regional endpoint; connection pooling absent on their path.
- Microservice chain of six sequential calls at 2 ms each is fine in one region and 600 ms when one service is deployed elsewhere: the chain multiplies the slow hop.
- Redis in another AZ "is slow": each command is a round trip; the client is not pipelining; 1–2 ms × thousands of commands.
- Synchronous replication to a cross-region standby makes every commit take ≥ 1 RTT: not a tuning problem, a distance one.