简体   繁体   中英

JMS message not rolled back after connection lost during 2PC prepare

I am trying to wrap my head arround the following issue:

TL;DR How can I make sure a rollback occurs when connection is lost to the queue manager during the prepare of the 2PC.

  • java: 8
  • spring boot: 2.3.12.RELEASE
  • spring-jms: 5.2.20.RELEASE
  • mq-jms-spring-boot-starter: 2.6.5 (allclient: 9.2.5.0)
  • narayana spring boot starter: 2.6.3 (narayana: 5.11.3.Final)
  • sniffy: 3.1.12

==========================================================

  1. Application is reading a persistent message (without expiry) from queue X in a transaction
  2. I set a breakpoint in the XAResouceRecord.topLevelPrepare and using Sniffy I cut the the connection to the queue manager during the 2PC prepare
  3. I wait more than the HBINT (300) time (or shutdown the VM to disconnect the TCP connections instantly)
  4. I expected the message to be available on the message queue X, it's back-out queue or a dead letter queue

However, the message is not rolled back. I don't see any transaction logs (I believe this is to be expected since the prepare did not finish). No uncommitted messages on the queue or back-out queue.

If I put a breakpoint in AbstractPollingMessageListenerContainer.receiveAndExecute after receiving the message but before the transaction is commited I can see that the message is no longer on the queue. So it appears if the session.commit has already happened. How can I make sure a rollback occurs when connection is lost to the queue manager during the prepare of the 2PC. I am probably missing something here but I can't seem what.

After some more digging I believe I found the issue. The message is now rolledback when I break the connection to the queue manager during the prepare of 2PC. Hopefully this might help somebody else.

In my question I mentioned putting a breakpoint in the AbstractPollingMessageListenerContainer.receiveAndExecute . In the spring version that I was using 5.2.20.RELEASE the code looked like this:

        if (this.transactionManager != null) {
            // Execute receive within transaction.
            TransactionStatus status = this.transactionManager.getTransaction(this.transactionDefinition);
            boolean messageReceived;
            try {
                messageReceived = doReceiveAndExecute(invoker, session, consumer, status);
            }
            catch (JMSException | RuntimeException | Error ex) {
                rollbackOnException(this.transactionManager, status, ex);
                throw ex;
            }
            this.transactionManager.commit(status);
            return messageReceived;
        }

Which appeared a bit odd since the transactionManager.commit was not surrounded by a try catch. So what was happening if the commit failed?

The try-catch was added in 5.3.16, see https://github.com/spring-projects/spring-framework/pull/1807

        if (this.transactionManager != null) {
            // Execute receive within transaction.
            TransactionStatus status = this.transactionManager.getTransaction(this.transactionDefinition);
            boolean messageReceived;
            try {
                messageReceived = doReceiveAndExecute(invoker, session, consumer, status);
            }
            catch (JMSException | RuntimeException | Error ex) {
                rollbackOnException(this.transactionManager, status, ex);
                throw ex;
            }
            try {
                this.transactionManager.commit(status);
            }
            catch (TransactionException ex) {
                // Propagate transaction system exceptions as infrastructure problems.
                throw ex;
            }
            catch (RuntimeException ex) {
                // Typically a late persistence exception from a listener-used resource
                // -> handle it as listener exception, not as an infrastructure problem.
                // E.g. a database locking failure should not lead to listener shutdown.
                handleListenerException(ex);
            }
            return messageReceived;
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM