By the end of this lesson
  • Explain the implications of the FLP Impossibility Theorem and how practical consensus protocols bypass it using partial synchrony
  • Trace Phase 1 (Prepare/Promise) and Phase 2 (Accept/Accepted) of Single-Decree Paxos and Multi-Paxos
  • Prove the five core Raft safety invariants and the Log Matching property
  • Derive why Crash Fault-Tolerant (CFT) systems require 2f + 1 nodes, while Byzantine Fault-Tolerant (BFT) systems strictly require 3f + 1 nodes

Consensus is the pinnacle challenge of distributed systems. At its core, consensus solves a seemingly simple question: How can a group of independent computers agree on a state transition or log sequence over an unreliable network where packets can be arbitrarily delayed, reordered, duplicated, or dropped, and where individual machines can crash at any instant?

Without consensus, building reliable distributed databases, distributed locking services, and linearizable coordination primitives is mathematically impossible.

This lesson explores the foundations of consensus: the FLP Impossibility Theorem, Leslie Lamport’s Paxos, Diego Ongaro’s Raft, and the rigorous mathematical divergence between Crash Fault-Tolerance (CFT) and Byzantine Fault-Tolerance (BFT).


1. Problem and Motivation

Consider an etcd or ZooKeeper cluster managing leader leases for a distributed database.

  • If the primary database locks a row, writes an update, and commits, all replicas must agree on that exact sequence of operations.
  • If a temporary network switch partition splits a 5-node cluster into two groups ( nodes and nodes), both sides cannot be allowed to elect independent leaders and commit concurrent writes (Split-Brain catastrophe).

Why can’t we simply use two-phase commit (2PC)?

  • 2PC is a blocking protocol: If the coordinator crashes while participants are in the PREPARED state holding locks, the entire system blocks indefinitely. 2PC guarantees atomicity, but fails to provide availability in the presence of node crashes.

True consensus protocols provide both Safety (nothing bad happens; nodes never decide on conflicting values) and Non-blocking Liveness (eventually, all healthy nodes decide).


2. Mental Model & Architectural Taxonomy

SYSTEM ARCHITECTURE BLUEPRINTDISTRIBUTED SYSTEMS
Distributed Consensus: Multi-Paxos, Raft Invariants & CFT vs. BFT
ARCHITECTURAL FLOW · STEP 1 OF 3

1. Phase 1: Leader Lease / Prepare (Run once per term) & Phase 2: Steady-State Log Append (Fast Path)

Establishes the ingress and structural boundary across Phase 1: Leader Lease / Prepare (Run once per term) & Phase 2: Steady-State Log Append (Fast Path): 1a. Proposer sends Prepare(proposal_number = n) 1b. Acceptors return Promise(highest_v_seen, highest_n_seen) Acceptors pledge never to accept any future proposal < n Requires MAJORITY quorum (f + 1 of 2f + 1 nodes); 2a. Leader sends Accept(instance_id, slot_i, value = v) 2b. Acceptors return Accepted(instance_id, slot_i) In Multi-Paxos, Phase 1 is skipped during steady-state Commits in exactly 1 RTT over local network!; Proposer A issues Prepare(10) → Majority promises.

Architectural Invariant: Fix: Randomized backoff election timers (used in Raft).
ARCHITECTURAL FLOW · STEP 2 OF 3

2. Raft 5 Core Safety Invariants

Coordinates core data-plane execution and state transitions through Raft 5 Core Safety Invariants: 1. Election Safety: At most one leader elected per term; 2. Leader Append-Only: Leader never overwrites its own log; 3. Log Matching: Matching (index, term) implies identical logs.

Architectural Invariant: Election Rule: Candidate must have more up-to-date log than voter!.
ARCHITECTURAL FLOW · STEP 3 OF 3

3. Crash Fault-Tolerant (CFT) vs. Byzantine (BFT) & CFT (Raft / Paxos)

Enforces durability, state convergence, and output verification across Crash Fault-Tolerant (CFT) vs. Byzantine (BFT) & CFT (Raft / Paxos): Tolerates: Crash-stop; Nodes: N = 2f + 1; Quorum: f + 1 3 nodes tolerate 1 crash.

Architectural Invariant: Quorum: 2f + 1 4 nodes tolerate 1 traitor.

