|
28 | 28 | import static org.springframework.kafka.test.assertj.KafkaConditions.key; |
29 | 29 | import static org.springframework.kafka.test.assertj.KafkaConditions.value; |
30 | 30 |
|
| 31 | +import java.util.Collections; |
31 | 32 | import java.util.Iterator; |
32 | 33 | import java.util.Map; |
33 | 34 | import java.util.concurrent.BlockingQueue; |
| 35 | +import java.util.concurrent.atomic.AtomicBoolean; |
34 | 36 |
|
35 | 37 | import org.apache.kafka.clients.consumer.Consumer; |
36 | 38 | import org.apache.kafka.clients.consumer.ConsumerConfig; |
|
51 | 53 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
52 | 54 | import org.springframework.context.annotation.Bean; |
53 | 55 | import org.springframework.context.annotation.Configuration; |
| 56 | +import org.springframework.kafka.support.TransactionSupport; |
54 | 57 | import org.springframework.kafka.support.transaction.ResourcelessTransactionManager; |
55 | 58 | import org.springframework.kafka.test.rule.KafkaEmbedded; |
56 | 59 | import org.springframework.kafka.test.utils.KafkaTestUtils; |
@@ -234,6 +237,58 @@ public void testTransactionSynchronizationExceptionOnCommit() { |
234 | 237 | assertThat(producer.closed()).isTrue(); |
235 | 238 | } |
236 | 239 |
|
| 240 | + @Test |
| 241 | + public void testExcecuteInTransactionNewInnerTx() { |
| 242 | + @SuppressWarnings("unchecked") |
| 243 | + Producer<Object, Object> producer1 = mock(Producer.class); |
| 244 | + @SuppressWarnings("unchecked") |
| 245 | + Producer<Object, Object> producer2 = mock(Producer.class); |
| 246 | + producer1.initTransactions(); |
| 247 | + AtomicBoolean first = new AtomicBoolean(true); |
| 248 | + |
| 249 | + DefaultKafkaProducerFactory<Object, Object> pf = new DefaultKafkaProducerFactory<Object, Object>( |
| 250 | + Collections.emptyMap()) { |
| 251 | + |
| 252 | + @Override |
| 253 | + protected Producer<Object, Object> createTransactionalProducer() { |
| 254 | + return first.getAndSet(false) ? producer1 : producer2; |
| 255 | + } |
| 256 | + |
| 257 | + @Override |
| 258 | + Producer<Object, Object> createTransactionalProducerForPartition() { |
| 259 | + return createTransactionalProducer(); |
| 260 | + } |
| 261 | + |
| 262 | + }; |
| 263 | + pf.setTransactionIdPrefix("tx."); |
| 264 | + |
| 265 | + KafkaTemplate<Object, Object> template = new KafkaTemplate<>(pf); |
| 266 | + template.setDefaultTopic(STRING_KEY_TOPIC); |
| 267 | + |
| 268 | + KafkaTransactionManager<Object, Object> tm = new KafkaTransactionManager<>(pf); |
| 269 | + |
| 270 | + try { |
| 271 | + TransactionSupport.setTransactionIdSuffix("testExcecuteInTransactionNewInnerTx"); |
| 272 | + new TransactionTemplate(tm).execute(s -> { |
| 273 | + return template.executeInTransaction(t -> { |
| 274 | + template.sendDefault("foo", "bar"); |
| 275 | + return null; |
| 276 | + }); |
| 277 | + }); |
| 278 | + |
| 279 | + InOrder inOrder = inOrder(producer1, producer2); |
| 280 | + inOrder.verify(producer1).beginTransaction(); |
| 281 | + inOrder.verify(producer2).beginTransaction(); |
| 282 | + inOrder.verify(producer2).commitTransaction(); |
| 283 | + inOrder.verify(producer2).close(); |
| 284 | + inOrder.verify(producer1).commitTransaction(); |
| 285 | + inOrder.verify(producer1).close(); |
| 286 | + } |
| 287 | + finally { |
| 288 | + TransactionSupport.clearTransactionIdSuffix(); |
| 289 | + } |
| 290 | + } |
| 291 | + |
237 | 292 | @Configuration |
238 | 293 | @EnableTransactionManagement |
239 | 294 | public static class DeclarativeConfig { |
|
0 commit comments