110
110
@ EmbeddedKafka (topics = { TransactionalContainerTests .topic1 , TransactionalContainerTests .topic2 ,
111
111
TransactionalContainerTests .topic3 , TransactionalContainerTests .topic3DLT , TransactionalContainerTests .topic4 ,
112
112
TransactionalContainerTests .topic5 , TransactionalContainerTests .topic6 , TransactionalContainerTests .topic7 ,
113
- TransactionalContainerTests .topic8 , TransactionalContainerTests .topic8DLT , TransactionalContainerTests .topic9 },
113
+ TransactionalContainerTests .topic8 , TransactionalContainerTests .topic8DLT , TransactionalContainerTests .topic9 ,
114
+ TransactionalContainerTests .topic10 },
114
115
brokerProperties = { "transaction.state.log.replication.factor=1" , "transaction.state.log.min.isr=1" })
115
116
public class TransactionalContainerTests {
116
117
@@ -136,6 +137,8 @@ public class TransactionalContainerTests {
136
137
137
138
public static final String topic9 = "txTopic9" ;
138
139
140
+ public static final String topic10 = "txTopic10" ;
141
+
139
142
private static EmbeddedKafkaBroker embeddedKafka ;
140
143
141
144
@ BeforeAll
@@ -1082,4 +1085,67 @@ void testNoAfterRollbackWhenFenced() throws Exception {
1082
1085
assertThatIllegalStateException ().isThrownBy (container ::start );
1083
1086
}
1084
1087
1088
+
1089
+ @ Test
1090
+ void testArbpWithoutRecovery () throws InterruptedException {
1091
+ // init producer
1092
+ Map <String , Object > producerProperties = KafkaTestUtils .producerProps (embeddedKafka );
1093
+ DefaultKafkaProducerFactory <Object , Object > pf = new DefaultKafkaProducerFactory <>(producerProperties );
1094
+ pf .setTransactionIdPrefix ("testArbpResetWithoutRecover.batchListener" );
1095
+ final KafkaTemplate <Object , Object > template = new KafkaTemplate <>(pf );
1096
+ // init consumer
1097
+ String group = "groupInARBP3" ;
1098
+ Map <String , Object > consumerProperties = KafkaTestUtils .consumerProps (group , "false" , embeddedKafka );
1099
+ consumerProperties .put (ConsumerConfig .ISOLATION_LEVEL_CONFIG , "read_committed" );
1100
+ DefaultKafkaConsumerFactory <Integer , String > cf = new DefaultKafkaConsumerFactory <>(consumerProperties );
1101
+ ContainerProperties containerProps = new ContainerProperties (topic10 );
1102
+ containerProps .setPollTimeout (10_000 );
1103
+ containerProps .setBatchRecoverAfterRollback (false ); // we want to test the behavior if recovery is disabled
1104
+ final var successLatch = new AtomicReference <>(new CountDownLatch (2 ));
1105
+ containerProps .setMessageListener (new BatchMessageListener <Integer , String >() {
1106
+ private int attempt = 0 ;
1107
+
1108
+ @ Override
1109
+ public void onMessage (List <ConsumerRecord <Integer , String >> data ) {
1110
+ if (3 > attempt ++) { // the first three attempts should fail
1111
+ throw new BatchListenerFailedException ("fail for test" , data .get (0 ));
1112
+ }
1113
+ data .forEach (d -> successLatch .get ().countDown ());
1114
+ }
1115
+ });
1116
+ // init container
1117
+ KafkaTransactionManager <Object , Object > tm = new KafkaTransactionManager <>(pf );
1118
+ containerProps .setKafkaAwareTransactionManager (tm );
1119
+ KafkaMessageListenerContainer <Integer , String > container = new KafkaMessageListenerContainer <>(cf , containerProps );
1120
+ container .setBeanName ("testArbpWithoutRecover" );
1121
+ DefaultAfterRollbackProcessor <Object , Object > afterRollbackProcessor = spy (
1122
+ new DefaultAfterRollbackProcessor <>(new FixedBackOff (0L , FixedBackOff .UNLIMITED_ATTEMPTS ))
1123
+ );
1124
+ container .setAfterRollbackProcessor (afterRollbackProcessor );
1125
+ container .start ();
1126
+
1127
+ // process first batch
1128
+ template .executeInTransaction (t -> {
1129
+ template .send (new ProducerRecord <>(topic10 , 0 , 0 , "bar1" ));
1130
+ template .send (new ProducerRecord <>(topic10 , 0 , 0 , "bar2" ));
1131
+ return null ;
1132
+ });
1133
+ assertThat (successLatch .get ().await (30 , TimeUnit .SECONDS )).isTrue (); // wait for first batch
1134
+
1135
+ // process second batch
1136
+ successLatch .set (new CountDownLatch (2 )); // reset latch
1137
+ template .executeInTransaction (t -> {
1138
+ template .send (new ProducerRecord <>(topic10 , 0 , 0 , "bar4" ));
1139
+ template .send (new ProducerRecord <>(topic10 , 0 , 0 , "bar5" ));
1140
+ return null ;
1141
+ });
1142
+ assertThat (successLatch .get ().await (30 , TimeUnit .SECONDS )).isTrue (); // wait for second batch
1143
+
1144
+ // assert three processBatch calls due to the failed attempts + one call to clearThreadState
1145
+ verify (afterRollbackProcessor , times (3 )).processBatch (any (), any (), any (), any (), any (), anyBoolean (), any ());
1146
+ verify (afterRollbackProcessor ).clearThreadState ();
1147
+
1148
+ container .stop ();
1149
+ }
1150
+
1085
1151
}
0 commit comments