The Landscape of Consensus Protocols

                                  Consensus Protocols
                                           │
                 ┌─────────────────────────┴─────────────────────────┐
                 ▼                                                   ▼
       Crash Fault-Tolerant (CFT)                       Byzantine Fault-Tolerant (BFT)
      (Nodes crash-stop or crash-recover)              (Nodes can act maliciously / lie)
                 │                                                   │
        Nodes: N = 2f + 1                                   Nodes: N = 3f + 1
       Quorum: Q = f + 1                                   Quorum: Q = 2f + 1
                 │                                                   │
       ┌─────────┴─────────┐                               ┌─────────┴─────────┐
       ▼                   ▼                               ▼                   ▼
  Multi-Paxos            Raft                            PBFT              Tendermint
(Chubby, Spanner)     (etcd, TiKV)                   (Castro-Liskov)      (Cosmos, Web3)

3. The FLP Impossibility Theorem (1985)

In 1985, researchers Fischer, Lynch, and Paterson published their landmark paper:

“Impossibility of Distributed Consensus with One Faulty Process”

The Theorem

In a purely asynchronous distributed system, no deterministic consensus protocol can guarantee both safety and liveness in the presence of even a single unannounced crash failure.

Why FLP Holds:

In an asynchronous network, message delay is unbounded. If Node A does not hear from Node B, it is mathematically impossible to differentiate between two realities:

  1. Node B has crashed.
  2. Node B is running perfectly fine, but the network link is experiencing high latency.

An adversary can delay packets in a pathological cycle, continually flipping nodes between indecisive states forever.

How Real Systems Bypass FLP

Practical protocols (Paxos, Raft) make two engineering relaxations:

  1. Partial Synchrony: They assume that while the network can experience arbitrary delays temporarily, there exists an unknown Global Stabilization Time (GST) after which message transmission delays are bounded by .
  2. Randomization: Raft uses randomized election timeouts () to break deterministic symmetry and prevent dueling candidate livelocks.

4. Single-Decree Paxos & Multi-Paxos

Invented by Turing Award laureate Leslie Lamport, Paxos operates in two distinct phases:

Phase 1: Prepare / Promise (Leader Election / Lease)

  1. 1a. Prepare: A Proposer selects a unique, monotonically increasing proposal number and broadcasts to a majority of Acceptors.
  2. 1b. Promise: If on an Acceptor:
    • Acceptor sets .
    • Acceptor replies , pledging never to accept any future proposal numbered less than .

Phase 2: Accept / Accepted (Value Commitment)

  1. 2a. Accept: If the Proposer receives promises from a majority of Acceptors ():
    • It sets value (the value from the promise with the highest proposal number), or its own desired value if no acceptor reported a prior value.
    • Proposer broadcasts to the Acceptors.
  2. 2b. Accepted: If , the Acceptor accepts and broadcasts to the Proposer and Learners.

Multi-Paxos Optimization

Single-Decree Paxos requires 2 full Round Trips (RTTs) for every single log slot. Multi-Paxos amortizes Phase 1 across thousands of log entries: once a Proposer wins Phase 1, it acquires a stable leader lease and executes only Phase 2 (Accept/Accepted) for subsequent log entries, committing updates in 1 RTT.

Raft’s Five Core Safety Invariants & Log Matching Proof

Ongaro and Ousterhout (2014) decomposed Multi-Paxos into Raft, structuring replicated log consensus around five formal safety invariants that hold at all times across arbitrary crash-recoveries and network partitions:

  1. Election Safety: At most one leader can be elected in a given term . (Guaranteed because each server votes at most once per term and winning requires a strict majority ).
  2. Leader Append-Only: A leader never overwrites or deletes entries in its own log; it only appends new entries.
  3. Log Matching: If two logs contain an entry with the same index and term , then the logs store the identical command at index and are byte-for-byte identical in all preceding entries .
    • Proof by Induction: By Election Safety, at most one leader creates entries in term , and it assigns each index at most once (base match at index ). On every AppendEntries RPC for index , the leader transmits (prevLogIndex = k - 1, prevLogTerm), and the follower rejects the append unless its local entry at matches prevLogTerm. By induction from down to index , matching at forces the entire prefix to match identically.
  4. Leader Completeness: If a log entry is committed in term , that entry will be present in the logs of the leaders for all higher-numbered terms . (Enforced via the RequestVote up-to-date log comparison: lexicographically comparing (lastLogTerm, lastLogIndex) across intersecting commit and election majorities).
  5. State Machine Safety: If a server has applied a log entry at index to its state machine, no other server will ever apply a different log entry for index .

5. Mathematical Proof: CFT () vs. BFT ()

