Consensus: Raft and Paxos in Plain English
hardConsensus is the problem of getting distributed nodes to agree on a value (or sequence of values) despite some failing or being temporarily unreachable. It's the foundation of every strongly consistent distributed system: etcd, ZooKeeper, Spanner, CockroachDB, Kafka KRaft.
Key Concepts
AppendEntries → commits once a majority acks. Safety: only nodes whose log contains all committed entries from prior terms can be elected leader (leader completeness).(N-1)/2 failures and avoid split votes. 3 nodes tolerate 1 failure (need 2 to write); 5 tolerate 2 (need 3). Going to 7+ increases latency without increasing fault tolerance for typical single-failure-at-a-time scenarios. Even numbers (4, 6) are bad — same fault tolerance as 3, 5 but higher split-vote risk.Approach
- Pick an existing implementation: etcd, ZooKeeper, Consul, or roll consensus into your system via a library (HashiCorp Raft, Etcd-io Raft).
- Size the cluster — 3 nodes (1 failure tolerated) for non-critical; 5 for high availability.
- Always odd-numbered. Even = split-vote risk; the extra node doesn't increase fault tolerance.
- Snapshot regularly; truncate log to bound growth.
- Use joint consensus for membership changes, never naive replace.
- For read scaling, use leader lease + ReadIndex; only relax to follower reads if your consistency story allows it.
- Geo-distribute carefully — latency-bound; cross-region writes hurt. Often run consensus per-region with cross-region async replication.
- Monitor leader election rate, commit latency, log size, snapshot age.
Raft in detail
Three roles: follower, candidate, leader.
Term: monotonically increasing logical clock. Each term has at most one leader.
Election: follower timeout → candidate → request votes → majority → leader.
AppendEntries: leader sends entries with prev-log-index + prev-log-term. Followers reject if mismatch (log-matching property).
Commit: leader marks entry committed once majority has it; applies to state machine.
Safety: leader-completeness — leader for term T contains all entries committed in prior terms.
Membership change: joint consensus (Cold ∪ Cnew) or single-node-at-a-time.
Components
- Replicated log on each node (append-only, persistent).
- State machine that applies committed entries deterministically.
- Term number (logical clock).
- Heartbeats from leader (also serve as keepalive).
- Election timer (randomized to avoid simultaneous candidacies).
- Snapshot mechanism + log truncation.
- Membership configuration (joint consensus protocol).
- ReadIndex / lease for consistent reads.
Trade-offs
Strong consistency at the cost of majority availability. Lose quorum, lose writes.
Read scalability is poor by default — every read goes through leader for strong consistency.
Write latency bounded by 1 cross-node RTT — fast within DC, slow across regions.
Membership changes are subtle — easy to get wrong without joint consensus.
Snapshotting is essential for any long-running cluster.
Operational complexity: leader election storms, slow followers, network partitions all need handling.
Common interview/practice mistakes
- Even number of nodes (4, 6). Doesn't help fault tolerance, increases split-vote risk.
- Believing reads scale by adding nodes. They don't, unless you relax consistency.
- Running 7+ nodes for 'safety'. Latency degrades; same fault tolerance as 5.
- Cross-region consensus for low-latency workloads. Geo-Raft is real but slow; consider regional + replication.
- Rolling your own. The bug surface is enormous; use etcd, ZooKeeper, or libraries.