A metadata store on a 5-node Raft group serves 50k reads per second against 2k writes per second. Reads must be linearizable and the read path must not add a network round trip. Which read path fits?
Show the full answer Hide the answer
The deciding property
Not the read rate. The deciding property is the stated constraint that the read path adds no round trip, because that single sentence eliminates three of the five options - and it should make you ask whether the constraint is real before you accept the clock assumption that comes with satisfying it.
A leader cannot simply read its own memory and claim linearizability. It may have been deposed by a quorum it cannot see, in which case another leader is already committing writes the old leader knows nothing about. Every linearizable read path is a way of establishing "I am still the leader right now" at some price.
Why the lease read
A leader that wins an election holds an implicit lease: no other leader can be elected until a full election timeout has elapsed without heartbeats. If the leader tracks the time of its last successful heartbeat round and serves local reads only inside that window minus the maximum clock error, the read is linearizable with no messages at all. This is the lease-read optimisation described in the Raft dissertation (2014) alongside the ReadIndex approach, and implemented in etcd and TiKV.
The price is explicit: correctness now depends on bounded clock drift between the members. It is a real assumption, not a formality - a virtual machine suspended and migrated can resume with a stale clock and believe its lease is still valid. CockroachDB's documented default treats 500 ms as the maximum tolerated offset and has a node shut itself down when it finds itself more than 80% of that away from half the cluster, which is the shape of the guard rail this option needs.
Why the other options fail
- Follower reads. Correct for most metadata use and simply not linearizable: a follower can be an entry behind, so a client that just wrote can read the old value. Right answer to a different question, and the one to propose when you discover the no-round-trip constraint was someone's preference rather than a requirement.
- A no-op log entry per read. This is correct and it is the most expensive thing on the list: 50k reads per second becomes 50k consensus rounds and 50k durable writes per second, 25 times the write load the cluster was sized for. Teams reach for it because it is easy to reason about, then discover the disk.
- ReadIndex with a heartbeat round. Also correct, and the right default when you cannot bound clock drift. It costs one round trip to a quorum per batch of reads - cheap, but the constraint said no round trip, so it loses here and wins the moment the clock assumption looks shaky.
- Quorum read across followers. This is the Dynamo-style read-repair instinct applied to a log-replicated system, and it does not transfer. Entries a follower holds may not be committed, so "newest value found" can return a write that is later truncated, which is worse than stale: it is a value that never existed.
What would flip the decision
| If this changes | Choose | Because |
|---|---|---|
| Clocks cannot be bounded (shared virtual machines - frequent migrations) | ReadIndex | one round trip is cheaper than a correctness bug |
| Reads tolerate seconds of staleness | Follower reads | scales horizontally and takes load off the leader |
| Read rate falls below the write rate | No-op log entry | simplest to reason about and the cost is now trivial |
| Reads must survive leader loss | Follower reads with a staleness bound | a leaderless window has no lease to read inside |
When this is the wrong answer
If the service can tolerate stale reads at all, every row above is over-engineering. The majority of metadata reads in real systems are followed by a write that will be validated anyway, so a compare-and-swap on the write path gives the safety that the strict read was protecting, at no read cost.