Atomikos Forum |
|
I'm using atomikos as JTA implementation on Tomcat and Oracle as Database. When I open an XA Transaction enlisting 2 or more datasources and performing only readonly operations (selects and/or updates/deletes that don't affect any rows) atomikos leaks all connections I used in this transaction. They are never released or reaped back to be available in the ConnectionPool.
Debugging atomikos code I noticed that it happens because in the ActiveStateHandler.commit() method, when the call to prepare() returns Participant.READ_ONLY, it doesn't call commitWithAfterCompletionNotification(). I understand that it makes sense not running the second phase if the first says that it's a readonly transaction, but it has two side effects: 1. AtomikosConnectionProxy.afterCompletion() is never called and thus the connection is not released. 2. This transaction state is never changed from BranchEndedStateHandler to TerminatedStateHandler, which makes the reap method to not release this connection during maintenance as well.
Just one more information, other unexpected behavior I noticed in this case is that although the ConnectionPool.reap() method is unable to release these connections it still updates the lastReleased timestamp, what makes it look like the connections are still in use if you look at the debug logs when the maintenance runs.
Yes. If you have only one participant in a transaction atomikos always do one phase commit and there's issue in this case.
We fixed the issue (by notifying all synchronizations after a readonly transaction) and rebuilt transactions.jar. Now I'm waiting for a reply from Atomikos team in order to make the fix available to the community.
I'm using Atomikos Essentials 3.9.3 and Oracle ojdbc6 driver (11.2.0.4). It's pretty easy to reproduce the issue in this version. I can send you a test code if you want.
The only thing you need to make sure is that at the end of ActiveStateHandler.prepare() method, where you have the following test, allReadOnly is true: // here we are if all yes. if ( allReadOnly ) { Then it's going to change the handler to TerminatedStateHandler and return. The method that called it, (CoordinatorImp.terminate) is going to get Participant.READ_ONLY as response and not to call commit(). |