|
46 | 46 | import java.util.concurrent.TimeUnit; |
47 | 47 | import java.util.concurrent.atomic.AtomicBoolean; |
48 | 48 | import java.util.concurrent.atomic.AtomicReference; |
| 49 | +import java.util.function.BiConsumer; |
49 | 50 |
|
50 | 51 | import org.apache.commons.logging.LogFactory; |
51 | 52 | import org.apache.kafka.clients.consumer.Consumer; |
@@ -112,9 +113,11 @@ public class TransactionalContainerTests { |
112 | 113 |
|
113 | 114 | private static String topic3DLT = "txTopic3.DLT"; |
114 | 115 |
|
| 116 | + private static String topic4 = "txTopic4"; |
| 117 | + |
115 | 118 | @ClassRule |
116 | 119 | public static EmbeddedKafkaRule embeddedKafkaRule = new EmbeddedKafkaRule(1, true, topic1, topic2, topic3, |
117 | | - topic3DLT) |
| 120 | + topic3DLT, topic4) |
118 | 121 | .brokerProperty(KafkaConfig.TransactionsTopicReplicationFactorProp(), "1") |
119 | 122 | .brokerProperty(KafkaConfig.TransactionsTopicMinISRProp(), "1"); |
120 | 123 |
|
@@ -595,6 +598,69 @@ public void accept(ConsumerRecord<?, ?> record, Exception exception) { |
595 | 598 | logger.info("Stop testMaxAttempts"); |
596 | 599 | } |
597 | 600 |
|
| 601 | + @SuppressWarnings("unchecked") |
| 602 | + @Test |
| 603 | + public void testRollbackProcessorCrash() throws Exception { |
| 604 | + logger.info("Start testRollbackNoRetries"); |
| 605 | + Map<String, Object> props = KafkaTestUtils.consumerProps("testRollbackNoRetries", "false", embeddedKafka); |
| 606 | + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); |
| 607 | + props.put(ConsumerConfig.GROUP_ID_CONFIG, "group"); |
| 608 | + props.put(ConsumerConfig.ISOLATION_LEVEL_CONFIG, "read_committed"); |
| 609 | + DefaultKafkaConsumerFactory<Integer, String> cf = new DefaultKafkaConsumerFactory<>(props); |
| 610 | + ContainerProperties containerProps = new ContainerProperties(topic4); |
| 611 | + containerProps.setPollTimeout(10_000); |
| 612 | + |
| 613 | + Map<String, Object> senderProps = KafkaTestUtils.producerProps(embeddedKafka); |
| 614 | + senderProps.put(ProducerConfig.RETRIES_CONFIG, 1); |
| 615 | + DefaultKafkaProducerFactory<Object, Object> pf = new DefaultKafkaProducerFactory<>(senderProps); |
| 616 | + pf.setTransactionIdPrefix("noRetries."); |
| 617 | + final KafkaTemplate<Object, Object> template = new KafkaTemplate<>(pf); |
| 618 | + final CountDownLatch latch = new CountDownLatch(1); |
| 619 | + AtomicReference<String> data = new AtomicReference<>(); |
| 620 | + containerProps.setMessageListener((MessageListener<Integer, String>) message -> { |
| 621 | + data.set(message.value()); |
| 622 | + if (message.offset() == 0) { |
| 623 | + throw new RuntimeException("fail for no retry"); |
| 624 | + } |
| 625 | + latch.countDown(); |
| 626 | + }); |
| 627 | + |
| 628 | + @SuppressWarnings({ "rawtypes" }) |
| 629 | + KafkaTransactionManager tm = new KafkaTransactionManager(pf); |
| 630 | + containerProps.setTransactionManager(tm); |
| 631 | + KafkaMessageListenerContainer<Integer, String> container = |
| 632 | + new KafkaMessageListenerContainer<>(cf, containerProps); |
| 633 | + container.setBeanName("testRollbackNoRetries"); |
| 634 | + BiConsumer<ConsumerRecord<?, ?>, Exception> recoverer = (rec, ex) -> { |
| 635 | + throw new RuntimeException("arbp fail"); |
| 636 | + }; |
| 637 | + DefaultAfterRollbackProcessor<Object, Object> afterRollbackProcessor = |
| 638 | + spy(new DefaultAfterRollbackProcessor<>(recoverer, new FixedBackOff(0L, 0L))); |
| 639 | + container.setAfterRollbackProcessor(afterRollbackProcessor); |
| 640 | + final CountDownLatch stopLatch = new CountDownLatch(1); |
| 641 | + container.setApplicationEventPublisher(e -> { |
| 642 | + if (e instanceof ConsumerStoppedEvent) { |
| 643 | + stopLatch.countDown(); |
| 644 | + } |
| 645 | + }); |
| 646 | + container.start(); |
| 647 | + |
| 648 | + template.setDefaultTopic(topic4); |
| 649 | + template.executeInTransaction(t -> { |
| 650 | + RecordHeaders headers = new RecordHeaders(new RecordHeader[] { new RecordHeader("baz", "qux".getBytes()) }); |
| 651 | + ProducerRecord<Object, Object> record = new ProducerRecord<>(topic4, 0, 0, "foo", headers); |
| 652 | + template.send(record); |
| 653 | + template.sendDefault(0, 0, "bar"); |
| 654 | + return null; |
| 655 | + }); |
| 656 | + assertThat(latch.await(60, TimeUnit.SECONDS)).isTrue(); |
| 657 | + assertThat(data.get()).isEqualTo("bar"); |
| 658 | + container.stop(); |
| 659 | + pf.destroy(); |
| 660 | + assertThat(stopLatch.await(10, TimeUnit.SECONDS)).isTrue(); |
| 661 | + logger.info("Stop testRollbackNoRetries"); |
| 662 | + } |
| 663 | + |
598 | 664 | @SuppressWarnings("serial") |
599 | 665 | public static class SomeOtherTransactionManager extends AbstractPlatformTransactionManager { |
600 | 666 |
|
|
0 commit comments