DNS and connection setup
Trace a client request and design an HTTP contract with clear failure, retry and pagination behavior.
THE IDEA
DNS and connection setup
Before a request can be answered, the client must find an address (DNS, cached according to the record's TTL), open a transport connection (a TCP handshake) and usually negotiate encryption (TLS). Each step can cost round trips. In a simple model with a 40 ms round trip, a cold request costs 4 round trips (160 ms) before any server work; with the DNS answer cached it is 120 ms; on a reused connection it is just 40 ms. That is why connection reuse matters, and why protocol choices change the path.
Predict first. Does resolving a hostname prove an application request succeeded?
Chapter context
Clients communicate through name resolution, connections, transport security and application protocols. API semantics determine what a caller can safely infer or repeat when a network interruption hides an operation's outcome.
TRY IT YOURSELF
Part 1. Do it hands-on
Count round trips by hand, then write the latency model and a TTL cache.
- To do: Simple model, round trip 40 ms. A first request needs: DNS lookup (1 round trip to the resolver), TCP handshake (1), TLS 1.3 handshake (1) and the request/response itself (1). Total ms before the response arrives?
- To do: The DNS answer is cached. Total ms?
- To do: The connection from an earlier request is reused (no DNS, TCP or TLS setup). Total ms?
- To do: Does resolving a hostname prove an application request succeeded? Type yes or no
Part 2. Do it in code
- setup_rtts(dns_cached, reused, tls="1.3"): round trips before the response arrives, in a simple model. A reused connection needs only the request/response (1). Otherwise: DNS lookup 1 (0 if cached) + TCP handshake 1 + TLS handshake (1 for "1.3", 2 for "1.2") + the request 1. ValueError for another TLS version.
- latency_ms(rtt_ms, dns_cached, reused, tls="1.3"): rtt_ms × setup_rtts.
- cache_put(cache, name, ip, now, ttl): store ip with expiry now + ttl in the dict cache.
- cache_get(cache, name, now): the ip if the entry exists and now < expiry, otherwise None (and the expired entry is removed).
- resolve(cache, name, now, ttl, lookup): return (ip, hit) — from the cache if fresh (hit True) else call lookup(name), cache it and return (ip, False).
Stuck, or something not working? You can come back to it any time.
CHECK YOUR UNDERSTANDING
Does resolving a hostname prove an application request succeeded?
Chapter reference: math, worked example & code
This section belongs to the whole chapter, so it is the same on every lesson in it and may cover a different lesson's topic. The code for the experiment you just ran is inside the experiment itself.
Math and notation
For k sequential calls with round-trip latency r, the network component alone is approximately k*r under a simple fixed-RTT model. Independent parallel calls have an ideal max-path cost instead. This excludes connection setup, payload transfer, queueing and application computation.
Worked example
Three required sequential remote calls with 40 ms round trips consume about 120 ms of round-trip time before server work. For a separate write example, processing request key op-7 twice should create one logical result if the idempotency contract is implemented atomically. The calculation below is an offline model, not a distributed storage implementation.
Reference code
The following example uses the Python standard library and does not call a remote service.
assert 3*40 == 120
seen={}
def create(key,value):
if key in seen:
if seen[key] != value: raise ValueError('key reused with different intent')
return seen[key]
seen[key]=value
return value
assert create('op-7','order-1') == create('op-7','order-1')
assert len(seen) == 1
Python examples are provided for study and local execution; this page does not execute Python.
Common mistakes
A timeout is not proof of rollback. HTTP transport security does not authorize object access. An in-memory deduplication dictionary does not supply durability or atomicity across servers.