BitBootcampBitBootcamp ← all explainers 🔄 Review ⚙ Settings

Drill 75 — The TTL That Was Too Long

Theme: Networking & DNS · Format: 20-min drill · Difficulty: core

The story

A payments platform runs active-passive across two regions, and the failover plan is one line in the runbook: if region A's health checks fail, point api.pay.example.com at region B. It has been rehearsed on a whiteboard and never in anger.

One Sunday, region A's database tier degrades and error rates climb. Ops declares failover and updates the DNS A record to region B's load balancer within four minutes — clean, by the book. Region B is healthy and nearly idle, waiting for the traffic.

The traffic never really comes. Hours pass and a large share of requests keep landing on the degraded region A. Customers keep seeing errors long after the incident channel says "failed over." Someone finally checks the record: its TTL is 86400 — twenty-four hours — set once, years ago, and never revisited. Every caching resolver at every ISP and corporate network, and every client's own cache, is still handing out region A's address.

⏸ Stop here and ask the room: region B was healthy and the DNS was changed in four minutes. Why did customers keep hitting the broken region for the rest of the day?

Run it — facilitator script

Timing for a 20-minute session: 2 min story · 10 min staged questions · 3 min reveal · 5 min transfer.

What does a TTL actually control — and who obeys it?

Is DNS even the right failover mechanism?

What should the TTL have been, and what's the tradeoff?

The reveal

The failover "worked" in the console in four minutes and did not work for users for most of the day — because the observable that matters is where requests land, not what the record says. The team dropped failover-record TTLs to 60 seconds and, more importantly, moved critical endpoints behind a health-checked global load balancer so the client-facing address never changes and cutover happens at the routing layer in seconds. They added external synthetic probes from several networks to measure the actual traffic shift, so "failover complete" now means observed, not edited.

Why this happens — the mechanics

DNS is a distributed cache you don't own. A resolver caches your answer for up to its TTL, and you cannot invalidate caches on machines you don't control — so DNS-based failover is bounded below by a TTL you had to choose in advance, and further by resolvers that clamp very low values upward. The console showing the new record is not the same as traffic having moved; the only trustworthy signal is where requests actually land, measured from outside your network.

Relying on DNS TTL for fast failover confuses "I changed the config" with "the world sees the change." It's the green-console-versus-reality gap of the silent-failure drills, wearing a networking costume — and the fix is the same: change the mechanism so the guarantee is structural (routing-layer failover), and measure the effect externally instead of trusting the edit.

Diagrams

Problem — how the failure unfolds:

timeline
    title Failover that didn't fail over
    section Sunday
        12.00 : Region A DB degrades, errors climb
        12.04 : Ops updates DNS A record to region B
        12.04-18.00 : Resolvers still serve cached region A (TTL 24h) : Users keep hitting the broken region
    section Monday
        Overnight : Caches slowly expire, traffic finally shifts
        AM : Record TTL discovered - set to 86400 years ago

Solution — where the guardrail goes:

flowchart TD
    A["Failover needed"] --> B["Health-checked global LB /<br/>anycast: address stays constant"]
    B --> C["Traffic shifts at routing<br/>layer in seconds"]
    A --> D["Failover DNS records: TTL 60s,<br/>pre-lowered before maintenance"]
    C --> E["External synthetics measure<br/>ACTUAL traffic shift"]
    D --> E
    E --> F["'Failed over' = observed, not edited"]

What good looks like

The lesson

DNS is a cache you don't own; its TTL is the floor on how fast you can fail over, and you set that floor long before the incident. Never confuse "I updated the record" with "traffic moved" — measure the shift from outside.

Named concepts: DNS TTL, propagation, health-checked failover, external synthetic monitoring.

Real-world echoes

Transfer

For each of your failover plans: what is the TTL on the record you'd change, and have you ever measured how long traffic actually takes to move? "We'd update DNS" is not a failover time.

Make it a ticket: inventory failover-critical DNS records, set their TTLs to 60s, move the critical path behind health-checked routing, and add an external synthetic that measures real traffic shift during a game-day failover.

Related