2 Replies Latest reply on Jul 31, 2019 6:28 AM by hchen00

    Wildfly Transaction REQUIRES_NEW not working

    hchen00

      We recently upgraded from jboss-eap-6.4 to Wildfly 16. Most things worked fine until we find one issue in the case that a method that is called using TransactionTemplate with TransactionDefinition.PROPAGATION_REQUIRES_NEW is not working...

       

      The case is that:  we use this PROPAGATION_REQUIRES_NEW transaction to commit a log of login failure, while the main transaction is rolled back due login exception.

      This used to work without problem in jboss-eap-6.4 though we were seeing warnings like:

        Trying to change transaction TransactionImple < ac, BasicAction: 0:ffff0a004b01:513406f2:5d37ce0b:2a0 status: ActionStatus.RUNNING > in enlist!

       

        But in Wildfly 16 PROPAGATION_REQUIRES_NEW doesn't work any more  -- the new transaction seems to rolled back together with the main transaction. After spending quite some time debugging into the Wildfly/Jboss code, I found that in org.jboss.jca.core.connectionmanager.listener.TxConnectionListener.enlist method, there is a new line:

        if (isEnlisted() || getState().equals(ConnectionState.DESTROY) || getState().equals(ConnectionState.DESTROYED))

               return;e

       

      And in our case, the TxConnectionListener object is the same in the new transaction as in the old transaction, so it's already marked as "enlisted". And therefore the rest of enlist is never executed for the new transaction, so Wildfly never issued a "XA Start" command for the new transaction.

       

      So the question is:  is the added  isEnlisted() check a bug introduced or there is something wrong we did to configure Wildfly and/or Spring?  How should the REUIRES_NEW case work?

       

      Here is a trace except I captured:

       

      TransactionTemplate.execute(TransactionCallback<T>) line: 130 

        JtaTransactionManager(AbstractPlatformTransactionManager).getTransaction(TransactionDefinition) line: 353 

          JtaTransactionManager(AbstractPlatformTransactionManager).handleExistingTransaction(TransactionDefinition, Object, boolean) line: 433 

          JtaTransactionManager.doBegin(Object, TransactionDefinition) line: 831 

        JtaTransactionManager.doJtaBegin(JtaTransactionObject, TransactionDefinition) line: 872 

          LocalUserTransaction.begin() line: 48 

          ContextTransactionManager.begin(CreationListener$CreatedBy) line: 62 

            LocalTransactionContext.beginTransaction(int, boolean, CreationListener$CreatedBy) line: 188 

            JBossJTALocalTransactionProvider(JBossLocalTransactionProvider).createNewTransaction(int) 

            ...

          ContextTransactionManager.resume(AbstractTransaction) line: 158 

            LocalTransaction.resume() line: 248 

            TransactionManagerDelegate(BaseTransactionManagerDelegate).resume(Transaction) line: 122 

              TransactionManagerImple.resume(Transaction) line: 111 

              AtomicAction.resume(AtomicAction) line: 361 

            LocalTransaction(AbstractTransaction).notifyAssociationListeners(boolean) line: 115 

              TransactionManagerService$2.associationChanged(AbstractTransaction, boolean) line: 97 

              UserTransactionRegistry.userTransactionStarted() line: 119 

                UserTransactionListenerImpl.userTransactionStarted() line: 52 

                CachedConnectionManagerImpl.userTransactionStarted() line: 249 

                  TxConnectionManagerImpl.transactionStarted(Collection<ConnectionRecord>) line: 460

                  TxConnectionListener.enlist() line: 264 

                    !-- changed in ironjacamar-core-impl-1.2

                    // If we are already enlisted there is no reason to check again, as this method

                    // could be called multiple times during a transaction lifecycle.

                    // We know that we can only be inside this method if we are allowed to

                    if (isEnlisted() || getState().equals(ConnectionState.DESTROY) || getState().equals(ConnectionState.DESTROYED))

                     return;