3

I need to synchronize a message reception, saving in a database and sending of another message, all in a single transaction. I'm using Spring Boot and relying on its autoconfiguration. The message broker is Active MQ.

For my use case it's enough with best effort 1 phase commit, so I've configured a ChainedTransactionManager:

@EnableTransactionManagement
@Configuration
public class TransactionConfiguration {

    @Bean
    public PlatformTransactionManager chainedTransactionManager(DataSourceTransactionManager dtm,
                                                         JmsTransactionManager jtm) {
        return new ChainedTransactionManager(jtm, dtm);
    }

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

    @Bean
        public JmsTransactionManager jmsTransactionManager(ConnectionFactory connectionFactory) {
        return new JmsTransactionManager(connectionFactory);
    }

}

The application flow would be something like this:

    private final JmsTemplate jmsTemplate;
    private final MessageRepository messageRepository;

    @Transactional
    @JmsListener(destination = "someDestination")
    public void process(Message message){
        jmsTemplate.isSessionTransacted();
        //Save to DB
        messageRepository.save(message);
        //Send to another queue
        jmsTemplate.convertAndSend("anotherDestination", new ProcessedMessage(message));
    }

Inside the process() method I've seen that jmsTemplate.isSessionTransacted(); is false.

  1. Do I need to explicitly configure a JmsTemplate bean with setSessionTransacted(true) or it is enough with my current transaction configuration?

  2. What's the difference between configuring JmsTemplate.setSessionTransacted(true) and the usage of ChainedTransactionManager + JmsTransactionManager?

codependent
  • 23,193
  • 31
  • 166
  • 308
  • I'm a bit confused. If you really need to synchronize two JMS operations (not clear if that's using the same broker) and a database operation in a single transaction how is a "best effort 1 phase commit" sufficient? That won't actually be using a single transaction so the operations won't actually be atomic which means in the case of a failure you could have inconsistent data. You'd need 2-phase commit (i.e. XA) to ensure consistency. – Justin Bertram Sep 30 '19 at 03:03
  • Yes, the read/write AMQ broker is the same. This excellent article from Dave Syer describes why the 1PC scenario is enough in my business case: https://www.javaworld.com/article/2077963/distributed-transactions-in-spring--with-and-without-xa.html?page=2 The database transaction will be commited first. Next, if the jms transaction (JMS read and write) fails, this one will be rolledback. Then the message will be redelivered. My consumer is idempotent so if when the message is redelivered it won't be saved in DB, only sent to the destination queue. – codependent Sep 30 '19 at 07:03
  • FWIW, I think that information is valuable enough to add to the question to avoid confusion. The "single transaction" you mention is misleading since there will be multiple transactions (one for JDBC & one for JMS) rather than a single one (as when using XA). – Justin Bertram Sep 30 '19 at 15:49
  • I raised similar issue: https://stackoverflow.com/q/58491469/2365727 – michaldo Oct 21 '19 at 17:38

0 Answers0