kevin-wu24 commented on code in PR #21122:
URL: https://github.com/apache/kafka/pull/21122#discussion_r2608362807


##########
core/src/main/scala/kafka/server/ControllerServer.scala:
##########
@@ -158,9 +158,10 @@ class ControllerServer(
       val apiVersionManager = new SimpleApiVersionManager(
         ListenerType.CONTROLLER,
         config.unstableApiVersionsEnabled,
-        () => featuresPublisher.features().setFinalizedLevel(
-          KRaftVersion.FEATURE_NAME,
-          raftManager.client.kraftVersion().featureLevel())
+        () => featuresPublisher.features().map(f =>

Review Comment:
   There is a subtlety here with `kraft.version`. Previously, 
`featuresPublisher.features` was always "present", so we can always set the 
"finalized" kraft version known by the local `KafkaRaftClient`. However, now 
the `featurePublisher.features()` might be an empty optional, in which case the 
function in the `.map` call won't be applied until after 
`featuresPublisher.features().isPresent()`.
   
   This means if a node has kraft.version=1, but has not committed the 
bootstrap metadata version record, it will not show that it is kraft.version=1 
in the ApiVersionsResponse it sends, even though it previously would have.
   
   I think the intention is, for better or for worse, that the 
"finalizedFeatures" section of the `ApiVersionsResponse` to show the local 
node's kraft.version level, even if it may be uncommitted. Is that correct 
@jsancio?



##########
server/src/main/java/org/apache/kafka/server/SimpleApiVersionManager.java:
##########
@@ -67,19 +69,19 @@ public ApiMessageType.ListenerType listenerType() {
 
     @Override
     public ApiVersionsResponse apiVersionResponse(int throttleTimeMs, boolean 
alterFeatureLevel0) {
-        FinalizedFeatures currentFeatures = features();
+        Optional<FinalizedFeatures> currentFeatures = featuresProvider.get();
         return new ApiVersionsResponse.Builder()
                 .setThrottleTimeMs(throttleTimeMs)
                 .setApiVersions(apiVersions)
                 .setSupportedFeatures(brokerFeatures)
-                .setFinalizedFeatures(currentFeatures.finalizedFeatures())
-                
.setFinalizedFeaturesEpoch(currentFeatures.finalizedFeaturesEpoch())
+                
.setFinalizedFeatures(currentFeatures.map(FinalizedFeatures::finalizedFeatures).orElse(Map.of()))
+                
.setFinalizedFeaturesEpoch(currentFeatures.map(FinalizedFeatures::finalizedFeaturesEpoch).orElse(-1L))
                 .setAlterFeatureLevel0(alterFeatureLevel0)
                 .build();
     }
 
     @Override
     public FinalizedFeatures features() {
-        return featuresProvider.get();
+        return featuresProvider.get().orElse(null);

Review Comment:
   We should avoid ever returning null, because this is not safe. Instead, we 
can change the `ApiVersionManager` interface's `features()` method to return an 
`Optional<FinalizedFeatures>`. The implementors can return the wrapped 
optional, and check things based on its presence. 
   
   For example, `ControllerApis#handleDescribeCluster` can now return a more 
useful error message if the optional is not present. We can also add a test for 
that in `ControllerApisTest`.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to