Paxos Consensus Algorithm: Consensus and Replication
Introduction and Core Concepts
The paxos consensus algorithm is a foundational protocol for achieving fault-tolerant consensus in distributed systems where processes may crash or messages may be delayed and lost. First described by Leslie Lamport in 1989 and later published as "The Part-Time Parliament" in 1998, Paxos solves the fundamental problem of getting a group of unreliable computers to agree on a single value, even when some of them fail. This problem is central to building reliable distributed databases, configuration management services, and replicated state machines.
At its core, paxos consensus algorithm addresses the consensus problem: a set of nodes must agree on one result from among possibly many proposals. The challenge is not simply voting, but ensuring safety and liveness in an asynchronous network. Safety means that only a single value can be chosen and that chosen value is never forgotten, while liveness means that some proposed value will eventually be chosen if enough nodes are healthy and can communicate. Paxos guarantees safety at all times, even during network partitions, and guarantees liveness when the network becomes sufficiently stable for a long enough period.
Understanding Paxos is essential because it provides the theoretical underpinning for virtually all strongly consistent replication systems. Before Paxos, building systems that remained consistent despite failures relied on ad-hoc and often incorrect protocols. The algorithm introduced a rigorous, provably correct approach based on majority quorums and monotonic proposal numbers. While often considered difficult to understand and implement correctly, its core idea is elegant: proposers compete to get their value accepted by a majority of acceptors, and once a majority has accepted a value, that value becomes immutable and will be learned by all participants.
Why Consensus Matters
In a single-machine system, consensus is trivial — there is only one decision maker. In a distributed system with 3, 5, or 7 replicas, each replica may receive different client requests at different times and see different failure patterns. Without consensus, replicas diverge, leading to data loss, split-brain scenarios, and inconsistent reads. The paxos consensus algorithm provides a way to linearize operations across replicas so that the entire cluster behaves like a single, highly available and fault-tolerant machine.
Key terminology defines the roles in Paxos. Proposers propose values, Acceptors vote on proposals, and Learners learn the chosen value. In practice, a single physical node often plays all three roles. A quorum is typically a majority of acceptors (e.g., 3 out of 5). The requirement for overlapping majorities ensures that any two decisions must have at least one acceptor in common, which prevents contradictory choices.
How It Works
Paxos operates in two main phases for each consensus instance, often called a decree or slot. Phase 1 is the Prepare/Promise phase, and Phase 2 is the Accept/Accepted phase. A proposer that wants to get a value chosen first picks a unique, monotonically increasing proposal number 'n' and sends a Prepare(n) message to a majority of acceptors. This proposal number must be globally unique, usually constructed as (counter, proposer_id).
When an acceptor receives a Prepare(n) message, it compares 'n' to the highest proposal number it has ever seen. If 'n' is greater than any previous number, the acceptor promises never to accept any proposal numbered less than 'n' and replies with a Promise message. This promise includes the highest-numbered proposal it has already accepted, if any, along with its value. If 'n' is not greater, the acceptor can ignore or reject the prepare. This rule is crucial for ensuring that old proposers cannot overwrite a value that may have already been chosen.
If the proposer receives Promise responses from a majority of acceptors, it enters Phase 2. It must now choose a value to propose. The rule is strict: if any acceptor in the majority reported a previously accepted value, the proposer must propose the value associated with the highest proposal number among those reports. If none reported a value, the proposer is free to propose its own original value. It then sends an Accept(n, v) message to the acceptors. An acceptor accepts this proposal unless it has already promised to a higher-numbered proposal. If a majority accepts, the value 'v' is considered chosen. Learners can then be notified via Accepted messages.
Quorums and Safety Guarantee
The safety of paxos consensus algorithm rests on the quorum intersection property. Because any two majorities overlap, if a value 'v' is chosen at proposal number 'n', then any future proposer with a higher number 'n' > 'n' will contact at least one acceptor that has accepted 'v'. That proposer will therefore be forced to propose 'v' again, ensuring the choice never changes. This induction guarantees that once a value is chosen, all later chosen values will be identical.
Liveness is not guaranteed in the face of dueling proposers. Two proposers can continuously outbid each other with higher proposal numbers, preventing anyone from completing Phase 2. This is known as livelock. In practice, this is solved by electing a distinguished proposer — a leader — who is the only one allowed to propose. This optimization leads directly to Multi-Paxos, which is used in production systems.
| Message Type | Sender → Receiver | Purpose | Payload |
|---|---|---|---|
| Prepare | Proposer → Acceptors | Discover prior accepted values and block older proposals | Proposal number n |
| Promise | Acceptor → Proposer | Acknowledge prepare and return prior state | n, highest accepted (n', v') or null |
| Accept | Proposer → Acceptors | Request to accept a specific value | Proposal number n, value v |
| Accepted | Acceptor → Learners | Notify that value was accepted | n, v |
| Nack / Reject | Acceptor → Proposer | Inform proposer its number is too low | Highest seen n |
Implementation
Implementing paxos consensus algorithm correctly requires careful handling of persistent state, proposal numbering, and quorum logic. Acceptors must persist their highest promised proposal number and highest accepted proposal before replying, otherwise a crash and recovery could violate safety. Below are simplified Python implementations that illustrate the core logic without networking boilerplate.
The first example models the Acceptor's persistent state and its two critical handlers. Note how the acceptor only updates its promise if the incoming number is strictly greater, and only accepts if it has not promised to a higher number. In a real system, `max_promised` and `accepted` would be written to stable storage like a write-ahead log.
class Acceptor:
def __init__(self, acceptor_id):
self.id = acceptor_id
# Persistent state - must survive crashes
self.max_promised = -1 # highest Prepare n seen
self.accepted_n = -1 # highest Accept n accepted
self.accepted_value = None
def on_prepare(self, n):
"""Handle Prepare(n) from proposer."""
# Promise only if n is strictly greater than any prior promise
if n > self.max_promised:
self.max_promised = n
# Must persist max_promised before replying
self.persist()
# Return promise with last accepted value (if any)
return ("promise", self.max_promised, self.accepted_n, self.accepted_value)
else:
# Nack with highest promised to help proposer retry
return ("nack", self.max_promised)
def on_accept(self, n, value):
"""Handle Accept(n, value) from proposer."""
# Accept only if we have not promised to a higher number
if n >= self.max_promised:
self.max_promised = n
self.accepted_n = n
self.accepted_value = value
self.persist()
return ("accepted", n, value)
else:
return ("nack", self.max_promised)
def persist(self):
# In production: fsync to disk / WAL
pass
The proposer logic implements both phases and the critical value-selection rule. It must generate globally unique and increasing proposal numbers and wait for a quorum of promises before proceeding. The most subtle part is choosing the value for the Accept phase — it cannot simply use its own value if any acceptor has already accepted something.
class Proposer:
def __init__(self, proposer_id, acceptors):
self.id = proposer_id
self.acceptors = acceptors
self.proposal_counter = 0
def generate_proposal_number(self):
# Unique across proposers: (counter, proposer_id)
# Using tuple comparison ensures total ordering
self.proposal_counter += 1
return (self.proposal_counter, self.id)
def propose(self, my_value):
n = self.generate_proposal_number()
promises = []
# --- Phase 1: Prepare ---
for acceptor in self.acceptors:
resp = acceptor.on_prepare(n)
if resp[0] == "promise":
promises.append(resp)
quorum = len(self.acceptors) // 2 + 1
if len(promises) < quorum:
# Failed to get quorum, backoff and retry with higher n
return None
# --- Critical Paxos Rule: Choose value ---
# If any promise contained an accepted value, use the one with highest n
highest_accepted = None
chosen_value = my_value
for _, _, acc_n, acc_val in promises:
if acc_val is not None:
if highest_accepted is None or acc_n > highest_accepted[0]:
highest_accepted = (acc_n, acc_val)
if highest_accepted is not None:
chosen_value = highest_accepted[1]
# --- Phase 2: Accept ---
accepts = 0
for acceptor in self.acceptors:
resp = acceptor.on_accept(n, chosen_value)
if resp[0] == "accepted":
accepts += 1
if accepts >= quorum:
return chosen_value # Value is chosen!
else:
return None
For a complete replicated log, we need Multi-Paxos where many consensus instances are chained together. The naive approach of running full two-phase Paxos for every log entry is expensive. Multi-Paxos optimizes by running Phase 1 only once for a leader to establish its leadership for all future slots.
class MultiPaxosLeader:
def __init__(self, leader_id, acceptors):
self.leader_id = leader_id
self.acceptors = acceptors
self.leader_proposal_n = (0, leader_id)
self.is_leader = False
self.next_slot = 0
def become_leader(self):
"""Run Phase 1 once for all future slots."""
self.leader_proposal_n = (self.leader_proposal_n[0] + 1, self.leader_id)
n = self.leader_proposal_n
promises = [a.on_prepare(n) for a in self.acceptors]
quorum = len(self.acceptors) // 2 + 1
# Count only promises, not nacks
promise_count = sum(1 for r in promises if r[0] == "promise")
if promise_count >= quorum:
self.is_leader = True
return True
return False
def propose_value_for_slot(self, slot, value):
"""After becoming leader, only Phase 2 is needed per slot."""
if not self.is_leader:
raise Exception("Not leader - must run become_leader first")
# In steady state, skip Prepare and directly send Accept
# Acceptors will accept because leader's n is still the highest promised
n = self.leader_proposal_n
accepts = 0
for acceptor in self.acceptors:
# Simplified: acceptor tracks state per slot
if hasattr(acceptor, 'on_accept_for_slot'):
resp = acceptor.on_accept_for_slot(slot, n, value)
else:
resp = acceptor.on_accept(n, value)
if resp[0] == "accepted":
accepts += 1
quorum = len(self.acceptors) // 2 + 1
return accepts >= quorum
Fault handling and timeouts are essential for liveness. A proposer that does not receive a quorum within a timeout should increment its proposal number and retry after a randomized exponential backoff to avoid dueling proposers. Learners should also periodically poll acceptors or listen for Accepted messages to learn chosen values even if they missed the initial broadcast.
import random
import time
def paxos_propose_with_retry(proposer, value, max_retries=5):
"""Robust propose with backoff and Nack handling."""
for attempt in range(max_retries):
result = proposer.propose(value)
if result is not None:
# Value was chosen (may not be our original value!)
return result
# Learn highest promised n from nacks to jump ahead
# In real RPC, collect nack_n from acceptors
backoff = (2 ** attempt) * 0.01 + random.uniform(0, 0.01)
time.sleep(backoff)
# Force proposer to generate higher n next time
proposer.proposal_counter += 1
raise TimeoutError("Failed to reach consensus after retries")
# Example of quorum calculation helper
def has_quorum(responses, total_acceptors):
"""Check if responses constitute a majority."""
needed = total_acceptors // 2 + 1
successes = sum(1 for r in responses if r[0] in ("promise", "accepted"))
return successes >= needed
Step-by-Step Example
Consider a cluster of 5 acceptors (A1-A5) and two proposers (P1 and P2) competing to choose a value. P1 wants to propose value "X" and P2 wants to propose "Y". This example demonstrates how Paxos guarantees that only one value is ultimately chosen, even with concurrent proposals and a failure.
Initially, no acceptor has accepted any value. P1 starts first with proposal number n=1. It sends Prepare(1) to a majority, say A1, A2, A3. All three have max_promised = -1, so 1 > -1, they all promise and return (null) since they have accepted nothing. P1 now has a quorum of promises and, seeing no prior accepted values, is free to propose its own value "X". It sends Accept(1, "X") to A1, A2, A3. Assume A1 and A2 accept, but the message to A3 is delayed due to network partition. At this point, "X" has been accepted by 2 acceptors — not yet a majority of 5, so not yet chosen.
Concurrently, P2 starts with a higher proposal number n=2 (perhaps because it has a higher proposer ID). It sends Prepare(2) to a different majority: A3, A4, A5. A4 and A5 have never been contacted, so they promise for n=2. A3 had promised to n=1 earlier, but 2 > 1, so it also promises to P2. Crucially, A3's promise to P2 includes its accepted state? No, A3 never accepted "X" because the Accept was delayed. So A3 returns null as well. P2 now has promises from A3, A4, A5. Since none reported an accepted value, P2 proposes its own value "Y" with Accept(2, "Y") to A3, A4, A5. All three accept "Y" because 2 >= their max_promised. Now "Y" has 3 accepts — a majority — so "Y" is chosen.
What happens to P1's delayed Accept(1, "X") to A3? When it finally arrives, A3 has already promised to n=2, and the rule requires n >= max_promised to accept. Since 1 < 2, A3 rejects it. P1 will eventually timeout waiting for a quorum of Accepted messages, learn that its proposal failed (perhaps by seeing a Nack with n=2), and retry with a higher number, say n=3. When P1 retries with Prepare(3) to A1, A2, A3, it will get promises: A1 returns (1, "X"), A2 returns (1, "X"), A3 returns (2, "Y"). The highest accepted is (2, "Y"), so P1 is now forced to propose "Y" instead of "X" with Accept(3, "Y"). This ensures the previously chosen value "Y" is not overwritten. The system converges on "Y" as the single chosen value.
Key Takeaway from the Trace
This trace illustrates two vital properties. First, a higher proposal number can preempt a lower one, but it cannot arbitrarily choose a new value if a value has already been accepted by someone in its quorum. Second, once a value is chosen (like "Y" with 3 accepts), any future proposer that obtains a majority will necessarily intersect that majority and be forced to adopt "Y". The delayed message from P1 does not cause inconsistency because the promise mechanism blocks stale accepts.
Complexity Analysis
The performance of paxos consensus algorithm is typically measured in message complexity, latency (number of round trips), and fault tolerance rather than traditional CPU time. However, we can analyze it in Big O terms for a cluster of N acceptors where a quorum Q = floor(N/2) + 1.
In the classic two-phase Paxos, Phase 1 requires 2*Q messages (Q Prepares and Q Promises) and Phase 2 requires 2*Q messages (Q Accepts and Q Accepted). So the total message complexity per consensus instance is O(N). Latency is 2 round trips (4 message delays) in the best case when there is no contention. In steady-state Multi-Paxos with a stable leader, Phase 1 is skipped, reducing latency to 1 round trip (Proposer to Acceptors and back) and message complexity to O(N) per command but with half the delays.
Time complexity for the proposer and acceptor logic itself is O(N) to count quorums and O(1) per acceptor. The dominant cost is network I/O, not computation. Space complexity is O(1) per consensus instance per acceptor (storing max_promised and accepted value) and O(S) for S slots in a replicated log. Learners need O(S) to store the chosen sequence.
Worst-case complexity degrades due to contention. Dueling proposers can cause unbounded retries, leading to O(k*N) messages for k retries and no guarantee of termination without a leader election mechanism. This is why practical systems bound retries with exponential backoff and leader leases. Best case is with a single stable leader and no failures, achieving optimal throughput.
| Operation / Phase | Best Case | Average Case | Worst Case | Space per Node |
|---|---|---|---|---|
| Prepare Phase (messages) | O(N) | O(N) | O(k*N) with k retries | O(1) |
| Accept Phase (messages) | O(N) | O(N) | O(k*N) with contention | O(1) |
| Single Decree Latency | 4 delays (2 RTT) | 4 delays | Unbounded (livelock) | O(1) |
| Multi-Paxos Steady State | 2 delays (1 RTT) | 2 delays | 4 delays if leader fails | O(1) per slot |
| Replicated Log of S slots | O(S*N) messages | O(S*N) | O(k*S*N) | O(S) |
| Quorum Check Computation | O(N) | O(N) | O(N) | O(1) |
Variations and Optimizations
Classic Paxos, while correct, is rarely used verbatim in production due to its liveness issues and message overhead. Several well-known variations optimize for different scenarios while preserving the core safety proof.
Multi-Paxos is the most important optimization. It observes that if the same leader runs many consensus instances (log slots), it can run Phase 1 once for all slots. After the leader is established with a high proposal number, it can repeatedly run only Phase 2 for each new client command. This reduces latency from 2 RTT to 1 RTT and is the basis for nearly all Paxos-based log replication systems. The leader also batches multiple client requests and pipelines Phase 2 messages to increase throughput.
Fast Paxos reduces latency further to 1 RTT even without a stable leader by allowing proposers to send Accept directly, bypassing Prepare, when there is no collision. It requires larger quorums (typically 3/4 of acceptors) to handle collisions safely and is useful in low-contention, low-latency environments. Generalized Paxos extends this by allowing commutative commands to be reordered, so if two proposers propose different but commutative operations (e.g., writes to different keys), both can be chosen without conflict.
Other Notable Variants
Cheap Paxos reduces cost by using fewer acceptors for the second phase. It uses main acceptors plus auxiliary witnesses that only participate in Phase 1, allowing deployment with fewer expensive nodes. Egalitarian Paxos (EPaxos) eliminates the leader bottleneck entirely by letting any replica propose commands and using dependency graphs to order interfering commands, achieving better load balancing and lower latency in geo-distributed settings.
Disk Paxos optimizes for systems where acceptors communicate via shared disks rather than messages, useful in SAN environments. Stoppable Paxos allows reconfiguration of the acceptor set without downtime by running a separate Paxos instance to agree on the new membership. This is critical for long-lived services that need to add or remove nodes.
Production optimizations also include batching, pipelining, and leader leases. Batching groups many client operations into a single Paxos instance to amortize message cost. Pipelining allows the leader to have many outstanding Accept phases concurrently, limited only by flow control. Leader leases use time-based leases to allow the leader to serve reads locally without running Paxos, dramatically improving read throughput.
Real-World Applications
The paxos consensus algorithm and its variants power some of the world's most critical infrastructure. While Raft has become popular for new systems due to its understandability, Paxos remains deeply embedded in large-scale production systems where its proven correctness and flexibility are valued.
Google's Chubby lock service, described in a seminal 2006 paper, uses Paxos to replicate a lock database across 5 nodes. Chubby provides coarse-grained locking and reliable storage for configuration, and it in turn is used by Bigtable, GFS, and other Google systems to elect masters and store metadata. Google Spanner, the globally distributed database, uses Paxos (specifically Multi-Paxos with leader leases) to replicate data across zones and achieve external consistency with TrueTime.
Amazon's DynamoDB and other internal services use Paxos-based replication for strongly consistent coordination. Microsoft's Azure Storage and Cosmos DB have historically used Paxos variants for partition replication. Apache ZooKeeper, though often associated with Zab (Zookeeper Atomic Broadcast), uses a protocol very similar to Paxos for its atomic broadcast and leader election.
Other notable uses include Apache Cassandra's lightweight transactions which use Paxos for linearizable compare-and-set operations, Ceph's monitors for cluster map consistency, and etcd's predecessor. Even blockchain and distributed ledger systems borrow quorum and promise concepts from Paxos for Byzantine-tolerant variants.
| System / Product | Paxos Variant Used | Purpose | Scale |
|---|---|---|---|
| Google Chubby | Multi-Paxos | Distributed locks & configuration | 5 replicas per cell, thousands of cells |
| Google Spanner | Multi-Paxos + Leader Leases | Globally replicated database shards | Millions of Paxos groups worldwide |
| Apache Cassandra LWT | Classic Paxos (4 RTT) | Linearizable CAS operations | Per-partition quorum |
| Ceph Monitors | Paxos | Cluster map & metadata consistency | 3-5 monitors per cluster |
| Azure Storage | Paxos-like replication | Partition replication & failover | 3 replicas per partition |
Comparison with Alternatives
Paxos is not the only consensus algorithm. Choosing between Paxos and its alternatives involves trade-offs in understandability, performance, fault model, and operational complexity. The most common comparison is with Raft, which was explicitly designed to be more understandable than Paxos while providing the same fault-tolerance guarantees.
Raft achieves this by decomposing consensus into leader election, log replication, and safety, and by restricting the protocol so that only a leader can propose values and logs can only be appended. This makes Raft easier to implement and reason about, but it is slightly less flexible than Paxos, which allows any proposer to compete and permits more optimizations like Egalitarian Paxos. In terms of performance, both have the same steady-state complexity of O(N) messages and 1 RTT with a stable leader.
Zab (Zookeeper Atomic Broadcast) is similar to Multi-Paxos but adds a synchronization phase for leader activation and is optimized for ZooKeeper's primary-backup replication model. Viewstamped Replication (VR) is another primary-backup protocol that predates Paxos and shares many ideas but uses a different reconfiguration and view-change mechanism. For Byzantine faults where nodes may act maliciously, neither Paxos nor Raft is sufficient; PBFT (Practical Byzantine Fault Tolerance) is required, at the cost of higher message complexity O(N^2) and requiring 3f+1 nodes to tolerate f Byzantine failures instead of 2f+1 for crash faults.
| Feature | Paxos / Multi-Paxos | Raft | Zab | PBFT |
|---|---|---|---|---|
| Fault Model | Crash-stop / omission | Crash-stop / omission | Crash-stop / omission | Byzantine (malicious) |
| Quorum Size | Majority (f+1 of 2f+1) | Majority (f+1 of 2f+1) | Majority | 2f+1 of 3f+1 |
| Steady-State Latency | 1 RTT (Multi-Paxos) | 1 RTT | 1 RTT | 3 RTT |
| Message Complexity | O(N) | O(N) | O(N) | O(N^2) |
| Leader Required? | No (but needed for liveness) | Yes, strong leader | Yes, primary | Yes, primary with view change |
| Understandability | Low - subtle, hard to implement | High - designed for clarity | Medium | Low - very complex |
| Log Constraint | Any value can be proposed | Leader append-only | Primary append-only | Total order via 3-phase commit |
| Reconfiguration | Complex (Stoppable Paxos) | Joint consensus | Built-in | View change protocol |
Common Mistakes and Best Practices
Implementing paxos consensus algorithm correctly is notoriously error-prone. Even experienced engineers make subtle mistakes that violate safety. The most common pitfall is failing to persist state before sending a response. An acceptor must write max_promised and accepted_n/value to stable storage and fsync before replying with Promise or Accepted. If it replies first and then crashes before persisting, it may promise the same number to two different proposers after recovery, breaking the quorum intersection guarantee.
Another frequent mistake is using non-unique or non-monotonic proposal numbers. If two proposers generate the same number, acceptors cannot distinguish them, and liveness suffers. Proposal numbers must be totally ordered and unique across all proposers, typically by combining a local counter with a unique node ID. Reusing numbers after a restart without persisting the counter can also cause correctness issues.
Forgetting the value-selection rule is a critical safety bug. A proposer that receives promises containing previously accepted values must propose the value with the highest accepted number, not its own value. Ignoring this and proposing its own value can overwrite a value that was already chosen by a previous majority, violating the fundamental Paxos invariant. This bug often appears when developers optimize the "no prior value" fast path incorrectly.
Best Practices for Production
Always implement a distinguished leader with leases to avoid livelock. Use randomized timeouts for leader election and exponential backoff for retries. Implement comprehensive testing with fault injection, including message loss, duplication, reordering, crashes, and partitions. Tools like Jepsen and TLA+ model checking are invaluable for validating correctness.
Batch and pipeline aggressively for performance, but ensure batching does not violate the atomicity of individual slots. Use checksums and epoch numbers to detect stale messages from old leaders. Monitor quorum health and alert when you cannot reach a majority. Finally, prefer well-tested libraries (e.g., libpaxos, or Raft implementations if Raft is acceptable) over writing Paxos from scratch unless you have a strong reason and formal verification.
- Never skip fsync on acceptor state — memory-only state is unsafe.
- Always use (counter, node_id) tuples for proposal numbers and persist the counter.
- Enforce the "propose highest accepted value" rule exactly as specified.
- Elect a stable leader and use heartbeats/leases for liveness.
- Test with network partitions and crash-recovery scenarios, not just happy paths.
Interview Questions
Interviewers frequently probe understanding of paxos consensus algorithm because it tests knowledge of distributed systems fundamentals, trade-offs, and attention to detail. Expect questions that go beyond definitions to scenarios and proofs.
Q1: Why does Paxos require a majority quorum and what happens if you use a smaller quorum?
Answer: Majority quorums guarantee that any two quorums intersect in at least one acceptor. This intersection is what prevents two different values from being chosen. If you use a smaller quorum, say 2 out of 5, two disjoint quorums could choose different values concurrently (e.g., A1,A2 choose X and A4,A5 choose Y) with no overlapping node to force convergence, violating safety. The general requirement is that quorums must intersect; majority is the smallest quorum that guarantees intersection for crash faults. For flexible quorums, you can use read and write quorums where R + W > N and W > N/2.
Q2: Explain the difference between Classic Paxos and Multi-Paxos and why Multi-Paxos is used in practice.
Answer: Classic Paxos runs both Phase 1 (Prepare/Promise) and Phase 2 (Accept/Accepted) for every single value to be chosen, costing 2 RTT. Multi-Paxos optimizes for a sequence of values (a log) by running Phase 1 once to elect a stable leader with a high proposal number. That leader can then skip Phase 1 for subsequent slots and directly run Phase 2, reducing latency to 1 RTT and halving message count. This is crucial for throughput when replicating a log of thousands of commands per second, as in Spanner or Chubby.
Q3: How does Paxos handle proposer failures and what is the livelock problem?
Answer: If a proposer fails after sending Prepare but before completing Accept, another proposer can take over with a higher proposal number and complete the instance. Safety is preserved because the new proposer will learn any value that might have been chosen. Livelock occurs when two proposers continuously preempt each other: P1 with n=1 is preempted by P2 with n=2, then P1 retries with n=3 and preempts P2, ad infinitum, so no value is ever chosen. The solution is to elect a single distinguished leader (via leader election or leases) so only one proposer is active at a time, guaranteeing progress when the network is stable.
Q4: Compare Paxos and Raft — when would you choose one over the other?
Answer: Both provide crash-fault tolerant consensus with the same guarantees and similar performance (O(N) messages, 1 RTT steady state). Raft is preferred when understandability, auditability, and ease of implementation are priorities — it is explicitly designed to be teachable and has fewer edge cases due to its strong leader and log-matching constraints. Paxos is preferred when you need its flexibility for advanced optimizations like Egalitarian Paxos, Fast Paxos, or Generalized Paxos, or when integrating with legacy Paxos-based systems. In practice, most new projects choose Raft, while large established systems like Spanner and Chubby remain on Paxos variants.
Q5: What persistent state must an acceptor store and why?
Answer: An acceptor must persistently store `max_promised` (the highest Prepare n it has promised) and `accepted_n` and `accepted_value` (the highest Accept it has accepted). This state must be written to stable storage before sending Promise or Accepted. If not persisted, a crash and recovery could cause the acceptor to forget its promise and accept an older proposal that should have been blocked, potentially allowing two different values to be chosen and violating safety. This is the most common implementation bug in Paxos.
Frequently Asked Questions
What is Paxos Consensus Algorithm and how does it work?
Paxos Consensus Algorithm is a fundamental concept in computer science. It involves specific techniques and data organization methods designed to solve computational problems efficiently. Understanding the core mechanics helps developers choose the right approach for their applications.
What is the time complexity of Paxos Consensus Algorithm?
The time complexity of Paxos Consensus Algorithm varies depending on the specific operation and implementation. Common complexities range from O(1) constant time for basic operations to O(n log n) or O(n²) for more complex operations. The article above provides detailed complexity analysis for each operation.
When should I use Paxos Consensus Algorithm in my projects?
Use Paxos Consensus Algorithm when your application requires the specific performance characteristics it provides. Consider factors like data size, operation frequency, memory constraints, and whether the problem domain matches the strengths of Paxos Consensus Algorithm. The real-world applications section above covers common use cases.
What are the advantages and disadvantages of Paxos Consensus Algorithm?
The main advantages of Paxos Consensus Algorithm include efficient performance for its target use cases and well-understood theoretical guarantees. Disadvantages may include implementation complexity, memory overhead, or performance limitations in certain edge cases. Always benchmark against alternatives for your specific workload.
How does Paxos Consensus Algorithm compare to its alternatives?
Paxos Consensus Algorithm offers distinct trade-offs compared to alternative approaches. While it excels in certain scenarios, other algorithms or data structures may be better suited for different problem constraints. The comparison table in the article provides a detailed analysis of these trade-offs.