Stage 04 · Distributed systems and coordination
TTL as a Coordination Primitive
Most people meet TTL as a cache setting — a number you tune so stale data eventually goes away. It’s much more useful than that.
TTL turns a coordination problem into an expiry problem. Any time you’re about to write “delete it there and insert it here, atomically”, stop and ask whether an expiring write would do instead.
Start with the problem it obviously solves
You’re tracking which of a million devices are currently online. Each one reports in periodically. You need to know who’s still there.
The obvious design: a last_seen column, and a background job that sweeps the table every minute deleting anything older than the threshold. Now you have a job to write, schedule, monitor, and get paged about when it falls behind.
The TTL design: every write expires after thirty seconds. A device that stops reporting simply stops existing.
No sweeper, no heartbeat table, no liveness protocol. The absence of a write is the signal.
That alone is worth the trade. But two more useful properties fall out of it.
It makes migration free
Suppose that data is sharded, and a record needs to move from shard A to shard B — a device moved, a tenant was rebalanced, you split a hot partition.
The correct version of that is DELETE on A and INSERT on B, atomically, across two machines. That’s a distributed transaction, and if you’re doing it at any volume it’s a serious problem.
With TTL, you write to B and walk away. A’s copy expires by itself a few seconds later.
One local write. No coordination, no two-phase commit, no migration script. The record moves by attrition.
It gives you a provable freshness bound
Nothing in the store can be older than the TTL. Not “shouldn’t be” — can’t be.
That’s a meaningfully different guarantee from “a cleanup job runs every minute and usually keeps up.” You can put WHERE updated_at > now() - ttl in a query and know it’s redundant rather than load-bearing.
And it absorbs routing mistakes
This one is a bonus that shows up whenever your routing layer is eventually consistent — a cached shard directory, a gossiping ring, a config that propagates over a minute.
During a reconfiguration, some writes land in the wrong place. With durable data that’s a reconciliation problem. With TTL’d data, a misrouted write is self-healing: it expires, and by then the router has caught up. → Hotspots and Dynamic Resharding
When it’s the right tool
It fits when the data is regenerated by its source on a known cadence — location pings, presence, heartbeats, sessions, leases, cached lookups. Losing a record costs at most one cycle of staleness, and the recovery procedure is “wait.”
It’s wrong when the data is the system of record. A ride, an order, a payment. Expiry there isn’t cleanup, it’s data loss.
It’s also wrong when regeneration is expensive or driven by a human, or when you need to tell “deleted” apart from “expired” — TTL erases that distinction.
Tuning it
TTL > write_interval × (1 + missed_writes_you_tolerate) Write every 5 seconds, TTL 15–30 seconds, and you survive two dropped writes.
Too short and participants flicker out every time the network hiccups. Too long and you’re serving records for things that have been gone for a minute.
One honest weakness: expiry is a wall-clock decision, so badly skewed clocks across a fleet cause early or late expiry. Rarely a problem in practice, but say it if you’re asked.
Plan for it
If you know a dataset is going to be resharded or migrated often, make its rows expire. You convert the hardest part of any migration — moving the data — into a no-op, before you’ve written a line of migration code.
In an interview
Mention more than one job the TTL is doing. Most candidates treat it as cleanup. Explaining that it also lets a record move between shards without a distributed transaction is the part that stands out.
Give the tuning rule when you set one — TTL longer than the write interval times the number of missed writes you’ll tolerate — so the number isn’t arbitrary.
The trap: putting a TTL on a system of record. It’s right for data the source regenerates; on an order or a payment, expiry is data loss.
Seen in
- 04 - Driver Location Index — liveness, cross-shard movement, and a freshness bound, from one TTL
- 10 - Dynamic Resharding and the Shard Index — the same TTL absorbing staleness during a live reshard
Related
Avoiding Distributed Transactions · Hotspots and Dynamic Resharding · Caching Strategies