fix(inkless:switch): bump leader epoch on classic-to-diskless switch#626
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes classic-to-diskless topic switches getting stuck in CLASSIC_TO_DISKLESS_SWITCH_PENDING by ensuring the controller emits PartitionChangeRecords that force a leader-epoch bump (which brokers use to enter makeLeader and complete sealing/registration).
Changes:
- Set
PartitionChangeRecord.leaderinmarkClassicToDisklessSwitchStartedsoPartitionRegistration.merge()bumpsleaderEpoch. - Add a warning when a partition has
NO_LEADERat switch time (switch stays pending until a leader is elected). - Extend the controller unit test to validate that emitted records include a leader and that
leaderEpochincrements after replay.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
metadata/src/main/java/org/apache/kafka/controller/ReplicationControlManager.java |
Forces leaderEpoch bump by setting leader on the emitted PartitionChangeRecord; logs when no leader exists. |
metadata/src/test/java/org/apache/kafka/controller/ReplicationControlManagerTest.java |
Asserts leader is set on the emitted record(s) and validates the leaderEpoch bump after replay. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
markClassicToDisklessSwitchStarted emits a PartitionChangeRecord without setting the leader field, so PartitionRegistration.merge() sees NO_LEADER_CHANGE and skips the leaderEpoch bump. Brokers only call makeLeader on epoch changes, leaving the partition stuck in PENDING. This was latent since the method was introduced, but was masked by BrokerMetadataPublisher.sealExistingLeadersOfTopicsSwitchedToDiskless() which triggered the switch via config-delta scan — independent of epoch. PR #604 (ad647a4) removed that path and moved all switch logic into applyLocalLeadersDelta, which requires isNewLeaderEpoch == true. The controller was never updated to provide it. Fix: set .setLeader(partition.leader) to force an epoch bump. The broker sees the new epoch, enters makeLeader, seals the log, and registers with InitDisklessLogManager. Also warn when the partition has no leader at switch time — the PENDING state persists and completes once a leader is elected. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
626d6f2 to
98f77b0
Compare
viktorsomogyi
approved these changes
Jun 3, 2026
giuseppelillo
pushed a commit
that referenced
this pull request
Jun 4, 2026
…626) markClassicToDisklessSwitchStarted emits a PartitionChangeRecord without setting the leader field, so PartitionRegistration.merge() sees NO_LEADER_CHANGE and skips the leaderEpoch bump. Brokers only call makeLeader on epoch changes, leaving the partition stuck in PENDING. This was latent since the method was introduced, but was masked by BrokerMetadataPublisher.sealExistingLeadersOfTopicsSwitchedToDiskless() which triggered the switch via config-delta scan — independent of epoch. PR #604 (ad647a4) removed that path and moved all switch logic into applyLocalLeadersDelta, which requires isNewLeaderEpoch == true. The controller was never updated to provide it. Fix: set .setLeader(partition.leader) to force an epoch bump. The broker sees the new epoch, enters makeLeader, seals the log, and registers with InitDisklessLogManager. Also warn when the partition has no leader at switch time — the PENDING state persists and completes once a leader is elected. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
gqmelo
pushed a commit
that referenced
this pull request
Jun 4, 2026
…626) markClassicToDisklessSwitchStarted emits a PartitionChangeRecord without setting the leader field, so PartitionRegistration.merge() sees NO_LEADER_CHANGE and skips the leaderEpoch bump. Brokers only call makeLeader on epoch changes, leaving the partition stuck in PENDING. This was latent since the method was introduced, but was masked by BrokerMetadataPublisher.sealExistingLeadersOfTopicsSwitchedToDiskless() which triggered the switch via config-delta scan — independent of epoch. PR #604 (ad647a4) removed that path and moved all switch logic into applyLocalLeadersDelta, which requires isNewLeaderEpoch == true. The controller was never updated to provide it. Fix: set .setLeader(partition.leader) to force an epoch bump. The broker sees the new epoch, enters makeLeader, seals the log, and registers with InitDisklessLogManager. Also warn when the partition has no leader at switch time — the PENDING state persists and completes once a leader is elected. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
markClassicToDisklessSwitchStarted emits a PartitionChangeRecord without setting the leader field, so PartitionRegistration.merge() sees NO_LEADER_CHANGE and skips the leaderEpoch bump. Brokers only call makeLeader on epoch changes, leaving the partition stuck in PENDING.
This was latent since the method was introduced, but was masked by BrokerMetadataPublisher.sealExistingLeadersOfTopicsSwitchedToDiskless() which triggered the switch via config-delta scan — independent of epoch. PR #604 (ad647a4) removed that path and moved all switch logic into applyLocalLeadersDelta, which requires isNewLeaderEpoch == true. The controller was never updated to provide it.
Fix: set .setLeader(partition.leader) to force an epoch bump. The broker sees the new epoch, enters makeLeader, seals the log, and registers with InitDisklessLogManager. Also warn when the partition has no leader at switch time — the PENDING state persists and completes once a leader is elected.