5

I Want to write one new application with Spring boot using the database as MySQL + Mango and for messaging Spring Kafka.

I tried with Many POC for synchronizing the transaction between Kafka and DB but I failed in certain conditions and also I searched many Repositories, blogs to get at least one example. I didn't get any example still now.

if anyone gives at least one example or configurations it would be a nice reference in the future for all.

Joyson Rego
  • 968
  • 2
  • 11
  • 29

1 Answers1

5

Here you go...

@SpringBootApplication
public class So56170932Application {

    public static void main(String[] args) {
        SpringApplication.run(So56170932Application.class, args);
    }

    @Bean
    public ApplicationRunner runner(KafkaTemplate<String, String> template) {
        return args -> template.executeInTransaction(t -> t.send("so56170932a", "foo"));
    }

    @Bean
    public ChainedKafkaTransactionManager<Object, Object> chainedTm(KafkaTransactionManager<String, String> ktm,
            DataSourceTransactionManager dstm) {

        return new ChainedKafkaTransactionManager<>(ktm, dstm);
    }

    @Bean
    public DataSourceTransactionManager dstm(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public ConcurrentKafkaListenerContainerFactory<?, ?> kafkaListenerContainerFactory(
            ConcurrentKafkaListenerContainerFactoryConfigurer configurer,
            ConsumerFactory<Object, Object> kafkaConsumerFactory,
            ChainedKafkaTransactionManager<Object, Object> ctm) {

        ConcurrentKafkaListenerContainerFactory<Object, Object> factory = new ConcurrentKafkaListenerContainerFactory<>();
        configurer.configure(factory, kafkaConsumerFactory);
        factory.getContainerProperties().setTransactionManager(ctm);
        return factory;
    }

    @Component
    public static class Listener {

        private final JdbcTemplate jdbcTemplate;

        private final KafkaTemplate<String, String> kafkaTemplate;

        public Listener(JdbcTemplate jdbcTemplate, KafkaTemplate<String, String> kafkaTemplate) {
            this.jdbcTemplate = jdbcTemplate;
            this.kafkaTemplate = kafkaTemplate;
        }

        @KafkaListener(id = "so56170932a", topics = "so56170932a")
        public void listen1(String in) {
            this.kafkaTemplate.send("so56170932b", in.toUpperCase());
            this.jdbcTemplate.execute("insert into so56170932 (data) values ('" + in + "')");
        }

        @KafkaListener(id = "so56170932b", topics = "so56170932b")
        public void listen2(String in) {
            System.out.println(in);
        }

    }

    @Bean
    public NewTopic topicA() {
        return TopicBuilder.name("so56170932a").build();
    }

    @Bean
    public NewTopic topicB() {
        return TopicBuilder.name("so56170932b").build();
    }

}

and

spring.datasource.url=jdbc:mysql://localhost/integration?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.enable-auto-commit=false
spring.kafka.consumer.properties.isolation.level=read_committed

spring.kafka.producer.transaction-id-prefix=tx-

logging.level.org.springframework.transaction=trace
logging.level.org.springframework.kafka.transaction=debug
logging.level.org.springframework.jdbc=debug

and

mysql> select * from so56170932;
+------+
| data |
+------+
| foo  |
| foo  |
| foo  |
| foo  |
| foo  |
| foo  |
| foo  |
| foo  |
| foo  |
+------+
9 rows in set (0.00 sec)
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Thanks @GaryRussell for Example. I have 2 questions. – Joyson Rego May 16 '19 at 17:42
  • 1. im creating multiple instances in docker for this application so the transaction prefix id should be same for each instance or different? 2. im going to synchronize 4 transaction (DataSourceTransactionManager,PlatformTransactionManager - default for entity manager, JpaTransactionManager and KafkaTransactionManager), is it fine? – Joyson Rego May 16 '19 at 17:48
  • 1. In order to properly handle zombie fencing, they should be the same; the actual transaction id consists of the prefix, consumer group, topic and partition. Then, if an instance goes down, Kafka can properly deal with a partition moving to another instance after a rebalance. 2. Yes; you have to decide the order though; normally you'll want Kafka first (so its commit is last) in order to avoid lost messages after a failure; your code will need to handle the possibility of duplicate deliveries that have committed to one or more DBs. – Gary Russell May 16 '19 at 19:02
  • I have one Kafka broker in docker and if I connected multiple spring -boot instances with the same transaction-id-prefix then it's giving producedFencedException occasionally and also transaction will not work. but if I give different transaction-id-prefix then its work fine. why it's like that? is there anything which I am missing! – Joyson Rego May 18 '19 at 14:58
  • If you are producing messages on a listener container thread, the `transactional.id` is `..`. Since a partition cannot be assigned to multiple instances, the `transactional.id`s will be unique. If you are producing messages outside of the context of a container thread, the `transactional.id` (and hence prefix) must be unique across instances. If you are doing both, you will need 2 distinct producer factories. If this doesn't answer your question, ask a new question showing your code and configuration. – Gary Russell May 18 '19 at 15:43
  • Thanks @garyRussell. I will create a new question with config and code – Joyson Rego May 18 '19 at 16:25
  • Does this work with `@Transactional` methods? In the case I want to save something to DB and then send a message through Kafka – Alessandro Dionisi Jun 05 '19 at 15:16
  • Don't ask new questions in comments - it doesn't help people find questions and answers; always ask a new question. Yes, `@Transactional` methods will work; it will synchronize the two transactions. If you have problems, ask a new question, showing configuration. – Gary Russell Jun 05 '19 at 17:07