Replacing XA transactions with the idempotent consumer pattern

Solution Verified - Updated -

Environment

  • JBoss Fuse 6.1

Issue

In our application we have a lot of routes that follow the same pattern:
a) read a message, do some processing
b) update/insert in the database
c) send a message to the next route

Coming from the web-applications world we all thought that we would need XA transactions to make sure we never lose messages. Do we need XA transactions or can we ensure that we don't lose messages by some usage of errorhandler and/or properties on the route or endpoints?

Resolution

In some cases you can replace XA transactions with the indepotent-based processing. In the particular example described in this KCS, you don't need XA to transactionally process the message flow. Plain local JMS transaction, ActiveMQ reredelivery mechanism and some Camel glue will be fine here.

Just mark consumer endpoint as transactional [1] (as demonstrated on the snippet below) and ActiveMQ will handle the message redelivery for you.

from("jms:in").transacted().
  to("bean:myDbBean?method=dbAction").
  to("jms:out");

Keep in mind that ActiveMQ broker will keep the message in the input queue until the transaction will be committed by the Camel at the end of the route. If myDbBean will throw the exception during the DB update, the Camel will not commit and message incoming from the queue will be redelivered by the broker. You can control how many times message will be redelivered by the broker on the ActiveMQ configuration level [2].

Notice that it is still possible that Camel can fail to commit JMS transaction after successful DB update (for example due to the power outage). That would mean that incoming message will be redelivered and processed twice. To handle this situation and keep your system state concise make myDbBean#dbAction() bean invocation idempotent [3], so it won't try to alter database twice. You can also try to make myDbBean#dbAction() operation idempotent on the business logic level (sometimes it is as trivial as checking if entity with given ID hasn't been persisted already). Keep also in mind that even if the DB service itself is not idempotent, you can usually still make it such be using Camel's idempotent consumer [3] pattern.

You should consider resigning from XA whenever it is possible. XA transactions come with really heavy performance, latency and scalability penalty. In case of Camel it is not only the matter of the raw XA performance issues, but also JMS component [4] performance (Camel can't cache JMS consumers while using XA).

[1] http://camel.apache.org/transactional-client.html
[2] http://activemq.apache.org/message-redelivery-and-dlq-handling.html
[3] http://camel.apache.org/idempotent-consumer.html
[4] http://camel.apache.org/jms.html

This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.

Comments