Memory Leak in JBossLocalTransactionProvider
a.durussel Jun 1, 2018 7:49 AMHi,
We have analyzed a case where two Jboss server are implicated in a remote EJB transaction.
The scenario is the following :
Java standalone client -------(EJB CALL on server 1)------> Server 1 start transaction, enlist XA ressource and call server 2 ------(EJB CALL to server 2)-------> Server 2 enlist subordinate transaction and return
The scenario works as expected and the XA transaction is committed correctly. However on Server 2 the subordinate transaction is stored at two levels:
1. inside a Map (named known) in Class org.wildfly.transaction.client.provider.jboss.JBossLocalTransactionProvider.java during call to method findOrImportTransaction.
final class XAImporterImpl implements XAImporter {
public ImportResult<Transaction> findOrImportTransaction(final Xid xid, final int timeout, final boolean doNotImport) throws XAException {
try {
final SimpleXid simpleXid = SimpleXid.of(xid);
final SimpleXid gtid = simpleXid.withoutBranch();
final ConcurrentMap<SimpleXid, Entry> known = JBossLocalTransactionProvider.this.known;
Entry entry = known.get(gtid);
if (entry != null) {
return new ImportResult<Transaction>(entry.getTransaction(), entry, false);
}
final boolean imported;
Transaction transaction;
if (doNotImport) {
imported = false;
transaction = ext.getTransaction(xid);
if (transaction == null) {
return null;
}
} else {
final TransactionImportResult result = ext.importTransaction(xid, timeout);
transaction = result.getTransaction();
imported = result.isNewImportedTransaction();
}
entry = getEntryFor(transaction, gtid);
final Entry appearing = known.putIfAbsent(gtid, entry);
if (appearing != null) {
// even if someone else beat us to the map, we still might have imported first... preserve the original Entry for economy though
return new ImportResult<Transaction>(transaction, appearing, imported);
} else {
return new ImportResult<Transaction>(transaction, entry, imported);
}
} catch (XAException e) {
throw e;
} catch (Throwable t) {
throw Log.log.resourceManagerErrorXa(XAException.XAER_RMFAIL, t);
}
}
2. inside another Map (named _transaction) in Class com.arjuna.ats.internal.jta.transaction.arjunacore.jca.TransactionImporterImple.java during call to method importTransaction of class org.jboss.tm.ExtendedJBossXATerminator (see above).
When the transaction is commited references to this subordinate transaction are kept in both Map detailed above.
A mechanism of cleanup based on timeout is implemented to remove old transaction in the Map (named known) in class org.wildfly.transaction.client.provider.jboss.JBossLocalTransactionProvider.java
but the map (named _transaction) is never clean up. This leads to a memory leak if server is never restarted.
To my understanding, it seems that at the commit phase of this subordinate transaction, we should call the removeImportedTransaction method of interface ExtendedJBossXATerminator to remove the subordinate transaction from the map.
Can somebody confirm this analyze ?
Thanks in advance for your time.