This is an automated email from the ASF dual-hosted git repository.

FrankChen021 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new 3e17fcc5b45 fix(test): wait for Kafka partitions before publishing 
(#19817)
3e17fcc5b45 is described below

commit 3e17fcc5b456311372b8bf1b8d7b3728adf5bf8c
Author: Frank Chen <[email protected]>
AuthorDate: Tue Aug 4 10:22:43 2026 +0800

    fix(test): wait for Kafka partitions before publishing (#19817)
    
    * Test: wait for Kafka partitions before publishing
    
    * Test: wait for Kafka topics before publishing
    
    * Test: target newly added Kafka partitions
    
    * fix(test): strengthen Kafka partition readiness checks
    
    * Test: rely on Kafka list offset retries
---
 .../indexing/kafka/simulate/KafkaResource.java     | 32 ++++++++++++++++----
 .../indexing/kafka/simulate/KafkaResourceTest.java | 34 ++++++++++++++++++++++
 2 files changed, 60 insertions(+), 6 deletions(-)

diff --git 
a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/simulate/KafkaResource.java
 
b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/simulate/KafkaResource.java
index 8ef0c888b7e..465e4db35d4 100644
--- 
a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/simulate/KafkaResource.java
+++ 
b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/simulate/KafkaResource.java
@@ -27,10 +27,12 @@ import org.apache.kafka.clients.admin.Admin;
 import org.apache.kafka.clients.admin.CreatePartitionsResult;
 import org.apache.kafka.clients.admin.NewPartitions;
 import org.apache.kafka.clients.admin.NewTopic;
+import org.apache.kafka.clients.admin.OffsetSpec;
 import org.apache.kafka.clients.consumer.KafkaConsumer;
 import org.apache.kafka.clients.producer.KafkaProducer;
 import org.apache.kafka.clients.producer.ProducerConfig;
 import org.apache.kafka.clients.producer.ProducerRecord;
+import org.apache.kafka.clients.producer.RecordMetadata;
 import org.apache.kafka.common.TopicPartition;
 import org.apache.kafka.common.serialization.ByteArrayDeserializer;
 import org.apache.kafka.common.serialization.ByteArraySerializer;
@@ -41,6 +43,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.Future;
 import java.util.concurrent.ThreadLocalRandom;
 
 /**
@@ -134,6 +137,7 @@ public class KafkaResource extends 
StreamIngestResource<KafkaContainer>
       admin.createTopics(
           List.of(new NewTopic(topicName, numPartitions, (short) 1))
       ).all().get();
+      waitForPartitionsToBeReady(admin, topicName, numPartitions);
     }
     catch (Exception e) {
       throw new RuntimeException(e);
@@ -162,9 +166,9 @@ public class KafkaResource extends 
StreamIngestResource<KafkaContainer>
   }
 
   /**
-   * Increases the number of partitions in the given Kakfa topic. The topic 
must
-   * already exist. This method waits until the increase in the partition count
-   * has started (but not necessarily finished).
+   * Increases the number of partitions in the given Kafka topic. The topic 
must
+   * already exist. This method waits until every partition is ready to handle
+   * requests.
    */
   @Override
   public void increasePartitionsInTopic(String topic, int newPartitionCount)
@@ -174,8 +178,8 @@ public class KafkaResource extends 
StreamIngestResource<KafkaContainer>
           Map.of(topic, NewPartitions.increaseTo(newPartitionCount))
       );
 
-      // Wait for the partitioning to start
       result.values().get(topic).get();
+      waitForPartitionsToBeReady(admin, topic, newPartitionCount);
     }
     catch (Exception e) {
       throw new RuntimeException(e);
@@ -218,8 +222,12 @@ public class KafkaResource extends 
StreamIngestResource<KafkaContainer>
     props.remove(ProducerConfig.TRANSACTIONAL_ID_CONFIG);
 
     try (final KafkaProducer<byte[], byte[]> kafkaProducer = new 
KafkaProducer<>(props)) {
-      for (ProducerRecord<byte[], byte[]> record : records) {
-        kafkaProducer.send(record);
+      final List<Future<RecordMetadata>> sendResults = new 
ArrayList<>(records.size());
+      for (final ProducerRecord<byte[], byte[]> record : records) {
+        sendResults.add(kafkaProducer.send(record));
+      }
+      for (final Future<RecordMetadata> sendResult : sendResults) {
+        sendResult.get();
       }
     }
     catch (Exception e) {
@@ -295,6 +303,18 @@ public class KafkaResource extends 
StreamIngestResource<KafkaContainer>
     return new KafkaProducer<>(producerProperties);
   }
 
+  private void waitForPartitionsToBeReady(Admin admin, String topic, int 
partitionCount) throws Exception
+  {
+    // Topic and partition creation may complete before the partition leaders
+    // are ready to handle requests. Verify all partitions through their
+    // leaders before allowing callers to publish records.
+    final Map<TopicPartition, OffsetSpec> partitionOffsets = new HashMap<>();
+    for (int partition = 0; partition < partitionCount; partition++) {
+      partitionOffsets.put(new TopicPartition(topic, partition), 
OffsetSpec.latest());
+    }
+    admin.listOffsets(partitionOffsets).all().get();
+  }
+
   private Map<String, Object> commonClientProperties()
   {
     return Map.of("bootstrap.servers", getBootstrapServerUrl());
diff --git 
a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/simulate/KafkaResourceTest.java
 
b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/simulate/KafkaResourceTest.java
index 9b823741772..a15492ca992 100644
--- 
a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/simulate/KafkaResourceTest.java
+++ 
b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/simulate/KafkaResourceTest.java
@@ -20,9 +20,11 @@
 package org.apache.druid.indexing.kafka.simulate;
 
 import org.apache.druid.testing.embedded.EmbeddedDruidCluster;
+import org.apache.kafka.clients.producer.ProducerRecord;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -60,8 +62,40 @@ public class KafkaResourceTest
     final String topicName = "test-topic";
     resource.createTopicWithPartitions(topicName, 3);
     assertEquals(Set.of(topicName), resource.listTopics());
+
+    // Verify that every partition can accept records immediately after 
creating a topic.
+    resource.produceRecordsWithoutTransaction(
+        List.of(
+            new ProducerRecord<>(topicName, 0, null, new byte[]{1}),
+            new ProducerRecord<>(topicName, 1, null, new byte[]{1}),
+            new ProducerRecord<>(topicName, 2, null, new byte[]{1})
+        )
+    );
+    assertEquals(
+        Map.of("0", 1L, "1", 1L, "2", 1L),
+        resource.getPartitionOffsets(topicName)
+    );
     resource.deleteTopic(topicName);
 
+    final String expandedTopicName = "test-expanded-topic";
+    resource.createTopicWithPartitions(expandedTopicName, 2);
+    resource.increasePartitionsInTopic(expandedTopicName, 4);
+
+    // Verify that every partition can accept records immediately after 
increasing the partition count.
+    resource.produceRecordsWithoutTransaction(
+        List.of(
+            new ProducerRecord<>(expandedTopicName, 0, null, new byte[]{1}),
+            new ProducerRecord<>(expandedTopicName, 1, null, new byte[]{1}),
+            new ProducerRecord<>(expandedTopicName, 2, null, new byte[]{1}),
+            new ProducerRecord<>(expandedTopicName, 3, null, new byte[]{1})
+        )
+    );
+    assertEquals(
+        Map.of("0", 1L, "1", 1L, "2", 1L, "3", 1L),
+        resource.getPartitionOffsets(expandedTopicName)
+    );
+    resource.deleteTopic(expandedTopicName);
+
     resource.stop();
     assertFalse(resource.isRunning());
   }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to