I have hit a race condition where the EJB interceptors and the reaper are both aborting a
transaction simultaneously. If the reaper doesn't finish rollback before ejb thread
enters it then the ejb thread gets INVALID_TRANSACTION (via ArjunaTransactionImple) (even
though the transaction is still on the thread). ArjunaTransactionImple knows that the
status is ActionStatus.ABORTED but elects to throw INVALID_TRANSACTION. By changing the
code to throw TRANSACTION_ROLLEDBACK instead the EJB interceptor thread can correctly
reason about the aborted transaction.
More specifically, here are the two possible outcomes depending on the order in which both
threads execute com.arjuna.ats.internal.jts.ControlWrapper code:
Scenario 1:
* reaper executes ControlWrapper#rollback method first
* server executes ControlWrapper#rollback; line _controlImpl.getImplHandle().rollback()
throws an NullPointerException as ArjunaTransactionImple has been already detached
exception is caught and TransactionRolledbackException is thrown
Scenario 2:
* both threads enter ControlWrapper#rollback at the same time
* server thread obtains ArjunaTransactionImple; transaction is in ABORTED state so
InvalidTransaction exception is thrown
Attempt to synchronize the code:
synchronized (_controlImpl) {
_controlImpl.getImplHandle().rollback();
}
on _controlImpl object doesn't work as it introduces possible deadlock to the code.
Another possibility would be to check transaction status inside
ArjunaTransactionImple#rollback method and throw TransactionRolledbackException when
transaction is already aborted. I have tested such fix in my branch and it fixes the
initial error - TransactionRolledbackException is thrown consistently.
Is such fix possible to introduce? Are there some problems that it may introduce?
--
Tomasz Adamski
Software Engineer
JBoss by Red Hat