5.1 Crash Fault-Tolerance (CFT) Quorum Derivation

Suppose a cluster of nodes must tolerate up to crash-stop failures:

  • Liveness Bound: When nodes crash, the remaining healthy nodes must still form a valid quorum and make progress:
  • Safety Bound (Quorum Intersection): To prevent two partitioned sub-clusters from electing competing leaders or committing conflicting log entries, any two quorums of size must overlap by at least 1 node:

Combining both bounds ():

Setting minimal gives :

  • To tolerate crash: ().
  • To tolerate crashes: ().

5.2 Byzantine Fault-Tolerance (BFT) Quorum Derivation

In an adversarial Byzantine network, up to nodes are malicious traitors that can either remain completely silent (simulating a crash) or equivocate (send conflicting signed vote to Quorum and vote to Quorum ):

  • Liveness Bound: Because up to Byzantine nodes may refuse to respond at all, the system cannot wait for more than replies:
  • Safety Bound (Honest Witness in Every Quorum Intersection): Any two quorums and of size intersect in at least nodes. Because up to nodes inside that intersection could be equivocating traitors, the intersection must contain at least nodes so that at least honest, non-faulty witness refuses to certify conflicting decisions:

Substituting :

Equivalently, among received responses, up to may be forged lies, leaving guaranteed honest responses that must strictly outvote traitors (). The minimal Byzantine quorum size for is:

  • To tolerate traitor: ().
  • To tolerate traitors: ().

6. Production TypeScript Simulation: Single-Decree Paxos Engine

Below is an executable simulation demonstrating Paxos Phase 1 and Phase 2 consensus, showing how acceptors force competing proposers to converge safely:

export interface PromiseResponse {
  accepted: boolean;
  highestAcceptedN: number | null;
  highestAcceptedV: string | null;
}

export class PaxosAcceptor {
  public highestPromisedN = 0;
  public highestAcceptedN: number | null = null;
  public highestAcceptedV: string | null = null;

  constructor(public readonly id: string) {}

  // Phase 1b: Evaluate Prepare
  public onPrepare(n: number): PromiseResponse {
    if (n > this.highestPromisedN) {
      this.highestPromisedN = n;
      return {
        accepted: true,
        highestAcceptedN: this.highestAcceptedN,
        highestAcceptedV: this.highestAcceptedV,
      };
    }
    return { accepted: false, highestAcceptedN: null, highestAcceptedV: null };
  }

  // Phase 2b: Evaluate Accept
  public onAccept(n: number, v: string): boolean {
    if (n >= this.highestPromisedN) {
      this.highestPromisedN = n;
      this.highestAcceptedN = n;
      this.highestAcceptedV = v;
      return true;
    }
    return false;
  }
}

export class PaxosProposer {
  constructor(
    public readonly id: string,
    public readonly acceptors: PaxosAcceptor[]
  ) {}

  public async propose(proposalNumber: number, initialValue: string): Promise<string> {
    const majority = Math.floor(this.acceptors.length / 2) + 1;

    // --- PHASE 1: PREPARE ---
    let promiseCount = 0;
    let highestPriorN: number | null = null;
    let chosenValue = initialValue;

    for (const acceptor of this.acceptors) {
      const resp = acceptor.onPrepare(proposalNumber);
      if (resp.accepted) {
        promiseCount++;
        if (resp.highestAcceptedN !== null) {
          if (highestPriorN === null || resp.highestAcceptedN > highestPriorN) {
            highestPriorN = resp.highestAcceptedN;
            chosenValue = resp.highestAcceptedV!; // Must adopt existing value!
          }
        }
      }
    }

    if (promiseCount < majority) {
      throw new Error(`PrepareRejected: Only received ${promiseCount}/${majority} promises`);
    }

    // --- PHASE 2: ACCEPT ---
    let acceptedCount = 0;
    for (const acceptor of this.acceptors) {
      if (acceptor.onAccept(proposalNumber, chosenValue)) {
        acceptedCount++;
      }
    }

    if (acceptedCount < majority) {
      throw new Error(`AcceptRejected: Only received ${acceptedCount}/${majority} accepts`);
    }

    return chosenValue; // Consensus successfully reached
  }
}

