Daniel Szalay
Daniel Szalay

Reputation: 4101

Camel: rollback jdbc transaction if consumer cannot acknowledge

I have a backend application which consumes messages using AMQ/Camel. During each message processing a database insert is done using a @Transactional JDBC DAO. I have two message brokers configured.

The problem: If the main broker is stopped, the current exchanges become stale in the consumers: some of them already did the insert, but didn't yet send an ACK back to the broker. In the end the broker will do a message rollback, and the application then gets the same messages again resulting in database errors since they are already inserted.

How can I configure the route so that it attempts to rollback in case the connection is broken? Is this only possible if I manage transactions programmatically?

Upvotes: 1

Views: 1187

Answers (1)

gogstad
gogstad

Reputation: 3739

First of all @Transactional on the DAO doesn't make sense. You want your to commit to the DB when you're finished with the request. For web applicaiton you usually have the transaction boundaries on the controller. For JMS consumers, you have it on the consumer (or near). That way your transaction boundaries encompass your business logic as well as the persisting logic. Once the thread passes the transaction boundary, the transaction manager for that boundary will commit (at least when its managing one resource, like a DB), and there is no way to rollback after this.

That said, managing transactions with several data sources is generally a hard problem. You will in most cases find it easier to make a system resilient to repeated messages than to synchronize commits to multiple data sources (e.g. persist a message id and look it up before you start processing a new message).

You have some options though:

  1. When I consume from JMS data sources and persists to databases, I usually have the transaction boundaries for the databases as high up in the route as possible. You can either have .transacted() as one of the first instructions, or rather you can mark the JMS consumer with transacted=true, and then hook in a transaction manager that commits to the database. You may still rollback the broker while commiting to the database, so you need some logic to handle duplicate messages.
  2. If you really want to rollback the commit to the database if the commit to the broker fails, your only options is to use a transaction manager that implements XA-transactions. These transactions managers will implement two-phase commits (i.e. 1. ask all resources if they're ready for commit and 2. commit to all resources. If phase 1. fails, rollback all transactions.) If you decide to try for this, you must mark your transaction boundary at the camel endpoint, as this is when you want to commit to the broker and the DB.

XA-transactions will be a more complex solution than handling duplicate messages, and it will require more of your developers. You have been warned.

Upvotes: 1

Related Questions