Skip to content

Commit 0c4ff87

Browse files
committed
Fix the consumer race in spring-kafka-1.3.x and 2.2.x scenarios
Both scenarios have the race fixed for spring-kafka-2.3.x-scenario in #841. The consumer has no committed offset and starts from the latest offset, while healthCheck reported success as soon as Tomcat started. When the entry request sent "helloWorld" before the consumer got its partition, the consumer skipped it, the request waited forever on the latch and its segment was never reported (SegmentNotFoundException for GET:/case/spring-kafka-case). Report healthy only after partitions are assigned. With auto commit disabled, the container resolves and commits the position of every assigned partition before it calls this listener, so a message sent after the health check passes is always consumed.
1 parent 698b224 commit 0c4ff87

2 files changed

Lines changed: 42 additions & 2 deletions

File tree

  • test/plugin/scenarios
    • spring-kafka-1.3.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring/kafka/controller
    • spring-kafka-2.2.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring/kafka/controller

‎test/plugin/scenarios/spring-kafka-1.3.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring/kafka/controller/CaseController.java‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
import okhttp3.Request;
2323
import okhttp3.Response;
2424
import org.apache.kafka.clients.consumer.ConsumerConfig;
25+
import org.apache.kafka.clients.consumer.ConsumerRebalanceListener;
2526
import org.apache.kafka.clients.consumer.ConsumerRecord;
2627
import org.apache.kafka.clients.producer.ProducerConfig;
28+
import org.apache.kafka.common.TopicPartition;
2729
import org.apache.kafka.common.serialization.Deserializer;
2830
import org.apache.kafka.common.serialization.StringDeserializer;
2931
import org.apache.kafka.common.serialization.StringSerializer;
@@ -43,6 +45,7 @@
4345

4446
import javax.annotation.PostConstruct;
4547
import java.io.IOException;
48+
import java.util.Collection;
4649
import java.util.HashMap;
4750
import java.util.Map;
4851
import java.util.concurrent.CountDownLatch;
@@ -61,6 +64,7 @@ public class CaseController {
6164

6265
private CountDownLatch latch = new CountDownLatch(1);
6366
private String helloWorld = "helloWorld";
67+
private volatile boolean partitionsAssigned = false;
6468

6569
@PostConstruct
6670
private void setUp() {
@@ -91,6 +95,19 @@ private void setUpConsumer() {
9195
Deserializer<String> stringDeserializer = new StringDeserializer();
9296
DefaultKafkaConsumerFactory<String, String> factory = new DefaultKafkaConsumerFactory(configs, stringDeserializer, stringDeserializer);
9397
ContainerProperties props = new ContainerProperties(topicName);
98+
// The consumer starts from the latest offset, so the case must not send before partitions are assigned.
99+
props.setConsumerRebalanceListener(new ConsumerRebalanceListener() {
100+
@Override
101+
public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
102+
}
103+
104+
@Override
105+
public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
106+
if (!partitions.isEmpty()) {
107+
partitionsAssigned = true;
108+
}
109+
}
110+
});
94111
props.setMessageListener(new AcknowledgingMessageListener<String, String>() {
95112
@Override
96113
public void onMessage(ConsumerRecord<String, String> data, Acknowledgment acknowledgment) {
@@ -131,7 +148,10 @@ public String springKafkaConsumerPing() {
131148
@RequestMapping("/healthCheck")
132149
@ResponseBody
133150
public String healthCheck() {
134-
return SUCCESS;
151+
if (partitionsAssigned) {
152+
return SUCCESS;
153+
}
154+
throw new RuntimeException("kafka consumer partitions not assigned");
135155
}
136156
}
137157

‎test/plugin/scenarios/spring-kafka-2.2.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring/kafka/controller/CaseController.java‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
import okhttp3.Request;
2323
import okhttp3.Response;
2424
import org.apache.kafka.clients.consumer.ConsumerConfig;
25+
import org.apache.kafka.clients.consumer.ConsumerRebalanceListener;
2526
import org.apache.kafka.clients.consumer.ConsumerRecord;
2627
import org.apache.kafka.clients.producer.ProducerConfig;
28+
import org.apache.kafka.common.TopicPartition;
2729
import org.apache.kafka.common.serialization.Deserializer;
2830
import org.apache.kafka.common.serialization.StringDeserializer;
2931
import org.apache.kafka.common.serialization.StringSerializer;
@@ -42,6 +44,7 @@
4244

4345
import javax.annotation.PostConstruct;
4446
import java.io.IOException;
47+
import java.util.Collection;
4548
import java.util.HashMap;
4649
import java.util.Map;
4750
import java.util.concurrent.CountDownLatch;
@@ -60,6 +63,7 @@ public class CaseController {
6063

6164
private CountDownLatch latch = new CountDownLatch(1);
6265
private String helloWorld = "helloWorld";
66+
private volatile boolean partitionsAssigned = false;
6367

6468
@PostConstruct
6569
private void setUp() {
@@ -90,6 +94,19 @@ private void setUpConsumer() {
9094
Deserializer<String> stringDeserializer = new StringDeserializer();
9195
DefaultKafkaConsumerFactory<String, String> factory = new DefaultKafkaConsumerFactory(configs, stringDeserializer, stringDeserializer);
9296
ContainerProperties props = new ContainerProperties(topicName);
97+
// The consumer starts from the latest offset, so the case must not send before partitions are assigned.
98+
props.setConsumerRebalanceListener(new ConsumerRebalanceListener() {
99+
@Override
100+
public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
101+
}
102+
103+
@Override
104+
public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
105+
if (!partitions.isEmpty()) {
106+
partitionsAssigned = true;
107+
}
108+
}
109+
});
93110
props.setMessageListener(new AcknowledgingMessageListener<String, String>() {
94111
@Override
95112
public void onMessage(ConsumerRecord<String, String> data, Acknowledgment acknowledgment) {
@@ -130,7 +147,10 @@ public String springKafkaConsumerPing() {
130147
@RequestMapping("/healthCheck")
131148
@ResponseBody
132149
public String healthCheck() {
133-
return SUCCESS;
150+
if (partitionsAssigned) {
151+
return SUCCESS;
152+
}
153+
throw new RuntimeException("kafka consumer partitions not assigned");
134154
}
135155
}
136156

0 commit comments

Comments
 (0)