-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-15425: Fail fast in Admin::listOffsets when topic (but not partition) metadata is not found #14314
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
KAFKA-15425: Fail fast in Admin::listOffsets when topic (but not partition) metadata is not found #14314
Changes from all commits
246acd5
8954742
d0eb999
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -212,6 +212,8 @@ | |
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.api.Timeout; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.Arguments; | ||
| import org.junit.jupiter.params.provider.MethodSource; | ||
| import org.junit.jupiter.params.provider.ValueSource; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
@@ -557,12 +559,16 @@ private static FindCoordinatorResponse prepareBatchedFindCoordinatorResponse(Err | |
| } | ||
|
|
||
| private static MetadataResponse prepareMetadataResponse(Cluster cluster, Errors error) { | ||
| return prepareMetadataResponse(cluster, error, error); | ||
| } | ||
|
|
||
| private static MetadataResponse prepareMetadataResponse(Cluster cluster, Errors topicError, Errors partitionError) { | ||
| List<MetadataResponseTopic> metadata = new ArrayList<>(); | ||
| for (String topic : cluster.topics()) { | ||
| List<MetadataResponsePartition> pms = new ArrayList<>(); | ||
| for (PartitionInfo pInfo : cluster.availablePartitionsForTopic(topic)) { | ||
| MetadataResponsePartition pm = new MetadataResponsePartition() | ||
| .setErrorCode(error.code()) | ||
| .setErrorCode(partitionError.code()) | ||
| .setPartitionIndex(pInfo.partition()) | ||
| .setLeaderId(pInfo.leader().id()) | ||
| .setLeaderEpoch(234) | ||
|
|
@@ -572,7 +578,7 @@ private static MetadataResponse prepareMetadataResponse(Cluster cluster, Errors | |
| pms.add(pm); | ||
| } | ||
| MetadataResponseTopic tm = new MetadataResponseTopic() | ||
| .setErrorCode(error.code()) | ||
| .setErrorCode(topicError.code()) | ||
| .setName(topic) | ||
| .setIsInternal(false) | ||
| .setPartitions(pms); | ||
|
|
@@ -5441,7 +5447,6 @@ public void testDescribeMetadataQuorumFailure() { | |
|
|
||
| @Test | ||
| public void testListOffsetsMetadataRetriableErrors() throws Exception { | ||
|
|
||
| Node node0 = new Node(0, "localhost", 8120); | ||
| Node node1 = new Node(1, "localhost", 8121); | ||
| List<Node> nodes = Arrays.asList(node0, node1); | ||
|
|
@@ -5464,7 +5469,8 @@ public void testListOffsetsMetadataRetriableErrors() throws Exception { | |
| env.kafkaClient().setNodeApiVersions(NodeApiVersions.create()); | ||
|
|
||
| env.kafkaClient().prepareResponse(prepareMetadataResponse(cluster, Errors.LEADER_NOT_AVAILABLE)); | ||
| env.kafkaClient().prepareResponse(prepareMetadataResponse(cluster, Errors.UNKNOWN_TOPIC_OR_PARTITION)); | ||
| // We retry when a partition of a topic (but not the topic itself) is unknown | ||
| env.kafkaClient().prepareResponse(prepareMetadataResponse(cluster, Errors.NONE, Errors.UNKNOWN_TOPIC_OR_PARTITION)); | ||
| env.kafkaClient().prepareResponse(prepareMetadataResponse(cluster, Errors.NONE)); | ||
|
|
||
| // listoffsets response from broker 0 | ||
|
|
@@ -5615,9 +5621,13 @@ public void testListOffsetsWithLeaderChange() throws Exception { | |
| } | ||
| } | ||
|
|
||
| @Test | ||
| public void testListOffsetsMetadataNonRetriableErrors() throws Exception { | ||
|
|
||
| @ParameterizedTest | ||
| @MethodSource("listOffsetsMetadataNonRetriableErrors") | ||
| public void testListOffsetsMetadataNonRetriableErrors( | ||
| Errors topicMetadataError, | ||
| Errors partitionMetadataError, | ||
| Class<? extends Throwable> expectedFailure | ||
| ) throws Exception { | ||
| Node node0 = new Node(0, "localhost", 8120); | ||
| Node node1 = new Node(1, "localhost", 8121); | ||
| List<Node> nodes = Arrays.asList(node0, node1); | ||
|
|
@@ -5633,18 +5643,49 @@ public void testListOffsetsMetadataNonRetriableErrors() throws Exception { | |
| node0); | ||
|
|
||
| final TopicPartition tp1 = new TopicPartition("foo", 0); | ||
| final MetadataResponse preparedResponse = prepareMetadataResponse( | ||
| cluster, topicMetadataError, partitionMetadataError | ||
| ); | ||
|
|
||
| try (AdminClientUnitTestEnv env = new AdminClientUnitTestEnv(cluster)) { | ||
| env.kafkaClient().setNodeApiVersions(NodeApiVersions.create()); | ||
|
|
||
| env.kafkaClient().prepareResponse(prepareMetadataResponse(cluster, Errors.TOPIC_AUTHORIZATION_FAILED)); | ||
| env.kafkaClient().prepareResponse(preparedResponse); | ||
|
|
||
| Map<TopicPartition, OffsetSpec> partitions = new HashMap<>(); | ||
| partitions.put(tp1, OffsetSpec.latest()); | ||
| ListOffsetsResult result = env.adminClient().listOffsets(partitions); | ||
|
|
||
| TestUtils.assertFutureError(result.all(), TopicAuthorizationException.class); | ||
| } | ||
| TestUtils.assertFutureError(result.all(), expectedFailure); | ||
| } | ||
| } | ||
|
|
||
| private static Stream<Arguments> listOffsetsMetadataNonRetriableErrors() { | ||
| return Stream.of( | ||
| Arguments.of( | ||
| Errors.TOPIC_AUTHORIZATION_FAILED, | ||
| Errors.TOPIC_AUTHORIZATION_FAILED, | ||
| TopicAuthorizationException.class | ||
| ), | ||
| Arguments.of( | ||
| // We fail fast when the entire topic is unknown... | ||
| Errors.UNKNOWN_TOPIC_OR_PARTITION, | ||
| Errors.NONE, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For my understanding, if the topic also had unknown topic or partition as the partition error message, we would fail fast there too.
Would we want to add this test case too just so the behavior is also shown in tests?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, done. I've tried to clarify in the comments that these are unusual cases we're testing for lest anyone think that this is normal broker behavior. |
||
| UnknownTopicOrPartitionException.class | ||
| ), | ||
| Arguments.of( | ||
| // ... even if a partition in the topic is also somehow reported as unknown... | ||
| Errors.UNKNOWN_TOPIC_OR_PARTITION, | ||
| Errors.UNKNOWN_TOPIC_OR_PARTITION, | ||
| UnknownTopicOrPartitionException.class | ||
| ), | ||
| Arguments.of( | ||
| // ... or a partition in the topic has a different, otherwise-retriable error | ||
| Errors.UNKNOWN_TOPIC_OR_PARTITION, | ||
| Errors.LEADER_NOT_AVAILABLE, | ||
| UnknownTopicOrPartitionException.class | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| @Test | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change only changes the behavior for unknown topic or partition errors right? Leader/broker not available continue to be retriable, topic auth, invalid topic, and other errors continue to fail the request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Asking because my understanding is the only case where we would be concerned about topic and partition level errors is when the topic level error is unknown topic or partition but somehow the partitions for that topic have a different error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly, and we have unit testing coverage to demonstrate this.
This is correct, and I'm optimistic that no such case exists, but wanted to note the potential change in behavior just to be safe.