Skip to content
Back to all concepts
Distributed SystemsConsensusAlgorithms

Leader Election

Comprehensive guide to distributed leader election algorithms including Raft, Paxos, and Bully algorithm, covering consensus, split-brain prevention, and real-world implementations in Kubernetes, ZooKeeper, and etcd.

By ScaleWikiUpdated 4 min readAdvanced

What is Leader Election?

In a distributed system, leader election is the process of designating a single node as the coordinator responsible for managing tasks across multiple machines.

Key question: When you have 5 database replicas, which one is the master? If the master dies, how do you pick a new one?

Why Need a Leader?

Scenario: 3 database replicas
Problem: All 3 accept writes → Data diverges!

Solution: Elect 1 leader
- Leader accepts all writes
- Followers replicate from leader
- Consistent view of data

Use cases:

  • Database replication (Postgres master)
  • Distributed locks (who runs the cron job?)
  • Task coordination (MapReduce job tracker)
  • Configuration management (etcd leader)

The Split-Brain Problem

Initial state:
┌──────┐  ┌──────┐  ┌──────┐
│Node A│  │Node B│  │Node C│
│LEADER│  │follower│ │follower│
└──────┘  └──────┘  └──────┘

Network partition:
┌──────┐     │     ┌──────┐  ┌──────┐
│Node A│     │     │Node B│  │Node C│
│LEADER│     │     │follower│ │follower│
└──────┘     │     └──────┘  └──────┘
 Partition 1 │  Partition 2

Both sides elect leaders:
┌──────┐     │     ┌──────┐  ┌──────┐
│Node A│     │     │Node B│  │Node C│
│LEADER│     │     │LEADER│ │follower│
└──────┘     │     └──────┘  └──────┘

PROBLEM: Two leaders accepting writes!

Consensus approach: Require a majority (floor(N/2) + 1) under protocol rules that prevent conflicting votes. A quorum count alone does not fence a stale leader from external resources.

3 nodes: Need 2 votes (quorum)
5 nodes: Need 3 votes (quorum)

Partition 1: Node A (1 vote) → Can't become leader
Partition 2: Nodes B, C (2 votes) → Can elect leader

Only one partition can have quorum!

Consensus Algorithms

1. Bully Algorithm

Simple ID-based election: Highest ID wins.

Process:

  1. Node suspects the leader is unreachable
  2. Sends election message to all higher-ID nodes
  3. If no response from higher-ID nodes, declares itself leader
  4. If higher-ID node responds, that node starts election

Implementation:

python
class BullyElection:
    def __init__(self, node_id, all_nodes):
        self.node_id = node_id
        self.all_nodes = sorted(all_nodes)
        self.leader = None
        self.in_election = False
    
    def start_election(self):
        """Start election process"""
        print(f"Node {self.node_id}: Starting election")
        self.in_election = True
        
        # Find nodes with higher IDs
        higher_nodes = [n for n in self.all_nodes if n > self.node_id]
        
        if not higher_nodes:
            # I have highest ID - become leader
            self.become_leader()
            return
        
        # Send election messages to higher nodes
        responses = []
        for node in higher_nodes:
            response = self.send_election_message(node)
            if response:
                responses.append(response)
        
        if responses:
            # Higher node responded - wait for them to win
            print(f"Node {self.node_id}: Higher node responded, waiting...")
        else:
            # No response from higher nodes - I win!
            self.become_leader()
    
    def become_leader(self):
        """Declare self as leader"""
        self.leader = self.node_id
        self.in_election = False
        print(f"Node {self.node_id}: I AM THE LEADER!")
        
        # Announce to all lower nodes
        for node in self.all_nodes:
            if node < self.node_id:
                self.send_coordinator_message(node)
    
    def send_election_message(self, target_node):
        """Send election message to target node"""
        # Simulate message - returns True if node alive
        return is_node_alive(target_node)
    
    def send_coordinator_message(self, target_node):
        """Announce leadership to node"""
        # Notify node of new leader
        pass
    
    def receive_election_message(self, from_node):
        """Handle incoming election message"""
        if from_node < self.node_id:
            # I have higher ID - respond and start my own election
            print(f"Node {self.node_id}: Received election from {from_node}, bullying...")
            self.start_election()
            return True
        return False

# Example usage
nodes = [1, 2, 3, 4, 5]
node3 = BullyElection(node_id=3, all_nodes=nodes)

# Node 5 (leader) dies
# Node 3 notices and starts election
node3.start_election()
# Output: Node 3 becomes leader (highest alive ID)

Pros:

  • Simple to implement
  • Elects the highest reachable ID under its timing and communication assumptions

Cons:

  • Many messages (O(n²) in worst case)
  • Not fault-tolerant during election
  • Can have multiple elections simultaneously

2. Raft Consensus

Modern, understandable consensus algorithm. Used by etcd (Kubernetes), Consul, and CockroachDB.

Key concepts:

  • Leader: Handles all client requests
  • Followers: Replicate leader's log
  • Candidates: Nodes trying to become leader

States:

┌──────────┐  timeout   ┌───────────┐  wins      ┌────────┐
│ Follower │───────────▶│ Candidate │───────────▶│ Leader │
└──────────┘            └───────────┘            └────────┘
     ▲                        │                       │
     │                        │ loses                 │
     │                        ▼                       │
     └────────────────────────────────────────────────┘

Election process:

What a safe election implementation must preserve