async function runDemo(): Promise<void> {
  console.log("=== Consensus Algorithms: Paxos Proposer & Acceptor Simulation ===");
  const acceptors = [new PaxosAcceptor("acc-1"), new PaxosAcceptor("acc-2"), new PaxosAcceptor("acc-3")];
  const proposerA = new PaxosProposer("prop-A", acceptors);
  const proposerB = new PaxosProposer("prop-B", acceptors);
  console.log("Initialized Multi-Acceptor Paxos Quorum (N=3, Majority=2)");

  const committed1 = await proposerA.propose(10, "CommitTransaction#101");
  console.log(`[Round n=10] Proposer A committed value: '${committed1}'`);

  // Competing Proposer B attempts to propose a different value with higher n=20
  const committed2 = await proposerB.propose(20, "ConflictingTransaction#202");
  console.log(`[Round n=20] Competing Proposer B forced to adopt chosen value: '${committed2}'`);
}

runDemo();

Simulation Execution Output & State Transitions

=== Consensus Algorithms: Paxos Proposer & Acceptor Simulation ===
Initialized Multi-Acceptor Paxos Quorum (N=3, Majority=2)
[Round n=10] Proposer A committed value: 'CommitTransaction#101'
[Round n=20] Competing Proposer B forced to adopt chosen value: 'CommitTransaction#101'

7. Open-Source Implementation Analysis

A. etcd & TiKV: Raft Engine

  • etcd/raft (v3.5+): Written in Go, etcd isolates the deterministic Raft state machine from network and disk I/O. Applications pass incoming network messages into RawNode.Step() and consume ready state transitions (Ready.Entries, Ready.HardState, Ready.Messages), persisting Entries and HardState via fsync to the local WAL before dispatching outbound network frames.
  • TiKV Multi-Raft (v7.x+): Partitioned distributed KV engines split keyspaces into Regions, each governed by an independent Raft group, combining ReadIndex heartbeats and LeaseRead bounded clock leases to serve linearizable reads without writing read-only entries to disk.

B. Google Spanner: Multi-Paxos with TrueTime

  • Spanner partitions tables into Directories/Splits, and each split is replicated across geographic zones via long-lived Multi-Paxos groups with 10-second disjoint leader leases.
  • Spanner integrates Multi-Paxos with TrueTime (GPS and rubidium atomic clock hardware API returning ). Leaders overlap Paxos replication with a commit-wait window () to guarantee global external consistency across continents.

8. Failure Modes & Operational War Stories

War Story: The Dueling Proposer Livelock

  • Incident: An early Multi-Paxos implementation ran two active proposers without a stable leader lease or randomized backoff.
  • Failure: Proposer 1 issued Prepare(10). Before it could send Accept, Proposer 2 sent Prepare(20), causing acceptors to update their promise to . When Proposer 1 sent Accept(10), acceptors rejected it. Proposer 1 immediately retried with Prepare(30), preempting Proposer 2’s Accept(20).
  • Outcome: Both proposers continually outbid each other in a frantic loop, generating millions of network packets per second while zero log entries were ever committed (Livelock—demonstrating FLP in action).
  • Mitigation: Introduced stable leader leases coupled with Pre-Vote / randomized exponential backoff timers () before re-proposing, ensuring one proposer cleanly completes Phase 2 before peers challenge leadership.

9. Design Alternatives, Trade-offs & Operational Decision Guide

Protocol / ArchitectureFault Model & Min NodesSteady-State LatencyMessage ComplexityWhen to Use & Primary Trade-off
Two-Phase Commit (2PC)Coordinator fail-stop blocks ( shards) + 3 fsyncsAtomic cross-shard commits on top of consensus groups; never standalone for HA coordination because coordinator crash freezes locks.
Raft (etcd, TiKV)CFT: () (from Leader)Kubernetes control planes, configuration metadata, and Multi-Raft SQL engines; requires strict in-order log prefix before election.
Multi-Paxos (Google Spanner)CFT: () (amortized lease)Geo-distributed databases needing out-of-order pipelined slot commits and flexible gap compaction during leader failover.
ZAB (Apache ZooKeeper)CFT: () (FIFO TCP broadcast)Primary-backup atomic broadcast where strict causal FIFO ordering of all state updates from a single epoch primary is mandatory.
PBFT / TendermintBFT: () (3 phases) (or w/ threshold sigs)Untrusted consortium or public validator networks tolerating malicious equivocation; high quadratic message overhead limits validator count to nodes.

