KRaft Consensus
Raft-based consensus for ZooKeeper-free operation.
Overview
KRaft (Kafka Raft) provides:
- No ZooKeeper dependency
- Metadata stored in Surgewave topics
- Faster controller failover
- Simpler operations
Configuration
{
"Surgewave": {
"RaftDataDirectory": "./raft",
"RaftElectionTimeoutMinMs": 150,
"RaftElectionTimeoutMaxMs": 300,
"RaftHeartbeatIntervalMs": 50
}
}
How It Works
flowchart TB
subgraph Raft["Raft Cluster"]
L["Leader (Voter)<br/>handles all writes"]
F1["Follower (Voter)"]
F2["Follower (Voter)"]
L -->|AppendEntries| F1
L -->|AppendEntries| F2
end
APIs
| API | Key | Description |
|---|---|---|
| Vote | 52 | Leader election voting |
| BeginQuorumEpoch | 53 | Start new leader term |
| EndQuorumEpoch | 54 | End leader term |
| DescribeQuorum | 55 | Query quorum state |
| FetchSnapshot | 59 | Fetch metadata snapshot |
Leader Election
- Election Timeout - Follower becomes candidate
- Request Votes - Candidate requests votes
- Majority Wins - First to majority becomes leader
- Heartbeats - Leader sends periodic heartbeats
Term 1: Broker1 is leader
↓ (Broker1 fails)
Term 2: Broker2 wins election
↓ (Broker1 returns as follower)
Broker Registration
Brokers register with controller:
1. BrokerRegistration (API 62)
- BrokerId, Rack, Endpoints
2. BrokerHeartbeat (API 63)
- Periodic liveness signal
3. Controller updates metadata
Configuration Options
| Setting | Default | Description |
|---|---|---|
RaftElectionTimeoutMinMs |
150 | Min election timeout |
RaftElectionTimeoutMaxMs |
300 | Max election timeout |
RaftHeartbeatIntervalMs |
50 | Heartbeat interval |
RaftDataDirectory |
./raft | Raft log storage |
ProcessRoles |
broker,controller | What this node does |
ControllerQuorumVoters |
(empty) | Nodes that vote, as id@host:port |
Roles and the Controller Quorum
By default every node runs in combined mode — it is both a broker and a controller, and every broker votes on metadata. That is the right shape for a single broker, for an embedded host, and for a small cluster.
The quorum then grows with the cluster, and a metadata write waits for a majority of all brokers. To keep it fixed, name the voters:
{
"Surgewave": {
"ProcessRoles": "broker",
"ControllerQuorumVoters": "1@controller-1:9093,2@controller-2:9093,3@controller-3:9093"
}
}
Nodes 1, 2 and 3 carry "ProcessRoles": "broker,controller" (or "controller" alone for
dedicated controllers). Every other broker sets "ProcessRoles": "broker" and becomes an
observer: it receives and serves the metadata log but does not vote, so adding brokers no
longer makes metadata writes slower.
ControllerQuorumVoters and ClusterNodes answer different questions and neither derives from
the other — cluster nodes say who exists, the voter list says who decides. They are checked
against each other at startup: a voter no broker knows about is rejected, because it would be
counted towards the majority and never answer.
A broker that is not in the quorum cannot work out where the quorum is from having been part
of it, so the configured list is how it makes first contact: it tries the voters in turn, and
once it is replicating it follows the leader the metadata log itself reports. The configured
list therefore only has to be good enough to reach the quorum once — a list that has gone
stale is survivable rather than fatal. A broker configured with a quorum and nothing else
still joins; ClusterNodes is not required for it.
The exception is a cluster starting for the first time, where there is no log to learn from and the configured list is the whole truth. If every voter has been tried and none answered, Surgewave logs an error naming the endpoints, so a mistyped list does not look like a network problem.
Prefer an odd number of voters. Four voters need three to agree, exactly as five do, so the fourth adds a node that can fail without adding a failure the quorum survives; Surgewave logs a warning for an even count rather than refusing it, since it is a legitimate intermediate state during a rolling change.
Changing the voter set on a running cluster (KIP-853 online reconfiguration) is not implemented — the list is read at startup.
Raft Log
Raft log persisted to disk:
raft/
├── log/
│ ├── 00000000000000000000.log
│ └── 00000000000000001000.log
├── snapshots/
│ └── snapshot-1000.dat
└── metadata.json
Quorum Operations
# View quorum state
surgewave cluster status
# Output:
# Cluster ID: surgewave-cluster
# Controller: Broker 1
# Quorum:
# Leader: 1 (epoch: 5)
# Voters: [1, 2, 3]
# Observers: []
Failure Scenarios
Leader Failure
- Followers detect missing heartbeats
- Election timeout triggers
- New leader elected (majority quorum)
- Clients redirect to new leader
Network Partition
[Broker1] | [Broker2, Broker3]
|
Minority │ Majority
(steps down) (elects new leader)
Minority partition cannot elect leader (no quorum).
Exponential Backoff
RPC retries use exponential backoff:
Attempt 1: 100ms
Attempt 2: 200ms
Attempt 3: 400ms
...
Max: 5000ms
Monitoring
| Metric | Description |
|---|---|
surgewave_raft_term |
Current raft term |
surgewave_raft_leader |
Current leader ID |
surgewave_raft_elections_total |
Election count |
surgewave_raft_log_end_offset |
Log end offset |
Best Practices
- Odd number of voters - 3 or 5 for clean majority
- Low latency network - Election timeouts depend on RTT
- Persistent storage - SSDs for raft log
- Monitor elections - Frequent elections indicate issues
Next Steps
- Failover - Failure handling
- Replication - Data replication