A vote counter alone is not Raft. A candidate carries its term and last-log metadata. A voter checks log freshness, persists its term/vote before responding, and votes at most once per term. The candidate counts distinct votes only for its current election; delayed responses from an older term cannot make it leader.

A majority is floor(voting_members / 2) + 1, including the candidate's own vote. Membership changes need the protocol's configuration-change rules. Use a maintained consensus implementation rather than a short timer/RPC example for authoritative state.

Raft guarantees:

  • Safety: At most one leader per term
  • Liveness: Progress requires a communicating majority and suitable timing conditions
  • Log replication: Only committed entries are authoritative; uncommitted entries may be overwritten

Pros:

  • Understandable (simpler than Paxos)
  • Production-ready implementations
  • Strong consistency guarantees

Cons:

  • Complex to implement correctly
  • Network partition sensitive
  • Leader bottleneck for writes

3. Paxos

Classic consensus algorithm (Leslie Lamport, 1989). Very difficult to understand and implement.

Roles:

  • Proposers: Propose values
  • Acceptors: Vote on proposals
  • Learners: Learn chosen value

Phases:

Phase 1: Prepare
- Proposer sends prepare(n) to acceptors
- Acceptors promise not to accept proposals < n

Phase 2: Accept
- Proposer sends accept(n, value)
- Acceptors accept if >= highest promise

Why it's hard: Subtle edge cases, liveness not guaranteed, difficult to implement.

Modern variants:

  • Multi-Paxos: Optimized for multiple decisions
  • Fast Paxos: Faster under low contention
  • Raft: Easier-to-understand alternative

Quorum Systems

Definition: Subset of nodes that can make progress.

3 nodes: Quorum = 2
5 nodes: Quorum = 3
7 nodes: Quorum = 4

Formula: Quorum = ⌊N/2⌋ + 1

Why quorum works:

5 nodes split into 2 partitions:
Partition A: 3 nodes  (has quorum) ✓
Partition B: 2 nodes  (no quorum)  ✗

Only Partition A can elect leader!

Trade-offs:

More nodes:
+ Higher availability (tolerate more failures)
- Slower consensus (need more votes)
- More expensive

Fewer nodes:
+ Faster consensus
+ Cheaper
- Lower availability

Real-World Systems

Kubernetes (etcd)

etcd uses Raft for leader election

3 or 5 voting members is common; odd counts use majority fault tolerance efficiently
Leader handles all writes
Followers replicate

If leader dies:
- Followers reach their configured election timeout
- New election starts
- Recovery time depends on timing and available quorum

Apache ZooKeeper

ZooKeeper uses ZAB (ZooKeeper Atomic Broadcast)
Similar to Paxos/Raft

Used by:
- Older Kafka deployments used ZooKeeper; current Kafka uses KRaft
- HBase (master election)
- Hadoop (NameNode HA)

Consul

Uses Raft for consensus

Deploy 3 or 5 server nodes
Leader handles writes
Followers forward to leader
Used for service discovery + KV store

Implementation Patterns

Using etcd for Leader Election

go
// Go client for etcd leader election
package main

import (
    "context"
    "fmt"
    "go.etcd.io/etcd/client/v3/concurrency"
    clientv3 "go.etcd.io/etcd/client/v3"
)

func main() {
    client, _ := clientv3.New(clientv3.Config{
        Endpoints: []string{"localhost:2379"},
    })
    defer client.Close()
    
    session, _ := concurrency.NewSession(client)
    defer session.Close()
    
    election := concurrency.NewElection(session, "/my-service/leader")
    
    // Campaign to become leader (blocks until elected)
    ctx := context.Background()
    if err := election.Campaign(ctx, "node-1"); err != nil {
        fmt.Println("Failed to campaign:", err)
        return
    }
    
    fmt.Println("I AM THE LEADER!")
    
    // Do leader work...
    doLeaderWork()
    
    // Resign leadership
    election.Resign(ctx)
}

func doLeaderWork() {
    // Only one node executes this at a time
    fmt.Println("Doing critical work as leader...")
}

A lease is not a fencing mechanism

A SET key owner NX PX ttl lease can expire while its old holder is paused. A new holder may then acquire it; when the old process resumes, both can attempt work. Comparing the owner token atomically before renewal/deletion protects the lease key, but does not prevent a stale process from writing to a different database.

For safety-critical work, require monotonically increasing fencing tokens that the protected resource validates, or perform the action within an appropriate transactional/consensus boundary. Decide what happens when the lease service is unreachable. A local boolean named isLeader is not sufficient authority.

Interview Tips 💡

When discussing leader election in system design interviews:

  1. Explain the need: "Multiple database replicas need one master for consistent writes..."
  2. Quorum math: "With 5 nodes, need 3 votes to prevent split-brain..."
  3. Choose algorithm: "Use Raft (via etcd) for simplicity and proven implementation..."
  4. Handle failures: "If leader dies, followers timeout after 150-300ms and start election..."
  5. Discuss trade-offs: "3 nodes cheaper but 5 nodes more fault-tolerant..."
  6. Real examples: "Kubernetes uses etcd with Raft, modern Kafka uses KRaft..."

Sources and a failure experiment

Raft's published paper defines election safety and log restrictions. etcd concurrency client documentation documents sessions and election primitives. Pause the old leader past its lease, elect a successor, then resume it: the protected resource must reject stale writes even if the old process still believes it leads.