05.31.2016 | Andreas Günzel | comment icon

Fighting the Transaction Reaper

Long running transactions might be killed by the App Server’s Transaction Reaper. I have implemented a litte helper to programmatic handle such kinds of exceptions.

The problem: Timed out translations

Transactions are a great invention. But sometimes they can be really annoying. In my current project we have a service call that takes some seconds – normally. However, in rare cases the call took too long and the enclosing transaction run into a timeout. To be more precise, the Application Server (JBoss EAP 6 in that case) cancelled the transaction which can be seen in the log file:

As you can see, the Transaction Reaper hooks in after the transaction was “finished” in view of the executing thread. So, after completion of the service invocation we just got an ugly exception in the log file:

This leads to a problem: If a call takes too long, the service invocation seems to complete correctly but the results are dropped anyway by the Application Server. In our concrete scenario we wanted to inform the operations team about that incident. Unfortunately, we did not reach the information about the timeout in our business code.

The Solution

My first idea was to inject EJBContext and check transaction status by calling getRollbackOnly(). This resulted in an IllegalStateException telling me that the transaction is already finished. Calling getUserTransactio() is permitted also since we use container managed transactions.

So, I used TransactionSynchronizationRegistry (which is part of JTA API). Listing 1 shows how to find out if the current transaction is going to be rolled back.

Having this, I wrote a small EJB called Worker that encapsulates the transaction handling. Listing 2 shows the final implementation.

I used the command pattern to separate the business logic. So, the concrete service call will be implemented in method Command#execute(). The worker runs this code in a new transaction. When execution is done we check for possible rollbacks and invoke callback method Command#onRollback() – again this implementation is service specific. In our scenario we sent an E-Mail. The call to execute the rollback code runs in another new transaction. Usage of SessionContext is based on a hack described in Paulin’s post.

Happy Coding,
Andreas

ejb java ee JTA SessionContext transaction