jmsactivemq-classicjava-ee-6

Why is the MDB consumer picking up the message from ActiveMQ again?


I have an MDB consumer with settings

@MessageDriven(name = "PackageReceiver", activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationLookup",
                                  propertyValue = "myQueue"),
        @ActivationConfigProperty(propertyName = "destinationType",
                                  propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(propertyName = "acknowledgeMode",
                                  propertyValue = "Auto-acknowledge")
})

The message processing process can last from a few seconds to several hours.

I understand that some kind of timeout is triggered during which there is no acknowledge for the broker (there is no transaction commit)

Also I understand that there are only two acknowledge modes for MDB: Auto-acknowledge and Dups-ok-acknowledge. For both options, the message is in the queue until the end of processing the onMessage() method, and using message.acknowledge() does not have the expected effect. Please, help me solve this problem!


Solution

  • The onMessage() method of an MDB executes in the context of a JTA transaction by default. In this situation the acknowledgeMode is ignored. The message is only acknowledged when the JTA transaction is committed (which is done implicitly when onMessage() returns or can be done explicitly using javax.ejb.EJBContext.getUserTransaction()).

    What's likely happening is that when your MDB takes a long time the JTA transaction is timing out and rolling back which causes the message to go back to the queue and get redelivered.

    To resolve this you could extend the transaction timeout (which depends on your application server) or disable JTA transations on your onMessage() (e.g. using @javax.ejb.TransactionAttribute(javax.ejb.TransactionAttributeType.NOT_SUPPORTED). Long-running transactions are generally considered an anti-pattern therefore I would recommend against extending the timeout. However, if you're working with any other transactional resources (e.g. JDBC, JMS, etc.) in your onMessage() then disabling JTA transactions could compromise the integrity of your data. Ultimately how you proceed will depend on your specific use-case.