Scenario 06.0.4 · ZooKeeper vs KRaft: the same cluster, two control planes
Kafka ZooKeeper vs KRaft: what freezes leader elections
The legacy control plane: an external ZooKeeper ensemble stores metadata, and ONE broker - the one holding its /controller znode - is the controller. Killing the controller broker just lets another claim it; losing the ensemble majority - not a broker - is what freezes leader elections.
Run this scenario in the playgroundZooKeeper control plane - ensemble z0/z1/z2, broker-1 elected controller. t+0.000s
The control plane is the legacy ZooKeeper plane: an EXTERNAL 3-node ensemble (z0/z1/z2) stores cluster metadata, and ONE broker is the active controller (broker-1 here: the first broker to create the ephemeral /controller znode wins, not necessarily the lowest id). Contrast KRaft, where the controller is a quorum of dedicated voter nodes.
Open at this step: step 1, ZooKeeper control plane - ensemble z0/z1/z2, broker-1 elected controllerTwo records committed to orders-0 (HW 2), led by broker-1. t+0.700s
broker-1 is BOTH the partition leader and the ZooKeeper-elected controller. Two records replicate to all three brokers; the high-water mark is 2.
Open at this step: step 2, Two records committed to orders-0 (HW 2), led by broker-1Controller broker-1 killed → broker-2 takes over /controller; orders-0 election pending. t+1.300s
Killing the controller BROKER does not kill the control plane - the ensemble is untouched, so the surviving brokers race to recreate /controller and broker-2 wins (a new controller term). broker-1 was also the partition leader, so orders-0 is briefly leaderless while the new controller runs the election.
Open at this step: step 3, Controller broker-1 killed → broker-2 takes over /controller; orders-0 election pendingNew controller broker-2 runs the election → broker-2 leads orders-0. t+1.800s
The freshly-elected controller broker-2 drives the leader election broker-1's death triggered, electing broker-2 as the new leader. The ensemble survived the broker loss - exactly what makes losing the controller broker non-fatal under ZooKeeper.
Open at this step: step 4, New controller broker-2 runs the election → broker-2 leads orders-0Ensemble node z1 killed → 2 of 3 still a majority, controller unaffected. t+2.500s
An ensemble of 3 serves while a strict majority (2 of 3) is reachable. Killing z1 leaves z0+z2 - still a majority - so broker-2 keeps the controllership.
Open at this step: step 5, Ensemble node z1 killed → 2 of 3 still a majority, controller unaffectedEnsemble node z2 killed → ensemble majority lost → NO controller. t+2.800s
Now only z0 survives: 1 of 3 is not a majority, so the ensemble stops serving and no broker can hold the /controller znode. The metadata store lives in the ZK NODES, not the brokers - so the control plane fails even though every remaining broker is healthy.
Open at this step: step 6, Ensemble node z2 killed → ensemble majority lost → NO controllerPartition leader broker-2 killed with no controller → election frozen. t+3.600s
broker-2 (still the leader) is killed. With no controller to run an election, orders-0 stays leaderless - the election freezes. This is the SAME end state as a KRaft quorum loss (06.0.2), reached through a separate ZooKeeper ensemble.
Open at this step: step 7, Partition leader broker-2 killed with no controller → election frozenEnsemble node z1 restarted → broker-3 becomes controller → frozen election resumes. t+4.100s
Restoring z1 brings the ensemble majority back (z0+z1). broker-3, the only live broker, claims /controller as the new controller, and the frozen election immediately resumes: broker-3 takes the lead. Recovery comes from restoring the ENSEMBLE, not the brokers.
Open at this step: step 8, Ensemble node z1 restarted → broker-3 becomes controller → frozen election resumesWhat you start with
KRaft makes the controller a quorum of dedicated voters (the other 06.0.x scenarios). Here we run the ZooKeeper plane that Kafka 4.0 removed (Kafka 3.x and earlier): an external 3-node ensemble holds the metadata, and the broker holding its ephemeral /controller znode is the active controller. We kill the controller broker (another broker just claims the znode), then take the ensemble past its majority and watch the same election freeze that a KRaft quorum loss produces.
Step by step
- ZooKeeper control plane - ensemble z0/z1/z2, broker-1 elected controller t+0.000s The control plane is the legacy ZooKeeper plane: an EXTERNAL 3-node ensemble (z0/z1/z2) stores cluster metadata, and ONE broker is the active controller (broker-1 here: the first broker to create the ephemeral /controller znode wins, not necessarily the lowest id). Contrast KRaft, where the controller is a quorum of dedicated voter nodes. Open at this step: step 1, ZooKeeper control plane - ensemble z0/z1/z2, broker-1 elected controller
- Two records committed to orders-0 (HW 2), led by broker-1 t+0.700s broker-1 is BOTH the partition leader and the ZooKeeper-elected controller. Two records replicate to all three brokers; the high-water mark is 2. Open at this step: step 2, Two records committed to orders-0 (HW 2), led by broker-1
- Controller broker-1 killed → broker-2 takes over /controller; orders-0 election pending t+1.300s Killing the controller BROKER does not kill the control plane - the ensemble is untouched, so the surviving brokers race to recreate /controller and broker-2 wins (a new controller term). broker-1 was also the partition leader, so orders-0 is briefly leaderless while the new controller runs the election. Open at this step: step 3, Controller broker-1 killed → broker-2 takes over /controller; orders-0 election pending
- New controller broker-2 runs the election → broker-2 leads orders-0 t+1.800s The freshly-elected controller broker-2 drives the leader election broker-1's death triggered, electing broker-2 as the new leader. The ensemble survived the broker loss - exactly what makes losing the controller broker non-fatal under ZooKeeper. Open at this step: step 4, New controller broker-2 runs the election → broker-2 leads orders-0
- Ensemble node z1 killed → 2 of 3 still a majority, controller unaffected t+2.500s An ensemble of 3 serves while a strict majority (2 of 3) is reachable. Killing z1 leaves z0+z2 - still a majority - so broker-2 keeps the controllership. Open at this step: step 5, Ensemble node z1 killed → 2 of 3 still a majority, controller unaffected
- Ensemble node z2 killed → ensemble majority lost → NO controller t+2.800s Now only z0 survives: 1 of 3 is not a majority, so the ensemble stops serving and no broker can hold the /controller znode. The metadata store lives in the ZK NODES, not the brokers - so the control plane fails even though every remaining broker is healthy. Open at this step: step 6, Ensemble node z2 killed → ensemble majority lost → NO controller
- Partition leader broker-2 killed with no controller → election frozen t+3.600s broker-2 (still the leader) is killed. With no controller to run an election, orders-0 stays leaderless - the election freezes. This is the SAME end state as a KRaft quorum loss (06.0.2), reached through a separate ZooKeeper ensemble. Open at this step: step 7, Partition leader broker-2 killed with no controller → election frozen
- Ensemble node z1 restarted → broker-3 becomes controller → frozen election resumes t+4.100s Restoring z1 brings the ensemble majority back (z0+z1). broker-3, the only live broker, claims /controller as the new controller, and the frozen election immediately resumes: broker-3 takes the lead. Recovery comes from restoring the ENSEMBLE, not the brokers. Open at this step: step 8, Ensemble node z1 restarted → broker-3 becomes controller → frozen election resumes
The takeaway
Both planes do the same job - store metadata, elect partition leaders - and both freeze elections when their metadata quorum is lost. The difference is WHERE that quorum lives: an external ZooKeeper ensemble (separate nodes) versus KRaft’s dedicated controller voters. Killing the controller broker is survivable in ZooKeeper because the ensemble outlives it; losing the ensemble majority is what actually stops the cluster.
Embed this scenario
Paste this into a blog post, a course page or your team's docs. The scenario plays inside your page, and the link under it points readers to this walkthrough.
<iframe src="https://monedula.dev/kafka-simulator/embed/?scenario=06.0.4" title="Kafka Simulator: ZooKeeper vs KRaft: the same cluster, two control planes" width="100%" height="520" style="border:1px solid #d0d0d0;border-radius:8px" loading="lazy"></iframe>
<p><a href="https://monedula.dev/kafka-simulator/scenarios/zk-vs-kraft/">Kafka ZooKeeper vs KRaft: what freezes leader elections</a></p>