3.1 Replication Protocol Explained
Module Overview
Goal: The ONE authoritative explanation of Kafka's replication protocol.
What You'll Learn:
- Leader-follower replication model
- Write path replication internals
- Acknowledgment flow
- Replica fetch protocol details
- Replication states and transitions
What's NOT Covered Here:
- ISR membership rules (covered in Module 3.2)
- High watermark calculation (covered in Module 3.2)
- Detailed follower fetch behavior (covered in Module 3.3)
Key Insight: Kafka uses leader-based replication where the partition leader coordinates all writes and followers replicate asynchronously. This design prioritizes availability and throughput over strong consistency.
1. Why Replication?
The Durability Problem
WITHOUT REPLICATION:
Single broker stores partition:
├─ Broker crashes → Data lost
├─ Disk fails → Data lost
└─ No fault tolerance
WITH REPLICATION (RF=3):
Three brokers store partition:
├─ One leader, two followers
├─ Any broker fails → Data still available
└─ Fault tolerance = RF - 1 failures
2. Leader-Based Replication Model
Roles
PARTITION REPLICAS:
Leader:
├─ Accepts all client writes
├─ Assigns offsets
├─ Serves client reads (default)
├─ Coordinates replication
└─ Tracks follower progress
Followers (RF - 1):
├─ Fetch data from leader (pull-based)
├─ Replicate leader's log exactly
├─ Do NOT accept client writes
├─ Stand by for leader failure
└─ Can serve reads (if configured)
Why Leader-Based?
ALTERNATIVE: Multi-Writer (consensus per write)
Problems:
├─ Requires distributed consensus (Paxos, Raft)
├─ Slower writes (quorum for each write)
└─ Complex coordination
KAFKA'S APPROACH: Single Writer (leader)
Benefits:
├─ Fast writes (no consensus per write)
├─ Simple replication (just copy log)
├─ High throughput
└─ Leader handles all coordination
3. Write Path Replication
Producer Write Flow
STEP-BY-STEP:
1. Producer sends ProduceRequest to leader
└─ Contains batch of records for partition
2. Leader validates request
├─ Authorization (if security enabled)
└─ Request size within limits
3. Leader appends to local log
├─ Assigns sequential offsets
├─ Writes to active segment
└─ Updates Log End Offset (LEO)
4. Leader waits for replication (if acks=all)
├─ Tracks follower LEOs
├─ Waits for all ISR members to fetch
└─ Advances High Watermark
5. Leader responds to producer
├─ Success: Base offset of batch
└─ Error: Error code (if failed)
Replication Flow
FOLLOWER REPLICATION:
While (true) {
1. Follower sends FetchRequest to leader
└─ Includes current offset (follower's LEO)
2. Leader responds with new records
└─ From follower's LEO to leader's LEO
3. Follower appends to local log
├─ Validates offsets match
├─ Writes to disk
└─ Updates follower's LEO
4. Repeat (continuous fetching)
}
Key points:
├─ Pull-based (follower initiates)
├─ Async (follower fetches independently)
└─ Leader tracks progress
4. Acknowledgment Levels
acks=0 (No Acknowledgment)
BEHAVIOR:
Producer:
└─ Fire-and-forget
└─ Doesn't wait for broker response
Leader:
└─ Accepts request
└─ May not write before responding
Durability: None
├─ Data may be lost
└─ No confirmation of write
Throughput: Highest
└─ No waiting
acks=1 (Leader Acknowledgment)
BEHAVIOR:
Producer:
└─ Waits for leader acknowledgment
Leader:
├─ Appends to local log
├─ Responds immediately (doesn't wait for followers)
└─ Replication happens asynchronously
Durability: Moderate
├─ Data on leader
├─ But not yet replicated
└─ Leader failure may lose data
Throughput: High
└─ Minimal waiting
acks=all (ISR Acknowledgment)
BEHAVIOR:
Producer:
└─ Waits for all ISR members to acknowledge
Leader:
├─ Appends to local log
├─ Waits for all ISR followers to fetch
├─ Advances High Watermark
└─ Responds to producer
Durability: Highest
├─ Data on all ISR replicas
├─ Safe from leader failure (if min.insync.replicas configured)
└─ Best durability guarantee
Throughput: Moderate
└─ Waits for replication
5. Replica Fetch Request Details
FetchRequest Contents
FETCH REQUEST FROM FOLLOWER:
FetchRequest:
├─ replica id: Follower's broker ID
├─ max wait: Max time to wait if no data
├─ min bytes: Minimum bytes to return
└─ partitions:
├─ topic: partition's topic
├─ partition: partition ID
├─ fetch offset: Follower's current LEO
└─ max bytes: Max bytes for this partition
Example:
├─ Follower (broker 2) fetching from leader (broker 1)
├─ Partition: orders-0
├─ Fetch offset: 1000 (follower's LEO)
└─ Leader responds with records 1000-1050
FetchResponse Contents
FETCH RESPONSE FROM LEADER:
FetchResponse:
├─ partition data:
│ ├─ topic: orders
│ ├─ partition: 0
│ ├─ high watermark: 950 (leader's HW)
│ ├─ log start offset: 0
│ ├─ records: [1000-1050] (requested data)
│ └─ aborted transactions: (if any)
└─ throttle time: 0 ms
Follower actions:
├─ Append records to local log
├─ Update follower LEO to 1051
└─ Note leader's HW (for consumer visibility)
6. Replication States
Replica State Machine
FOLLOWER REPLICA STATES:
NewReplica:
└─ Just created, not yet started replication
OnlineFetchingReplica:
└─ Actively fetching from leader (normal state)
OfflineReplica:
└─ Broker hosting replica is down
TruncatingLog:
└─ Truncating divergent log after leader change
DeletingReplica:
└─ Being deleted (partition reassignment or deletion)
TRANSITIONS:
NewReplica → OnlineFetchingReplica:
└─ Start replication
OnlineFetchingReplica → OfflineReplica:
└─ Broker failure
OfflineReplica → OnlineFetchingReplica:
└─ Broker recovery
OnlineFetchingReplica → TruncatingLog:
└─ Leader change detected, divergence found
7. Log End Offset (LEO) Tracking
Per-Replica LEO
LEADER MAINTAINS LEO FOR EACH REPLICA:
Partition: orders-0
Leader: Broker 1
Replicas: [1, 2, 3]
LEOs:
├─ Broker 1 (leader): 1010
├─ Broker 2 (follower): 1009
└─ Broker 3 (follower): 1008
Each fetch request updates leader's view of follower LEO:
└─ Follower sends: "My LEO is X"
└─ Leader records: "Follower Y is at offset X"
LEO Update Mechanism
WHEN FOLLOWER FETCHES:
1. Follower sends FetchRequest
└─ fetch offset = follower's current LEO (e.g., 1000)
2. Leader interprets this as:
└─ "Follower has replicated up to offset 999"
└─ "Follower's LEO is 1000"
3. Leader records: Follower LEO = 1000
4. Leader responds with records 1000+
5. Follower appends, updates local LEO to 1010
6. Next FetchRequest: fetch offset = 1010
└─ Leader updates: Follower LEO = 1010
Continuous loop keeps leader's view up-to-date
8. Replication Lag
Definition
REPLICATION LAG:
Lag = Leader LEO - Follower LEO
Example:
├─ Leader LEO: 1010
├─ Follower LEO: 1008
└─ Lag: 2 records
Units:
├─ Records (message count)
└─ Time (replica.lag.time.max.ms)
Lag Detection
LEADER TRACKS FOLLOWER LAG:
For each follower:
├─ Last fetch timestamp
├─ Last fetch offset
└─ Current lag
If (now - last fetch time) > replica.lag.time.max.ms:
└─ Follower considered "out of sync"
└─ Removed from ISR (covered in Module 3.2)
Example:
├─ replica.lag.time.max.ms: 10 seconds
├─ Follower hasn't fetched for 12 seconds
└─ Leader removes follower from ISR
9. Replication Throttling
Purpose
WHY THROTTLE?
Replication can saturate network:
├─ High write throughput → high replication traffic
├─ Replication shares network with client traffic
└─ May impact client latency
THROTTLING:
Limit replication bandwidth:
├─ leader.replication.throttled.rate
└─ follower.replication.throttled.rate
Applied during:
├─ Normal replication (if configured)
└─ Partition reassignment (to avoid overload)
Throttling Mechanism
LEADER-SIDE THROTTLING:
Leader tracks bytes sent to followers:
├─ If rate > throttle limit:
│ └─ Delay FetchResponse
└─ Else: Respond immediately
FOLLOWER-SIDE THROTTLING:
Follower tracks bytes fetched:
├─ If rate > throttle limit:
│ └─ Delay next FetchRequest
└─ Else: Fetch immediately
Result:
└─ Replication slowed to configured rate
└─ Protects network from saturation
10. Replication and Zero-Copy
sendfile() Optimization
WHEN FOLLOWER FETCHES:
Leader serving data to follower:
├─ Data read from disk (or page cache)
├─ Sent over network to follower
Zero-copy path:
└─ sendfile() syscall
└─ Disk → OS buffer → Network (kernel space)
└─ No copy to user space (Kafka process)
Benefits:
├─ Lower CPU usage
├─ Higher throughput
└─ Less memory pressure
Same optimization as consumer fetches
11. Replication During Reassignment
Partition Reassignment
WHEN ADDING NEW REPLICA:
Example: Add broker 4 as new replica for orders-0
1. Controller sends LeaderAndIsrRequest
└─ New replica list: [1, 2, 3, 4]
2. Broker 4 starts as NewReplica
└─ Fetches from beginning of log (offset 0)
3. Broker 4 catches up
├─ Replicates all existing data
└─ Eventually reaches leader's LEO
4. Broker 4 added to ISR
└─ Now in-sync, participates in durability
5. (Optional) Remove old replica
└─ If reassigning away from old broker
12. Key Takeaways
You've learned:
✅ Leader-based replication: Single writer (leader) per partition
✅ Pull-based: Followers fetch from leader (not pushed)
✅ acks levels: 0 (none), 1 (leader), all (ISR)
✅ FetchRequest: Follower sends current LEO, leader responds with new records
✅ LEO tracking: Leader maintains view of each follower's LEO
✅ Replication lag: Monitored via time and record count
✅ Replica states: New → Fetching → Offline (and others)
✅ Throttling: Controls replication bandwidth
✅ Zero-copy: sendfile() optimization for replication
✅ Reassignment: New replicas catch up from beginning
Summary
Kafka's replication is designed for high throughput:
Replication Model =
├─ Leader-based (single writer)
├─ Pull-based (followers initiate)
├─ Asynchronous (followers catch up independently)
└─ No consensus per write (fast writes)
Write Flow =
├─ Producer → Leader
├─ Leader appends to log
├─ Followers fetch asynchronously
├─ Leader tracks follower progress
└─ Acknowledgment based on acks level
Durability =
├─ acks=0: None (fire-and-forget)
├─ acks=1: Leader only (fast, moderate durability)
└─ acks=all: All ISR members (slow, high durability)
Remember: Replication in Kafka prioritizes availability and throughput. The leader-based, pull model allows high write rates without waiting for distributed consensus on every write.
Next up: Module 3.2 — ISR & High Watermark Mechanics — The authoritative explanation of durability boundaries.