frankvicky commented on code in PR #22405:
URL: https://github.com/apache/kafka/pull/22405#discussion_r3631274207


##########
clients/src/main/java/org/apache/kafka/clients/consumer/internals/ConsumerHeartbeatRequestManager.java:
##########
@@ -100,12 +101,14 @@ public boolean handleSpecificFailure(Throwable exception) 
{
         String errorMessage = exception.getMessage();
         if (exception instanceof UnsupportedVersionException) {
             String message = CONSUMER_PROTOCOL_NOT_SUPPORTED_MSG;
-            if (errorMessage.equals(REGEX_RESOLUTION_NOT_SUPPORTED_MSG)) {
+            if (exception instanceof UnsupportedProtocolFieldException) {
                 message = REGEX_RESOLUTION_NOT_SUPPORTED_MSG;
                 logger.error("{} regex resolution not supported: {}", 
heartbeatRequestName(), message);

Review Comment:
   Could we use `exception.getMessage()` here (and in the log line above)? That 
way we don't need to touch this branch when a new field validation is added to 
`ConsumerGroupHeartbeatRequest.Builder` — the specific message from the builder 
will flow through automatically. Also, `errorMessage` on line 101 is currently 
unused in this branch, so this would put it back to work.



##########
clients/src/main/java/org/apache/kafka/common/requests/ElectLeadersRequest.java:
##########
@@ -63,7 +63,7 @@ public String toString() {
 
         private ElectLeadersRequestData toRequestData(short version) {
             if (electionType != ElectionType.PREFERRED && version == 0) {
-                throw new UnsupportedVersionException("API Version 0 only 
supports PREFERRED election type");
+                throw new UnsupportedProtocolFieldException("ElectionType", 
apiKey().name(), version, 1);

Review Comment:
   Could it be better?
   ```suggestion
                   throw new 
UnsupportedProtocolFieldException(electionType.name(), apiKey().name(), 
version, 1);
   ```



##########
streams/src/main/java/org/apache/kafka/streams/processor/internals/InternalTopicManager.java:
##########
@@ -575,17 +575,14 @@ private Set<String> createTopics(final Set<NewTopic> 
topicsToCreate,
                     log.error("Unexpected error during topic creation for 
{}.\n" +
                             "Error message was: {}", topicName, 
cause.toString());
 
-                    if (cause instanceof UnsupportedVersionException) {
-                        final String errorMessage = cause.getMessage();
-                        if (errorMessage != null &&
-                                errorMessage.startsWith("Creating topics with 
default partitions/replication factor are only supported in CreateTopicRequest 
version 4+")) {
-
-                            throw new StreamsException(String.format(
-                                    "Could not create topic %s, because 
brokers don't support configuration replication.factor=-1."
-                                            + " You can change the 
replication.factor config or upgrade your brokers to version 2.4 or newer to 
avoid this error.",
-                                    topicName)
-                            );
-                        }
+                    if (cause instanceof UnsupportedProtocolFieldException) {
+                        // An older broker rejected a field we rely on (e.g. 
the default
+                        // replication.factor=-1, which requires CreateTopics 
request version 4+).
+                        throw new StreamsException(String.format(
+                                "Could not create topic %s, because brokers 
don't support configuration replication.factor=-1."
+                                        + " You can change the 
replication.factor config or upgrade your brokers to version 2.4 or newer to 
avoid this error.",
+                                topicName)
+                        );

Review Comment:
   This branch now matches any `UnsupportedProtocolFieldException`, but the 
`StreamsException` message still hard-codes `replication.factor=-1` as the root 
cause. Today `CreateTopicsRequest.Builder` throws this exception in two places 
(`validateOnly` at v0, and `topicsWithDefaults` at v<4), and Streams only hits 
the latter — so it works for now. 
   
   But if a new field validation is ever added to the builder, we'll 
misattribute the failure here. 
   Could we pass `cause.getMessage()` into the `StreamsException` instead of 
the fixed message? 
   That keeps the type-based check (which matches the spirit of this PR) 
without hard-coding an assumption about which field failed.



##########
clients/src/test/java/org/apache/kafka/common/requests/RequestResponseTest.java:
##########
@@ -699,8 +700,7 @@ public void 
testCreateTopicRequestV3FailsIfNoPartitionsOrReplicas() {
 
                 new Builder(data).build((short) 3);
             });
-        assertTrue(exception.getMessage().contains("supported in 
CreateTopicRequest version 4+"));
-        assertTrue(exception.getMessage().contains("[foo, bar]"));
+        assertTrue(exception.getMessage().contains("does not support [foo,bar] 
in CREATE_TOPICS API version 3"));

Review Comment:
   Should we add a unit test for `UnsupportedProtocolFieldException`?



##########
clients/src/main/java/org/apache/kafka/common/requests/CreateTopicsRequest.java:
##########
@@ -52,10 +51,8 @@ public CreateTopicsRequest build(short version) {
                 .collect(Collectors.toList());
 
             if (!topicsWithDefaults.isEmpty() && version < 4) {
-                throw new UnsupportedVersionException("Creating topics with 
default "
-                    + "partitions/replication factor are only supported in 
CreateTopicRequest "
-                    + "version 4+. The following topics need values for 
partitions and replicas: "
-                    + topicsWithDefaults);
+                throw new UnsupportedProtocolFieldException(String.join(",", 
topicsWithDefaults),
+                    apiKey().name(), version, 4);

Review Comment:
   Passing the topic names as `fieldOrValue` makes the message read as "the 
cluster does not support [foo,bar] in CREATE_TOPICS API version 3", which 
sounds like the topic names themselves are unsupported. The actual unsupported 
thing is using default partitions/replication for those topics. Could we pass 
something like `"default partitions/replication for topics [" + 
String.join(",", topicsWithDefaults) + "]"` instead, so the message accurately 
describes what the older broker rejected?



##########
clients/src/main/java/org/apache/kafka/common/requests/ListGroupsRequest.java:
##########
@@ -60,9 +59,7 @@ public ListGroupsRequest build(short version) {
                 boolean containedClassic = 
typesCopy.remove(GroupType.CLASSIC.toString());
                 boolean containedConsumer = 
typesCopy.remove(GroupType.CONSUMER.toString());
                 if (!typesCopy.isEmpty() || (!containedClassic && 
containedConsumer)) {
-                    throw new UnsupportedVersionException("The broker only 
supports ListGroups " +
-                        "v" + version + ", but we need v5 or newer to request 
groups by type. " +
-                        "Requested group types: [" + String.join(", ", 
data.typesFilter()) + "].");
+                    throw new UnsupportedProtocolFieldException("TypesFilter", 
apiKey().name(), version, 5);

Review Comment:
   The new message drops the actual `data.typesFilter()` contents, which the 
old message included ("Requested group types: [...]"). Without it the user 
can't tell which of their requested types the broker rejected. Could we pass 
the offending types into the field argument — e.g. the `typesFilter` values 
that remain after removing the versions-supported ones (`CLASSIC`/`CONSUMER`) — 
so the message still shows what actually failed?



##########
clients/src/main/java/org/apache/kafka/common/requests/CreateAclsRequest.java:
##########
@@ -90,8 +92,13 @@ private void validate(CreateAclsRequestData data) {
         if (version() == 0) {
             final boolean unsupported = 
data.creations().stream().anyMatch(creation ->
                 creation.resourcePatternType() != PatternType.LITERAL.code());
-            if (unsupported)
-                throw new UnsupportedVersionException("Version 0 only supports 
literal resource pattern types");
+            if (unsupported) {
+                String unsupportedType = Arrays.stream(PatternType.values())
+                    .filter(type -> type != PatternType.LITERAL)
+                    .map(PatternType::name)
+                    .collect(Collectors.joining(","));
+                throw new UnsupportedProtocolFieldException(unsupportedType, 
apiKey().name(), version(), 1);
+            }

Review Comment:
   The `unsupportedType` string is built by joining every non-LITERAL value 
from `PatternType.values()`, so the message will claim the cluster doesn't 
support that whole list even when the request only contained one non-LITERAL 
type. Could we surface the actual offending pattern type from the request (e.g. 
`creation.resourcePatternType()`) instead? The old message ("only LITERAL and 
ANY are supported" / "only literal resource pattern types") was also more 
actionable — it told the user what IS supported rather than enumerating what 
isn't. Same suggestion applies to `DeleteAclsRequest` and `DescribeAclsRequest`.



-- 
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