9 Replies Latest reply on Dec 20, 2009 5:23 AM by aloubyansky

    JBAS-7507: UserTransaction & Nested Transactions

    jiwils

      This thread is for discussing JBAS-7507 and its implementation.

      When nested transactions support is explicitly disabled (the default), we need to throwing a NotSupportedException if a remote (or even local) client calls UserTransaction.begin() twice on the same thread with no rollback/commit in between.

      A larger question is whether or not we should be supporting nested transactions at all. While Arjuna supports them, it seems that there is little practical application for them based on comments from Mark Little and others since not many resource managers support them. If we don't throw a NotSupportedException when the Arjuna nested transactions flag is enabled, then we have to ensure that code like the tx sticky load balancing policies support them (more work, and I imagine there are many other examples too).

      Dimitris asked that we discuss the implementation for the throwing of the NSE in the disabled case, and that's the focus of JBAS-7507, but I think there is potentially a larger question here too (that may be a different discussion, however).

        • 1. Re: JBAS-7507: UserTransaction & Nested Transactions
          jiwils

           

          "Dimitris Andreadis" wrote:
          Is org.jboss.tm.usertx.server.UserTransactionSession used solely for remote client UserTransactions or are there other call paths to it that would be valid otherwise, e.g. BMT in the server side implementing something like REQUIRES_NEW.

          Should throwing an exception be configurable in ClientUserTransactionService, with the default being 'true'?


          • 2. Re: JBAS-7507: UserTransaction & Nested Transactions
            dimitris

            Assuming we are going to throw a NotSupportedException, it's not clear to me where this exception should originate: On the client side or the server side?

             

            Can org.jboss.tm.usertx.server.UserTransactionSessionImpl detect an existing transaction is associated with the client-side thread?

            • 3. Re: JBAS-7507: UserTransaction & Nested Transactions
              vickyk

              I did spend some time looking at the code implemetation, I am yet to conclude things.

               

              I did see the ClientUserTransaction(CUT) being bound to JNDI here

               ctx.bind(JNDI_NAME, ClientUserTransaction.getSingleton());
              

               

              http://anonsvn.jboss.org/repos/jbossas/branches/JBPAPP_4_2/server/src/main/org/jboss/tm/usertx/server/ClientUserTransactionService.java

               

              At the remote client side I can get the CUT by looking at "UserTransaction" object e.g ctx.lookup("UserTransaction")

               

               

                   [java] org.jboss.tm.usertx.client.ClientUserTransaction@15663a2
              
              

               

              However when I make any invocation from ut it does not go through

              http://svn.jboss.org/repos/jbossas/branches/JBPAPP_4_2/server/src/main/org/jboss/tm/usertx/client/ClientUserTransaction.java

               

              but it invokes invoke(..) in

              http://anonsvn.jboss.org/repos/jbossas/branches/JBPAPP_4_2/server/src/main/org/jboss/tm/usertx/server/ClientUserTransactionService.java

               

              I am in process of understanding the flow yet, may be someone can give quick pointers .

              What am I missing in understanding the flow?

              • 4. Re: JBAS-7507: UserTransaction & Nested Transactions
                aloubyansky

                "Dimitris Andreadis" wrote:
                Is org.jboss.tm.usertx.server.UserTransactionSession used solely for remote client UserTransactions or are there other call paths to it that would be valid otherwise, e.g. BMT in the server side implementing something like REQUIRES_NEW.

                AFAICS it is used only for remote clients.

                EJBs use org.jboss.ejb.EnterpriseContext$UserTransactionImpl, servlets - org.jboss.tm.usertx.client.ServerVMClientUserTransaction.

                 

                None of these implementations checks whether there already exists an active transaction associated with the thread. So, if it has to be checked it has to be done in every impl or in the TransactionManager impl.

                 

                As to the REQUIRES_NEW or whatever call path that leads to a nested transaction, at the end it would still be a nested transaction which is not supported. So, there are two choices:

                1. throw an exception

                2. suspend the currently active transaction, start a new one and when it ends resume the suspended one, like it's done in CMT for REQUIRES_NEW.

                • 5. Re: JBAS-7507: UserTransaction & Nested Transactions
                  aloubyansky

                  I guess for remote clients it could be fixed in two places:

                  - UserTransactionSessionImpl on the serverside

                  - or probably even better on the client side in ClientUserTransaction

                   

                  C:\Users\avoka\svn.jboss.org\jbossas\branches\JBPAPP_4_2_0_GA_CP\server>svn diff
                  Index: src/main/org/jboss/tm/usertx/server/UserTransactionSessionImpl.java
                  ===================================================================
                  --- src/main/org/jboss/tm/usertx/server/UserTransactionSessionImpl.java (revision 93372)
                  +++ src/main/org/jboss/tm/usertx/server/UserTransactionSessionImpl.java (working copy)
                  @@ -44,6 +44,7 @@
                   import org.jboss.logging.Logger;
                   import org.jboss.tm.TransactionPropagationContextFactory;
                   import org.jboss.tm.TransactionPropagationContextUtil;
                  +import org.jboss.tm.TxUtils;
                   import org.jboss.tm.usertx.interfaces.UserTransactionSession;
                   import org.jboss.util.collection.WeakValueHashMap;
                  
                  @@ -128,11 +129,20 @@
                         SystemException
                      {
                         TransactionManager tm = getTransactionManager();
                  +
                  +      Object tpc = getTPCFactory().getTransactionPropagationContext();
                  +      Transaction currentTx = (Transaction) activeTx.get(tpc);
                  +      // int status = tm.getStatus(); <-- this doesn't work here
                  +      // int status = currentTx == null ? Status.STATUS_NO_TRANSACTION : currentTx.getStatus(); <- this does
                  +      // System.out.println(TxUtils.getStatusAsString(status));
                  +      if(currentTx != null)
                  +         throw new NotSupportedException("Attempt to start a nested transaction (the transaction started previously hasn't been ended yet).");
                  +
                         // Set timeout value
                         tm.setTransactionTimeout(timeout);
                         // Start tx, and get its TPC.
                         tm.begin();
                  -      Object tpc = getTPCFactory().getTransactionPropagationContext();
                  +
                         // Suspend thread association.
                         Transaction tx = tm.suspend();
                         // Remember that a new tx is now active.
                  Index: src/main/org/jboss/tm/usertx/client/ClientUserTransaction.java
                  ===================================================================
                  --- src/main/org/jboss/tm/usertx/client/ClientUserTransaction.java      (revision 93372)
                  +++ src/main/org/jboss/tm/usertx/client/ClientUserTransaction.java      (working copy)
                  @@ -113,6 +113,10 @@
                      public void begin()
                         throws NotSupportedException, SystemException
                      {
                  +      int status = getStatus();
                  +      if(status != Status.STATUS_NO_TRANSACTION)
                  +         throw new NotSupportedException("Attempt to start a nested transaction (the transaction started previously hasn't been ended yet).");
                  +
                         ThreadInfo info = getThreadInfo();
                         trace = log.isTraceEnabled(); // Only check for trace enabled once per transaction
                  
                  • 6. Re: JBAS-7507: UserTransaction & Nested Transactions
                    aloubyansky

                    From the issue description:

                    check org.jboss.tm.usertx.server.UserTransactionSessionImpl::begin(.)
                    **************************************************************
                         Transaction tx = tm.suspend();
                    **************************************************************
                    This suspension of the ongoing tx had resulted in not throwing NotSupportedException.

                    This is actually true. If tm.resume(tx) is called after the suspension (and commented out in commit(tpc) and rollback(tpc)) then subsequent ut.begin() will fail with NSE:

                    16:06:31,730 ERROR [STDERR] javax.transaction.NotSupportedException
                    16:06:31,730 ERROR [STDERR]     at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.begin(BaseTransaction.java:79)
                    16:06:31,730 ERROR [STDERR]     at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.begin(BaseTransactionManagerDelegate.java:77)
                    16:06:31,730 ERROR [STDERR]     at org.jboss.tm.usertx.server.UserTransactionSessionImpl.begin(UserTransactionSessionImpl.java:147)
                    16:06:31,730 ERROR [STDERR]     at org.jboss.tm.usertx.server.ClientUserTransactionService.invoke(ClientUserTransactionService.java:112)
                    

                    So the TM impl has this logic. The problem is you can't keep client tx associated with the serverside thread.

                    • 7. Re: JBAS-7507: UserTransaction & Nested Transactions
                      vickyk

                      vickyk wrote:

                      What am I missing in understanding the flow?

                      After spending more time I managed to find the reason and came up with this change, it is client side validation.

                       

                      [vicky@posh client]$ svn diff
                      Index: ClientUserTransaction.java
                      ===================================================================
                      --- ClientUserTransaction.java    (revision 93943)
                      +++ ClientUserTransaction.java    (working copy)
                      @@ -107,7 +107,15 @@
                             throws NotSupportedException, SystemException
                          {
                             ThreadInfo info = getThreadInfo();
                      -
                      +      
                      +      int txCount = getTxSpanned();
                      +      if(txCount > 0)
                      +      {
                      +          // Rollback the earlier tx !
                      +          rollback();
                      +          // Throw Not supported TX Exception.
                      +          throw new NotSupportedException("Nested Transaction is not supported for remote standalone Client");
                      +      }      
                             try
                             {
                                Object tpc = getSession().begin(info.getTimeout());
                      @@ -311,7 +319,7 @@
                             Reference ref = new Reference("org.jboss.tm.usertx.client.ClientUserTransaction",
                                "org.jboss.tm.usertx.client.ClientUserTransactionObjectFactory",
                                null);
                      -
                      +      
                             return ref;
                          }
                       
                      @@ -401,6 +409,11 @@
                             return ret;
                          }
                       
                      +   private int getTxSpanned()
                      +   {
                      +       ThreadInfo info = (ThreadInfo) threadInfo.get();
                      +       return info.getTPCSize();
                      +   }
                       
                          // Inner classes -------------------------------------------------
                       
                      @@ -496,6 +509,11 @@
                             {
                                timeout = seconds;
                             }
                      +      
                      +      int getTPCSize()
                      +      {
                      +          return tpcStack.size();
                      +      }
                          }
                       
                       }
                      
                      
                      • 8. Re: JBAS-7507: UserTransaction & Nested Transactions
                        vickyk

                        alex.loubyansky@jboss.com wrote:

                         

                        I guess for remote clients it could be fixed in two places:

                        - UserTransactionSessionImpl on the serverside

                        - or probably even better on the client side in ClientUserTransaction

                        Yes the ClientUserTransaction changes are the better option as this would happen on the client side, we must go with it.

                        I did not check the reponse on forums yesterday so couldn't use your analysis on client side, I have also tested the similar one.

                        However I see your changes much lesser and simpler than mine, I somehow had overlooked at the ClientUserTransaction::getStatus.

                        • 9. Re: JBAS-7507: UserTransaction & Nested Transactions
                          aloubyansky
                          In general, the failure of a nested transaction should not affect the parent transaction. If we consider second ut.begin() as an attempt to start a nested transaction, its failure shouldn't abort the current one. So, I'd say don't rollback the current one.