6.1 The Kafka Controller Internals
Module Overview
Goal: Deep dive into controller behavior — the brain of the Kafka cluster.
What You'll Learn:
- Controller responsibilities and operations
- Controller election and failover
- LeaderAndIsrRequest internals
- Metadata update propagation
- Controller state machine
- Controller bottlenecks and optimizations
What's NOT Covered Here:
- ZooKeeper/KRaft details (covered in Modules 5.2 and 5.3)
- Leader election algorithms (covered in Module 4.2)
- Broker lifecycle (covered in Module 7.1)
Key Insight: The controller is the central coordination point in Kafka. Understanding controller internals explains how Kafka manages partition leadership, handles failures, and maintains cluster state.
Note: This module covers controller behavior in both ZK and KRaft modes, noting differences where applicable.
1. Controller Responsibilities
Core Duties
THE CONTROLLER MANAGES:
1. Partition Leadership:
├─ Elect leaders on broker failure
├─ Preferred leader election
└─ Update partition metadata
2. Broker Lifecycle:
├─ Detect broker joins/leaves
├─ Handle broker registration
└─ Track broker liveness
3. Topic Operations:
├─ Create topics
├─ Delete topics
└─ Modify topic configurations
4. Partition Reassignment:
├─ Coordinate replica movement
├─ Add/remove replicas
└─ Update assignments
5. ISR Management:
├─ Process ISR change requests from leaders
├─ Update cluster metadata
└─ Notify brokers of changes
6. Metadata Propagation:
└─ Send UpdateMetadataRequest to all brokers
2. Controller Election
Election Process (ZK Mode)
ZOOKEEPER-BASED ELECTION:
1. All brokers watch /controller znode
2. Controller dies:
└─ Ephemeral /controller znode deleted
3. All brokers notified (via ZK watch)
4. Brokers race to create /controller:
├─ First successful broker becomes controller
└─ Others continue watching
5. New controller:
├─ Increments /controller_epoch
├─ Loads cluster state from ZK
├─ Sends UpdateMetadataRequest to all brokers
└─ Starts managing partitions
Election time: ~Seconds (depends on cluster size)
Election Process (KRaft Mode)
KRAFT RAFT-BASED ELECTION:
1. Controller quorum (3-5 controllers)
2. Active controller (Raft leader):
└─ Handles metadata operations
3. Current leader fails or loses quorum
4. Raft election:
├─ Followers detect leader failure
├─ Candidate elected via majority votes
└─ New leader assumes controller role
5. New controller:
├─ Continues from metadata log offset
├─ No need to reload entire state (already replicated)
└─ Immediately operational
Election time: ~Milliseconds to seconds (much faster than ZK)
3. Controller State Machine
State Transitions
CONTROLLER STATES:
Idle:
└─ Not the controller (watching for controller election)
ControllerChange:
└─ Just elected, initializing state
NewBrokerChange:
└─ Processing new broker registration
BrokerChange:
└─ Processing broker failure/departure
TopicChange:
└─ Processing topic creation/deletion
PartitionModification:
└─ Processing partition reassignment or ISR change
ControllerShutdown:
└─ Gracefully stepping down as controller
TRANSITIONS:
Idle → ControllerChange:
└─ Elected as controller
ControllerChange → (various states):
└─ Processing events
Any state → Idle:
└─ Lost controller role (failover)
4. Leader Election Coordination
Partition Leader Election
WHEN CONTROLLER ELECTS LEADER:
Trigger:
├─ Broker failure (current leader dead)
├─ Preferred leader election
├─ Partition creation
└─ Controlled shutdown
Process:
1. Controller identifies affected partitions
└─ All partitions led by failed broker
2. For each partition:
a. Load current state:
├─ Replicas: [1, 2, 3]
├─ ISR: [1, 2, 3]
└─ Leader: 1 (failed)
b. Select new leader:
├─ Remove failed broker from ISR → [2, 3]
├─ Choose first alive ISR member → Broker 2
└─ Increment leader epoch
c. Update metadata (ZK or metadata log)
3. Send LeaderAndIsrRequest to affected brokers
4. Brokers apply leadership changes
5. Controller sends UpdateMetadataRequest to all brokers
LeaderAndIsrRequest
LEADERANDISR REQUEST STRUCTURE:
LeaderAndIsrRequest:
├─ controller_id: 0
├─ controller_epoch: 5
├─ broker_epoch: 10 (KRaft only)
├─ partition_states:
│ └─ For each affected partition:
│ ├─ topic: "orders"
│ ├─ partition: 0
│ ├─ leader: 2 (new leader)
│ ├─ leader_epoch: 6 (incremented)
│ ├─ isr: [2, 3]
│ ├─ replicas: [1, 2, 3]
│ └─ adding_replicas: [] (if reassignment)
└─ live_leaders: [
{broker_id: 2, host: "broker2", port: 9092},
{broker_id: 3, host: "broker3", port: 9092}
]
Target brokers: All brokers in replica set
5. ISR Management
Processing ISR Changes
ISR CHANGE FLOW:
1. Partition leader detects follower lag:
└─ Follower hasn't fetched within replica.lag.time.max.ms
2. Leader updates local ISR:
└─ ISR: [1, 2, 3] → [1, 2]
3. Leader sends AlterPartitionRequest to controller:
└─ "Partition orders-0, new ISR: [1, 2], leader epoch: 5"
4. Controller validates request:
├─ Check leader epoch matches
├─ Check controller epoch matches
└─ Accept if valid
5. Controller updates metadata:
├─ ZK mode: Write to /brokers/topics/{topic}/partitions/{partition}/state
└─ KRaft mode: Append PartitionChangeRecord to metadata log
6. Controller responds to leader: Success
7. Leader commits ISR change locally
8. Controller sends UpdateMetadataRequest to all brokers
└─ All brokers learn of new ISR
6. Metadata Propagation
UpdateMetadataRequest
CONTROLLER BROADCASTS METADATA:
When to send:
├─ Leader election
├─ ISR change
├─ Broker join/leave
├─ Topic creation/deletion
└─ Partition reassignment
UpdateMetadataRequest:
├─ controller_id: 0
├─ controller_epoch: 5
├─ live_brokers:
│ └─ [(id=1, host=broker1, port=9092), ...]
├─ partition_states:
│ └─ For each updated partition:
│ ├─ topic: "orders"
│ ├─ partition: 0
│ ├─ leader: 2
│ ├─ isr: [2, 3]
│ └─ replicas: [1, 2, 3]
└─ topic_states: (KRaft only)
Sent to: All brokers in cluster
Brokers apply:
└─ Update local metadata cache
└─ Serve fresh metadata to clients
7. Controller Failover
What Happens When Controller Fails
CONTROLLER FAILOVER:
1. Current controller dies (Broker 0)
2. ZK Mode:
├─ Ephemeral /controller znode deleted
├─ All brokers notified
├─ Brokers race to create /controller
└─ Winner becomes new controller
KRaft Mode:
├─ Followers detect leader failure (heartbeat timeout)
├─ Raft election triggered
└─ Majority elects new leader (new controller)
3. New controller (Broker 1):
├─ Increments controller epoch (5 → 6)
│
├─ ZK Mode:
│ ├─ Loads all metadata from ZooKeeper
│ ├─ Scans /brokers/topics/, /brokers/ids/, etc.
│ └─ Rebuilds in-memory state (slow for large clusters)
│
└─ KRaft Mode:
├─ Already has metadata log replicated
├─ Loads from snapshot + recent log (fast)
└─ Immediately operational
4. New controller sends UpdateMetadataRequest to all brokers:
└─ "I'm the new controller (epoch 6)"
5. Brokers accept new controller (higher epoch)
6. Cluster fully operational
Failover time:
├─ ZK mode: Seconds to minutes (large clusters slow)
└─ KRaft mode: Milliseconds to seconds
8. Controller Bottlenecks
Common Performance Issues
CONTROLLER CAN BECOME BOTTLENECK:
1. Large Partition Count:
├─ Millions of partitions
├─ ZK mode: Must scan all metadata (slow failover)
└─ KRaft mode: Better, but still high memory usage
2. Frequent ISR Changes:
├─ High rate of AlterPartitionRequests
├─ Controller processes sequentially
└─ Can saturate controller CPU
3. Broker Failures:
├─ Controller must elect leaders for all affected partitions
├─ Large clusters: Thousands of partitions per broker
└─ Can take minutes to complete
4. ZooKeeper Latency:
├─ Every metadata change writes to ZK
├─ ZK write latency compounds
└─ Slows down controller operations
5. Single-threaded Processing (ZK mode):
└─ Controller event queue processes one event at a time
└─ Can fall behind under high load
Optimizations (KRaft)
KRAFT IMPROVEMENTS:
1. Batched Metadata Writes:
└─ Multiple changes in single log append
└─ Reduces round-trips
2. Snapshots:
└─ Fast failover (snapshot + recent log)
└─ No need to scan all metadata
3. Parallel Processing:
└─ Better concurrency for metadata operations
4. Lower Latency:
└─ No external ZK calls (in-memory + local log)
5. Higher Partition Scale:
└─ Tested with millions of partitions
9. Controller Operations
Topic Creation
CREATE TOPIC FLOW:
1. Client sends CreateTopicsRequest to any broker
2. Broker forwards to controller
3. Controller:
a. Validates request (name, partition count, RF)
b. Selects replica assignments:
└─ Distribute partitions across brokers
c. Writes metadata:
└─ ZK: Create znodes
└─ KRaft: Append TopicRecord, PartitionRecords
d. Sends LeaderAndIsrRequest to brokers hosting replicas
4. Brokers:
├─ Create partition directories
├─ Initialize logs
└─ Start replication (if follower)
5. Controller sends UpdateMetadataRequest to all brokers
6. Topic available for produce/consume
Partition Reassignment
PARTITION REASSIGNMENT FLOW:
Goal: Move partition from [1, 2, 3] to [2, 3, 4]
1. Client submits reassignment request to controller
2. Controller:
a. Add new replica (Broker 4):
└─ Replicas: [1, 2, 3, 4]
└─ Send LeaderAndIsrRequest
b. Broker 4 starts replicating (full sync)
c. Wait for Broker 4 to catch up (join ISR)
d. If leader is being removed (Broker 1):
└─ Elect new leader from [2, 3, 4]
e. Remove old replica (Broker 1):
└─ Replicas: [2, 3, 4]
└─ Send StopReplicaRequest to Broker 1
f. Update metadata
3. Reassignment complete
└─ Partition now on [2, 3, 4]
10. Controller Observability: Log Messages
Understanding Controller Logs
CONTROLLER LOGS:
Purpose:
└─ Track controller state changes and operations
└─ Essential for debugging and monitoring
└─ Indicates cluster health
Location:
└─ Kafka log files (server.log or controller.log)
└─ Typically: /var/log/kafka/server.log
Key Log Messages to Monitor
CONTROLLER ELECTION:
Successful election:
INFO [Controller] Successfully elected as the controller (kafka.controller.KafkaController)
INFO [ControllerEventThread] Starting controller event processor thread (kafka.controller.ControllerEventManager)
INFO [Controller] Elected as the controller with epoch 5
What it means:
├─ This broker became the active controller
├─ Controller epoch incremented
└─ Ready to process events
When you see it:
├─ Broker startup (if elected)
├─ Previous controller failed
└─ Manual controller failover
CONTROLLER STEPPING DOWN:
Relinquishing leadership:
INFO [Controller] Relinquishing controller leadership (kafka.controller.KafkaController)
INFO [ControllerEventThread] Shutting down controller event processor thread (kafka.controller.ControllerEventManager)
INFO [Controller] Resigned as the controller at epoch 5
What it means:
├─ This broker no longer controller
├─ Graceful shutdown or failover
└─ New election will occur
When you see it:
├─ Broker shutdown (controlled)
├─ Controller explicitly stepped down
└─ ZK session expired / KRaft leadership lost
CONTROLLER PROCESSING EVENTS:
Event processing:
INFO [Controller] Processing BrokerChange event (kafka.controller.ControllerEventManager)
INFO [Controller] New broker 3 registered (kafka.controller.KafkaController)
INFO [Controller] Broker 3 started (kafka.controller.KafkaController)
INFO [Controller] Removed broker 2 from metadata (kafka.controller.KafkaController)
What it means:
├─ Controller handling cluster changes
├─ Broker registration/deregistration
└─ Normal cluster operations
Event types:
├─ BrokerChange: Broker joined/left
├─ TopicChange: Topic created/deleted
├─ PartitionModification: Partition reassignment
├─ IsrChange: ISR membership changed
└─ ControllerChange: New controller elected
CONTROLLER EPOCH CHANGE:
Epoch increment:
INFO [Controller] Updated controller epoch to 5 (kafka.controller.KafkaController)
INFO [Controller] Incremented controller epoch to 6 in ZooKeeper (kafka.controller.KafkaController)
What it means:
├─ New controller elected
├─ Epoch prevents stale controller actions
└─ Monotonically increasing
Significance:
└─ Each controller generation has unique epoch
└─ Brokers reject requests from old epochs
PARTITION LEADERSHIP CHANGES:
Leader election:
INFO [Controller] Starting preferred leader election for partitions (kafka.controller.KafkaController)
INFO [Controller] Elected leader 2 for partition orders-0 at offset 1234 (kafka.controller.KafkaController)
INFO [Controller] Successfully elected leader for 10 partitions (kafka.controller.KafkaController)
What it means:
├─ Controller elected new partition leaders
├─ After broker failure or rebalance
└─ Partitions now available
When you see it:
├─ Broker failure
├─ Preferred leader election
└─ Partition reassignment
ISR UPDATES:
ISR changes:
INFO [Controller] Partition [orders,0] ISR updated to [1,2] (kafka.controller.KafkaController)
WARN [Controller] Partition [orders,0] ISR shrunk to [1] (kafka.controller.KafkaController)
INFO [Controller] Partition [orders,0] ISR expanded to [1,2,3] (kafka.controller.KafkaController)
What it means:
├─ ISR membership changed
├─ Follower fell behind or caught up
└─ Impacts durability guarantees
Watch for:
├─ Frequent ISR shrinks: Performance issues
├─ ISR size = 1: Reduced fault tolerance
└─ ISR never expanding: Replication problems
Error and Warning Messages
CONTROLLER ERRORS:
Connection issues:
ERROR [Controller] Error processing event BrokerChange (kafka.controller.ControllerEventManager)
ERROR [Controller] Failed to send LeaderAndIsrRequest to broker 2 (kafka.controller.KafkaController)
WARN [Controller] Broker 2 is not available, skipping LeaderAndIsrRequest (kafka.controller.KafkaController)
What it means:
├─ Controller communication failure
├─ Broker unreachable
└─ May impact metadata propagation
Action required:
└─ Investigate broker health
└─ Check network connectivity
ELECTION FAILURES:
Election problems:
ERROR [Controller] Controller election failed (kafka.controller.KafkaController)
WARN [Controller] Multiple brokers attempting to become controller (kafka.controller.KafkaController)
ERROR [Controller] ZooKeeper session expired during controller election (kafka.controller.KafkaController)
What it means:
├─ Controller election unsuccessful
├─ ZK/KRaft issues
└─ Cluster may be in degraded state
Action required:
└─ Check ZooKeeper/KRaft health
└─ Verify network connectivity
└─ Review controller logs on all brokers
Monitoring Tips
HEALTHY CONTROLLER LOGS:
Normal operation:
├─ Controller elected once (startup)
├─ Event processing completes quickly (<100ms)
├─ Few ISR changes (stable replication)
└─ No repeated errors
Patterns to watch:
✅ Good:
INFO [Controller] Successfully elected as the controller
INFO [Controller] Processing BrokerChange event
INFO [Controller] Elected leader for partition orders-0
❌ Bad (indicates issues):
ERROR [Controller] Error processing event
WARN [Controller] Controller flapping between brokers
ERROR [Controller] Failed to send LeaderAndIsrRequest repeatedly
WARN [Controller] ISR shrunk to 1 for multiple partitions
Log Analysis Commands
# Find controller elections
grep "Successfully elected as the controller" /var/log/kafka/server.log
# Count elections (should be low)
grep -c "Successfully elected as the controller" /var/log/kafka/server.log
# Find controller errors
grep ERROR /var/log/kafka/server.log | grep Controller
# Monitor ISR changes
grep "ISR updated" /var/log/kafka/server.log | tail -20
# Check controller epoch
grep "controller epoch" /var/log/kafka/server.log | tail -5
# Find broker failures (controller perspective)
grep "Removed broker" /var/log/kafka/server.log
# Monitor event processing time
grep "Processing.*event" /var/log/kafka/server.log | \
awk '{print $NF}' | sort -n
Alerting on Controller Issues
RECOMMENDED ALERTS:
1. Controller flapping:
└─ More than 5 elections in 10 minutes
└─ Indicates: Network issues, ZK problems, overload
2. Event processing slow:
└─ Events taking >1 second to process
└─ Indicates: Controller overwhelmed, hardware issue
3. Failed metadata propagation:
└─ LeaderAndIsrRequest failures
└─ Indicates: Broker unreachable, network issues
4. ISR shrink storm:
└─ Multiple partitions losing ISR members
└─ Indicates: Broker performance issues, network problems
Example alert script:
```bash
#!/bin/bash
# Alert on controller flapping
ELECTIONS=$(grep -c "Successfully elected as the controller" \
/var/log/kafka/server.log | \
tail -100)
if [ "$ELECTIONS" -gt 5 ]; then
echo "ALERT: Controller flapping detected ($ELECTIONS elections)"
# Send to monitoring system
exit 1
fi
---
### Log Levels for Controller
CONFIGURING LOG LEVELS:
In log4j.properties:
Default (INFO level)
log4j.logger.kafka.controller=INFO
Debug controller operations
log4j.logger.kafka.controller=DEBUG
Trace all controller events (very verbose)
log4j.logger.kafka.controller=TRACE
Only errors and warnings
log4j.logger.kafka.controller=WARN
Recommendation: ├─ Production: INFO (default) ├─ Troubleshooting: DEBUG (temporarily) └─ Never use TRACE in production (too verbose)
Dynamic log level change (no restart):
kafka-log-dirs.sh --describe \
--log-level kafka.controller=DEBUG
---
### Summary: Log Message Patterns
CONTROLLER LIFECYCLE LOGS:
Startup → Election → Processing Events → Shutdown
Broker starts: "Starting Kafka server"
Controller election: "Successfully elected as the controller"
Normal operation: "Processing BrokerChange event" "Elected leader for partition X" "ISR updated for partition Y"
Graceful shutdown: "Relinquishing controller leadership" "Shutting down controller event processor"
ERROR PATTERNS TO INVESTIGATE:
- Repeated election messages (flapping)
- LeaderAndIsrRequest failures
- Event processing errors
- ZooKeeper/KRaft connection issues
- Timeout errors
HEALTHY CLUSTER: └─ Controller elected once per broker lifetime └─ Events process quickly └─ Few ISR changes └─ No repeated errors
---
## 11. Key Takeaways
**You've learned:**
✅ **Controller is the cluster brain** — manages leadership, failures, metadata
✅ **Election:** ZK (race to create znode) vs KRaft (Raft leader)
✅ **LeaderAndIsrRequest** sent to brokers on leadership changes
✅ **AlterPartitionRequest** from leaders to controller for ISR changes
✅ **UpdateMetadataRequest** broadcasts metadata to all brokers
✅ **Controller failover:** ZK (slow, load from ZK) vs KRaft (fast, snapshot + log)
✅ **Controller bottlenecks:** Large clusters, ISR churn, ZK latency
✅ **KRaft optimizations:** Batching, snapshots, parallelism
✅ **Topic creation:** Controller assigns replicas, brokers initialize partitions
✅ **Partition reassignment:** Add new replica, sync, remove old replica
---
## Summary
**The controller is Kafka's central coordinator:**
Responsibilities = ├─ Partition leadership (elect leaders, track ISR) ├─ Broker lifecycle (detect joins/failures) ├─ Topic operations (create, delete, configure) ├─ Partition reassignment (move replicas) └─ Metadata propagation (UpdateMetadataRequest)
Key Requests = ├─ LeaderAndIsrRequest → Brokers (leadership changes) ├─ AlterPartitionRequest → Controller (ISR updates) ├─ UpdateMetadataRequest → All brokers (metadata sync) └─ StopReplicaRequest → Brokers (remove replicas)
Election = ├─ ZK: Race to create /controller znode └─ KRaft: Raft leader election
Failover = ├─ ZK: Slow (load all metadata from ZK) └─ KRaft: Fast (snapshot + recent log)
Bottlenecks = ├─ Large partition count ├─ Frequent ISR changes ├─ ZK latency (ZK mode) └─ Mitigated by KRaft
**Remember:** The controller is critical for cluster health. Understanding controller internals explains Kafka's coordination behavior and guides capacity planning (especially in large clusters).
**Next up:** Module 7.1 — Broker Lifecycle & State Transitions — Internal broker state machine.