Hands-on getaddrinfo cost lab: localhost vs numeric IP vs AI_NUMERICHOST, plus hosts/stub names with real p50/p95 µs. DNS sinkhole labeled honestly here.
6 min read
Every outbound connect starts with getaddrinfo. Numeric IPs should be near-free; hostnames go through nsswitch/DNS. How many microseconds on this lab box — and what can’t we claim?
This is a hands-on lab with measured numbers:
localhost, 127.0.0.1, ::1, /etc/hosts example.com.AI_NUMERICHOST on IPs (and expected failure on hostnames)..invalid) return 198.18.0.1.Related links:
Lab honesty (1 Oct 2026 IST): Python 3.13.5. Resolver is a stub/sinkhole (198.18.0.1 for dns.google, cloudflare.com, and *.invalid). This is not public DNS RTT. Affiliates: 0.
Verdict up front: warm numeric IP ~2.2 µs; localhost ~31 µs; hosts/stub names ~150–170 µs. Fresh process first localhost ~1.4 ms, then ~58 µs.
| Probe name | getaddrinfo result |
|---|---|
| dns.google | 198.18.0.1 |
| cloudflare.com | 198.18.0.1 |
| never-resolves-shoppercove-lab.invalid | 198.18.0.1 |
| example.com | 198.18.0.1 via /etc/hosts |
| localhost | ::1 + 127.0.0.1 |
So “DNS” arms here measure getaddrinfo + stub nsswitch, not overseas authoritative latency. The ranking (numeric ≪ localhost ≪ name) still teaches the API cost model.
Related links:
Script: lab-evidence/43-getaddrinfo/results/run_lab.py.
| Arm | p50 | p95 |
|---|---|---|
127.0.0.1 | 2.24 µs | 2.70 |
127.0.0.1 + AI_NUMERICHOST | 2.26 µs | 2.66 |
::1 + AI_NUMERICHOST | 1.91 µs | 2.12 |
localhost AF_INET | 20.5 µs | 35.2 |
localhost AF_UNSPEC | 31.5 µs | 48.2 |
example.com (hosts) | 169 µs | 332 |
stub dns.google | 167 µs | 300 |
stub cloudflare.com | 153 µs | 215 |
stub .invalid | 164 µs | 245 |
AI_NUMERICHOST on example.com → gaierror (expected).
Related links:
| Call | p50 |
|---|---|
| localhost first in new process | 1375 µs |
| localhost second | 58 µs |
127.0.0.1 NUMERICHOST | 10 µs |
Library/nss init dominates the first call. Don’t compare a cold first connect to a warm microbench without labeling.
localhost string costs tens of µs warm; milliseconds cold.AI_NUMERICHOST skips name syntax — use when input must already be an IP.Related links:
getaddrinfo results or connect with numeric addresses.AI_NUMERICHOST when only IPs allowed.Related links:
Timed with time.perf_counter around a single getaddrinfo call. Warm arms discard warmup iterations. Fresh-process arms spawn new Python interpreters. Sinkhole detection compares resolved IPs for unrelated names.
nameserver 10.0.0.2 in resolv.conf; hosts maps example.com → 198.18.0.1If your sidecar resolves every RPC by hostname, tens to hundreds of µs add up — unless you are on a sinkhole that lies quickly, which is its own incident.
AF_UNSPEC for localhost returned both AAAA (::1) and A (127.0.0.1) and cost ~31 µs p50 versus ~20 µs for AF_INET alone. If your client only speaks IPv4, narrowing the family avoids extra work. If you need Happy Eyeballs across real dual-stack DNS, expect more than this stub — and again, re-bench off the sinkhole.
A localhost TCP connect on this fleet has been tens of microseconds in earlier labs; a warm numeric getaddrinfo at ~2 µs is negligible beside it, while a cold ~1 ms first resolve is not. Cache at process start when the peer set is fixed.
Service meshes that already dial by IP bypass this tax entirely. Hostname-based configs are for humans; hot data planes should not rediscover the same string every request.
| Arm | ÷ numeric (~2.24 µs) |
|---|---|
| AI_NUMERICHOST IPv4 | ~1.0× |
| localhost AF_INET | ~9× |
| localhost AF_UNSPEC | ~14× |
| hosts/stub names | ~70× |
| fresh process first localhost | ~600× |
That last row is why “resolve once at startup” exists. The middle rows are why connection pools keep addresses warm.
On a real recursive resolver with cache misses, multiply again — this sinkhole cannot show you that; treat stub ~150 µs as a lower bound on the getaddrinfo path with instant answers, not an SLA for the Internet.
When documenting outages, separate “DNS provider slow” from “we call getaddrinfo on every request without caching.”
Warm numeric getaddrinfo ~2 µs, localhost ~20–31 µs, hosts/stub names ~150–170 µs on this box. A new process’s first localhost resolve sat near 1.4 ms. Use numeric/cached addresses on hot paths — and do not confuse this sinkhole with the public DNS.
Evidence: lab-evidence/43-getaddrinfo/results/. Affiliates: 0.
Lab evidence
Lab 1 Oct 2026 IST. Python 3.13.5. Warm loop: 127.0.0.1 ~2.2 us; AI_NUMERICHOST ~2.3 us; localhost AF_UNSPEC p50 31 us p95 48; localhost AF_INET 20 us. example.com (/etc/hosts) ~169 us. Stub DNS (all names -> 198.18.0.1 including .invalid) ~153-167 us — NOT public DNS RTT. Fresh process localhost first p50 ~1375 us then ~58 us. Affiliates: 0. Evidence: lab-evidence/43-getaddrinfo/.
Hands-on platform.platform vs os.uname host inventory lab: real ops/s plus cache notes, measured on Linux localhost today in this hands-on lab for SREs.
Summary1 Oct 20264 min read
Hands-on uuid.uuid4 vs uuid.uuid1 ID generation lab: real ops/s plus version/node checks, measured on Linux localhost today in this hands-on lab for SREs.
Summary1 Oct 20263 min read
Hands-on signal SIGUSR1 vs threading.Event wakeup lab: real p50 latency in microseconds, measured on Linux localhost today in this hands-on lab for SREs.
Summary1 Oct 20264 min read