Spring Kafka
Kafka topics
transaction handling
programming
application development

Write to two Kafka topics in a single transaction using Spring Kafka

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Apache Kafka is a powerful distributed event streaming platform capable of handling trillions of events a day. Initiating transactions across multiple Kafka topics ensures that either all messages in the transaction are committed or none of them. Spring Kafka abstracts some of the complexity of using Kafka behind a simpler API. In a typical Spring application, you can perform transactions across multiple Kafka topics easily using Spring Kafka’s transactional capabilities.

Fundamental Concepts of Kafka and Transactions

In Kafka, a transaction ensures that messages sent to different topics or partitions either all are committed or none are. This is critical for maintaining data consistency in distributed systems and is supported in Kafka using the concept of exactly-once semantics.

Transactions in Kafka are used to ensure that:

  • Messages can span multiple Kafka topics and partitions.
  • A group of messages are consumed and produced atomically.

To support transactions, Kafka uses a special transactional.id which uniquely identifies the transaction across the cluster. This ensures that retries due to network errors or other issues do not result in duplicated transactions.

Setup and Configuration in Spring Kafka

Setting up a transactional producer in Spring Kafka involves several key configurations and annotations. Below are the essential steps and configurations required:

  1. Enable Kafka Transactions: To enable transactional support, you need to set the spring.kafka.producer.transaction-id-prefix. This prefix will be used by Spring Kafka to configure each transaction ID uniquely.
  2. KafkaTransactionManager: Spring Kafka provides KafkaTransactionManager which manages Kafka transactions. It binds a producer to the thread running the @Transactional method.
  3. @EnableKafka: This annotation is added to configuration classes to enable Kafka listeners and transaction management.

Here is an example configuration class in Spring Kafka:

java
1@Configuration
2@EnableKafka
3public class KafkaConfig {
4
5    @Bean
6    public ProducerFactory<String, String> producerFactory() {
7        Map<String, Object> configProps = new HashMap<>();
8        configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
9        configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
10        configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
11        configProps.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);
12        configProps.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "prod-1");
13        return new DefaultKafkaProducerFactory<>(configProps);
14    }
15
16    @Bean
17    public KafkaTemplate<String, String> kafkaTemplate() {
18        return new KafkaTemplate<>(producerFactory());
19    }
20
21    @Bean
22    public KafkaTransactionManager<String, String> transactionManager(ProducerFactory<String, String> producerFactory) {
23        return new KafkaTransactionManager<>(producerFactory);
24    }
25}

Writing to Multiple Topics

To write to multiple Kafka topics within a single transaction, you use the KafkaTemplate provided by Spring Kafka. Here's how you might do it:

java
1@Service
2public class KafkaProducerService {
3
4    @Autowired
5    private KafkaTemplate<String, String> kafkaTemplate;
6
7    @Transactional
8    public void sendMessagesInTransaction() {
9        kafkaTemplate.send("topic1", "Key1", "Data for Topic1");
10        kafkaTemplate.send("topic2", "Key2", "Data for Topic2");
11        // The transaction is committed if no exceptions occurred.
12    }
13}

In this method, @Transactional ensures that these operations are part of a Kafka transaction. If any send operation or any other operation in the method throws an exception, the whole transaction will be rolled back, and none of the sends will be committed.

Key Points Summary

FeatureDescription
Transaction managementManaged by KafkaTransactionManager.
ConfigurationRequires setting up transaction-id-prefix and enabling idempotence.
KafkaTemplateUsed to send messages to different topics atomically within a transaction.
@TransactionalThis annotation ensures that method execution is within a transaction boundary.
RollbackOn failure, all messages are rolled back to maintain atomicity and consistency.

Conclusion

Using Spring Kafka to manage transactions across multiple topics enhances the robustness and consistency of distributed systems. Proper configuration and the use of Spring’s @Transactional ensures that your messages are either completely successful or completely rolled back, maintaining the atomic nature of your operations across multiple Kafka topics.


Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.