2929import static org .springframework .kafka .test .assertj .KafkaConditions .value ;
3030
3131import java .util .Collections ;
32+ import java .util .HashMap ;
3233import java .util .Iterator ;
3334import java .util .Map ;
3435import java .util .concurrent .BlockingQueue ;
3839import org .apache .kafka .clients .consumer .ConsumerConfig ;
3940import org .apache .kafka .clients .consumer .ConsumerRecord ;
4041import org .apache .kafka .clients .consumer .ConsumerRecords ;
42+ import org .apache .kafka .clients .consumer .OffsetAndMetadata ;
4143import org .apache .kafka .clients .producer .Callback ;
4244import org .apache .kafka .clients .producer .MockProducer ;
4345import org .apache .kafka .clients .producer .Producer ;
4446import org .apache .kafka .clients .producer .ProducerConfig ;
4547import org .apache .kafka .clients .producer .ProducerRecord ;
48+ import org .apache .kafka .common .TopicPartition ;
4649import org .apache .kafka .common .serialization .StringDeserializer ;
4750import org .apache .kafka .common .serialization .StringSerializer ;
4851import org .assertj .core .api .Assertions ;
6467import org .springframework .transaction .support .AbstractPlatformTransactionManager ;
6568import org .springframework .transaction .support .TransactionTemplate ;
6669
70+ import kafka .server .KafkaConfig ;
71+
6772/**
6873 * @author Gary Russell
6974 * @since 1.3
@@ -73,8 +78,19 @@ public class KafkaTemplateTransactionTests {
7378
7479 private static final String STRING_KEY_TOPIC = "stringKeyTopic" ;
7580
81+ private static final String LOCAL_TX_IN_TOPIC = "localTxInTopic" ;
82+
83+ private static final Map <String , String > BROKER_PROPERTIES = new HashMap <>();
84+
85+ static {
86+ BROKER_PROPERTIES .put (KafkaConfig .TransactionsTopicReplicationFactorProp (), "1" );
87+ BROKER_PROPERTIES .put (KafkaConfig .TransactionsTopicMinISRProp (), "1" );
88+ }
89+
7690 @ ClassRule
77- public static KafkaEmbedded embeddedKafka = new KafkaEmbedded (3 , true , STRING_KEY_TOPIC );
91+ public static KafkaEmbedded embeddedKafka =
92+ new KafkaEmbedded (1 , true , STRING_KEY_TOPIC , LOCAL_TX_IN_TOPIC )
93+ .brokerProperties (BROKER_PROPERTIES );
7894
7995 @ Test
8096 public void testLocalTransaction () throws Exception {
@@ -87,13 +103,22 @@ public void testLocalTransaction() throws Exception {
87103 template .setDefaultTopic (STRING_KEY_TOPIC );
88104 Map <String , Object > consumerProps = KafkaTestUtils .consumerProps ("testTxString" , "false" , embeddedKafka );
89105 consumerProps .put (ConsumerConfig .AUTO_OFFSET_RESET_CONFIG , "earliest" );
106+ consumerProps .put (ConsumerConfig .ISOLATION_LEVEL_CONFIG , "read_committed" );
90107 DefaultKafkaConsumerFactory <String , String > cf = new DefaultKafkaConsumerFactory <>(consumerProps );
91108 cf .setKeyDeserializer (new StringDeserializer ());
92109 Consumer <String , String > consumer = cf .createConsumer ();
93- embeddedKafka .consumeFromAnEmbeddedTopic (consumer , STRING_KEY_TOPIC );
110+ embeddedKafka .consumeFromAllEmbeddedTopics (consumer );
111+ template .executeInTransaction (kt -> kt .send (LOCAL_TX_IN_TOPIC , "one" ));
112+ ConsumerRecord <String , String > singleRecord = KafkaTestUtils .getSingleRecord (consumer , LOCAL_TX_IN_TOPIC );
94113 template .executeInTransaction (t -> {
95114 t .sendDefault ("foo" , "bar" );
96115 t .sendDefault ("baz" , "qux" );
116+ t .sendOffsetsToTransaction (Collections .singletonMap (
117+ new TopicPartition (LOCAL_TX_IN_TOPIC , singleRecord .partition ()),
118+ new OffsetAndMetadata (singleRecord .offset () + 1L )), "testLocalTx" );
119+ assertThat (KafkaTestUtils .getPropertyValue (
120+ KafkaTestUtils .getPropertyValue (template , "producers" , ThreadLocal .class ).get (),
121+ "delegate.transactionManager.transactionalId" )).isEqualTo ("my.transaction.0" );
97122 return null ;
98123 });
99124 ConsumerRecords <String , String > records = KafkaTestUtils .getRecords (consumer );
@@ -106,6 +131,8 @@ public void testLocalTransaction() throws Exception {
106131 }
107132 record = iterator .next ();
108133 assertThat (record ).has (Assertions .<ConsumerRecord <String , String >>allOf (key ("baz" ), value ("qux" )));
134+ // 2 log slots, 1 for the record, 1 for the commit
135+ assertThat (consumer .position (new TopicPartition (LOCAL_TX_IN_TOPIC , singleRecord .partition ()))).isEqualTo (2L );
109136 consumer .close ();
110137 assertThat (KafkaTestUtils .getPropertyValue (pf , "cache" , BlockingQueue .class ).size ()).isEqualTo (1 );
111138 pf .destroy ();
0 commit comments