10. Hands-on Operational Drills

  1. Simulate Split-Vote Value Adoption in Paxos: Modify the TypeScript simulation so Acceptors acc-1 and acc-2 accept "A" at while acc-3 misses Phase 2. Next, partition acc-1 offline and have Proposer B run Phase 1 with against {acc-2, acc-3}. Verify that acc-2’s promise forces Proposer B to adopt "A".
  2. Verify Split Quorums in an Even-Sized 4-Node CFT Cluster: Model a 4-node CFT cluster (). Show why tolerates only crash (worse availability than because 4 hardware nodes can fail instead of 3, and a partition halts both sides).
View Reference Architecture & Worked Solutions (CORE-M21-001)

1. Worked Solution — Drill 1: Simulate Split-Vote Value Adoption in Paxos

  • State Trace Across Quorum Intersection:
    1. Round (Proposer A):
      • Phase 1 Prepare(10) reaches all 3 acceptors (acc-1, acc-2, acc-3), which set highestPromisedN = 10 and return (null, null).
      • Phase 2 Accept(10, "A") reaches acc-1 and acc-2 (, a majority of ), while packet loss drops the message to acc-3:
        • acc-1: promised=10, accepted=(10, "A")
        • acc-2: promised=10, accepted=(10, "A")
        • acc-3: promised=10, accepted=(null, null)
    2. Node Crash & Round (Proposer B attempting to propose "B"):
      • acc-1 crashes offline. Proposer B contacts the surviving majority quorum with Prepare(20).
      • acc-3 replies Promise(20, null, null).
      • Intersection Witness (acc-2 ): acc-2 replies Promise(20, highestAcceptedN=10, highestAcceptedV="A").
      • By Lamport’s Phase 2a rule, Proposer B inspects all promises in , finds the non-null accepted value with the highest proposal number (10 -> "A"), discards its own initial value "B", and broadcasts Accept(20, "A") to {acc-2, acc-3}, preserving safety across leader failover!

2. Worked Solution — Drill 2: Verify Split Quorums in an Even-Sized 4-Node CFT Cluster ( vs. )

  • Strict Majority Math:
    • To prevent two disjoint partitions from committing conflicting values, a CFT quorum must be a strict majority:
    • For : , so max tolerable crashes .
    • For : , so max tolerable crashes .
  • Why Has Strictly Worse Availability Than :
    1. Higher Hardware Failure Probability: If each physical server has independent unavailability probability , the probability of simultaneous node failures (cluster outage) is:
      • (nearly higher outage risk!).
    2. Symmetric Network Partition Freeze: A clean cross-rack switch split divides into two sub-clusters of nodes each. Because neither side holds nodes, both sides halt completely, whereas in a two-way split always leaves a -node majority partition operational.

11. Summary & Architectural Decision Checklist

  • FLP Impossibility mandates choosing between blocking safety and probabilistic/partially synchronous liveness: always ensure consensus nodes run with randomized election timeouts () so transient network jitter does not trigger dueling elections.
  • Deploy odd cluster sizes ( or ) for Crash Fault-Tolerant coordination (etcd, Apache ZooKeeper, TiKV): requires and survives only failure while increasing write quorum latency and failure surface.
  • Reserve BFT () strictly for multi-organization or untrusted trust domains; inside a single private cloud VPC protected by SPIFFE mTLS, CFT protocols deliver higher throughput without all-to-all signature verification.

12. Authoritative References & Version Context

INTERACTIVE ISLAND

Raft Consensus Algorithm Live Simulator

Step through leader election, term monotonicity, log replication quorums, and network partition split-brain resilience.

RAFT CLUSTER EVENT & COMMIT LOG
MASTERY GATE · STEP 33 OF 94

Knowledge check & lesson completion

Score 4 / 4 (100%) on this check to mark this lesson Completed ✓, advance your position on the Master Spine, and add these concepts to your Spaced-Repetition Review Deck.

1. What does the Fischer-Lynch-Paterson (FLP) Impossibility Theorem formally prove about distributed systems?
2. In Paxos Phase 1, what does an Acceptor promise to a Proposer upon receiving a Prepare(n) message with proposal number n?
3. Why do Crash Fault-Tolerant (CFT) protocols require N = 2f + 1 nodes to survive f crashes, whereas Byzantine Fault-Tolerant (BFT) protocols require N = 3f + 1 nodes to survive f Byzantine failures?
4. In the Raft consensus algorithm, how does the Leader Completeness property guarantee that committed entries are never lost during leader elections?
PERSONAL NOTEBOOK

Chapter Notes

Personal engineering insights and observations saved to this browser.

0 notes
Tip: You can also highlight text in the article above to quote directly.

Loading personal notes...