KAFKA-20295: Add opt-in workaround for stale controller registrations during metadata.version upgrades - #69
Conversation
0539005 to
3df78ea
Compare
Feature validation currently checks every controller registration stored in metadata. After dynamic quorum reconfiguration, removed controllers can remain registered in the metadata image even though they are no longer part of the live voter set. This can block metadata.version upgrades because the stale registration may advertise an older supported range. Fix this by using the current Raft voter IDs when verifying controller feature support. Historical controller registrations are still preserved in metadata, but they no longer participate in feature gating. Add regression coverage to ensure stale removed controllers are ignored while live controllers with an older supported range still reject the upgrade. Co-authored-by: Pi Agent (gpt-5.4)
deecbd5 to
551d9dd
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces an opt-in recovery path to unblock metadata.version upgrades when stale controller registrations (no longer in the live KRaft voter set) linger in the metadata image and incorrectly fail feature validation. It also adds a safety guard to prevent re-adding outdated controllers into the quorum after using that override.
Changes:
- Adds
--ignore-stale-controller-registrationstokafka-features.sh upgrade, plumbed through AdminClientUpdateFeaturesOptionsinto a new UpdateFeatures RPC request flag. - Updates controller-side feature validation to optionally validate only the live voter-set controllers (and fail closed when voter IDs are unavailable while registrations are supported).
- Adds a metadata.version compatibility check when handling
AddRaftVoter, and exposes Raft voter IDs viaRaftClient.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/src/main/java/org/apache/kafka/tools/FeatureCommand.java | Adds CLI flag and forwards it into UpdateFeaturesOptions. |
| tools/src/test/java/org/apache/kafka/tools/FeatureCommandTest.java | Verifies the CLI flag propagates into UpdateFeaturesOptions. |
| clients/src/main/java/org/apache/kafka/clients/admin/UpdateFeaturesOptions.java | Adds the new AdminClient option knob for ignoring stale controller registrations. |
| clients/src/main/java/org/apache/kafka/clients/admin/KafkaAdminClient.java | Serializes the new request flag onto UpdateFeaturesRequestData. |
| clients/src/main/resources/common/message/UpdateFeaturesRequest.json | Introduces UpdateFeaturesRequest v3 with IgnoreStaleControllerRegistrations field. |
| clients/src/main/resources/common/message/UpdateFeaturesResponse.json | Extends response validVersions to include v3 (same schema as v2). |
| clients/src/test/java/org/apache/kafka/common/requests/UpdateFeaturesRequestTest.java | Adds protocol roundtrip test for v3 ignore-stale flag. |
| clients/src/test/java/org/apache/kafka/clients/admin/KafkaAdminClientTest.java | Ensures AdminClient sets ignore-stale flag on the request. |
| metadata/src/main/java/org/apache/kafka/controller/FeatureControlManager.java | Implements opt-in filtering of controller registrations using live voter IDs. |
| metadata/src/main/java/org/apache/kafka/controller/ConfigurationControlManager.java | Plumbs ignore-stale flag into FeatureControlManager updateFeatures. |
| metadata/src/main/java/org/apache/kafka/controller/ClusterFeatureSupportDescriber.java | Adds method to expose current quorum controller IDs. |
| metadata/src/main/java/org/apache/kafka/controller/QuorumController.java | Implements quorumControllerIds() by querying the Raft client voter set. |
| metadata/src/test/java/org/apache/kafka/controller/FeatureControlManagerTest.java | Adds regression tests for default vs opt-in behavior and failure-closed semantics. |
| core/src/main/scala/kafka/server/ControllerApis.scala | Rejects AddRaftVoter when the target controller’s metadata.version range is incompatible. |
| core/src/test/scala/unit/kafka/server/ControllerApisTest.scala | Tests AddRaftVoter compatibility checks and registration-required behavior. |
| raft/src/main/java/org/apache/kafka/raft/RaftClient.java | Adds RaftClient.voterIds() API for retrieving current voter IDs. |
| raft/src/main/java/org/apache/kafka/raft/KafkaRaftClient.java | Implements voterIds() using the control record state machine’s last voter set. |
| metadata/src/test/java/org/apache/kafka/metalog/LocalLogManager.java | Implements voterIds() for the test RaftClient used in metadata tests. |
| metadata/src/test/java/org/apache/kafka/image/publisher/SnapshotEmitterTest.java | Implements voterIds() in an RaftClient test stub. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Add an opt-in UpdateFeatures request flag for ignoring stale controller registrations during feature validation. By default, behavior is unchanged: feature updates still consider all registered controllers. When the new flag is set, feature validation uses the live voter set to filter out historical controller registrations that are no longer active. This gives us a targeted recovery path for clusters affected by stale controller metadata without changing normal upgrade semantics. Expose the flag through kafka-features upgrade as --ignore-stale-controller-registrations and add request/admin/controller coverage for the new path. Co-authored-by: Pi Agent (gpt-5.4)
The opt-in stale-controller override lets metadata.version upgrades ignore controllers that are outside the live quorum. That is fine for the current upgrade, but it leaves a gap if one of those skipped controllers is later added back as a voter. Close that gap by checking AddRaftVoter against the controller's registered supported metadata.version range before forwarding the request to the Raft layer. If the target controller does not advertise support for the current cluster metadata.version, reject the add-voter request early with INVALID_REQUEST. This keeps the override focused on the live quorum while still preventing an outdated controller from rejoining the quorum without first being upgraded. Co-authored-by: Pi Agent (gpt-5.4)
551d9dd to
279e436
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
raft/src/main/java/org/apache/kafka/raft/RaftClient.java:140
RaftClientis a public interface; addingvoterIds()as an abstract method is source/binary-incompatible for downstreamRaftClientimplementations. Make this adefaultmethod (returning an empty set) so existing implementations continue to compile, while allowingKafkaRaftClientto override it.
/**
* Returns the current voter IDs known to this client.
*
* Implementations may return an empty set before initialization completes.
*
* This method is thread-safe and may be called from threads other than the KRaft IO thread.
*
* @return the current voter IDs
*/
default Set<Integer> voterIds() {
|
Closing, rejected in favor of: #70 |
This PR adds a targeted recovery path for clusters where stale controller
registrations remain in metadata after quorum reconfiguration and block
metadata.versionupgrades.This mainly affects clusters that are upgraded to a newer Kafka version via
rolling upgrade, for example 3.9 -> 4.0. In that workflow, controller IDs are
not re-used, so controllers running the old version can remain registered in
metadata even after they are no longer part of the live voter set. Those stale
registrations are still advertised as using the previous version, which prevents
a the
metadata.versionupgrade (kafka-features.sh).The workaround basically consists in ignoring the stale controllers when the
kafka-features.shis run, so that it can complete successfully.A a proper fix that allows controllers to be unregistered in being worked on
(apache#22191), this PR provides a quickfix until
the proper fix is available.
The changes in this PR are split into three commits:
Ignore stale controller registrations in feature validation
Add opt-in stale controller check override
UpdateFeaturesrequest flagregistered controllers
registration support exists
Guard AddRaftVoter with metadata.version support
metadata.versionrange includes the cluster's currentmetadata.versionafter having been ignored by the opt-in override
This keeps the recovery path narrow and explicit:
Example usage:
kafka-features.sh --bootstrap-server $BOOTSTRAP upgrade --release-version 4.0 --ignore-stale-controller-registrations