Calling rollback_only via OTS is throwing the wrong exception
mmusgrov Jun 10, 2010 11:00 AMWe have an intermittent and rare scenario whereby calling rollback_only is throwing a CORBA::SystemException with major and minor codes INVALID_TRANSACTION and INACTIVE_TRANSACTION respectively.
According to the OTS spec we should only get this error if the transaction has already been prepared which in our scenario is not the case:
interface CosTransactions::Coordinator
rollback_only
The transaction associated with the target object is modified so that the only possible
outcome is to rollback the transaction. The Inactive exception is raised if the
transaction has already been prepared.
We should be getting success or CORBA::OBJECT_NOT_EXIST (if it has already been reaped or presumed abort).
The log output indicates that the Transaction Reaper is reaping the transaction at the same time we are marking it as rollback only:
16:11:10,296 WARN [arjLoggerI18N] [com.arjuna.ats.arjuna.coordinator.TransactionReaper_18] - TransactionReaper::check timeout for TX -53ee7cfb:10e2:4c0fa834:22ec in state RUN
[exec] 2010-06-09 16:11:10,296 [0x0000123c] WARN (TxLogControl :153 ) - rollback_only: INVALID_TRANSACTION minor: 20001
16:11:10,328 WARN [arjLoggerI18N] [com.arjuna.ats.arjuna.coordinator.TransactionReaper_7] - TransactionReaper::doCancellations worker Thread[Thread-18,5,jboss] successfully canceled TX -53ee7cfb:10e2:4c0fa834:22ec
The second log message is issued from a C client using the OTS inteface and the other two messages are comming from the Transaction manager running in the JBoss app server.
The scenario is sufficiently rare that it is hard for us to turn up the logging levels to get more infomation.
However, manually checking the code does suggest a mechanism for the error to manifest:
Reaper calls cancel (see doCancellations())
ControlWrapper.cancel()
calls ControlWrapper.rollback()
calls ArjunaTransactionImple.rollback()
calls destroyAction()
calls OTSManager.destroyControl(controlHandle);
calls ControlImple.destroy()
the last call sets ControlImple _transactionHandle = null
*** Now, here is the rub: any future call to ControlImple.getImplHandle() will return null. In particular calling ControlWrapper.rollback_only() catches the null pointer and it, in turn, will throw Inactive();
So now BlackTie calls rollback_only on its CosTransactions::Coordinator which is handled by the transaction manager as follows:
CurrentPOATie.rollback_only() receives the CORBA invocation and calls rollback_only on its _delegate which is an instance of com.arjuna.ats.internal.jts.orbspecific.CurrentImple which in turn gets the current control wrapper:
ControlWrapper currentAction = _theManager.current();
and calls currentAction.rollback_only();
but, see the comment above about this throwing Inactive(), CurrentImple.rollback_only catches Inactive and throws the CORBA exception we are seeing in our C client:
} catch (org.omg.CosTransactions.Inactive exc) {
throw new INVALID_TRANSACTION(
ExceptionCodes.INACTIVE_TRANSACTION,
CompletionStatus.COMPLETED_NO);
}
What is unclear is what precisely is the race condition involved in this scenario.