9 Replies Latest reply on Jun 23, 2008 7:19 AM by Mark Little

    Transaction suspend and resume: Spring 2.0 and Jboss 4.2.1

    deepak aggarwal Newbie

      Hi, We are using Spring and hibernate in our application in a jboss 4.2.1 container. JbossTM is our Transaction Manager. Recently we encountered a problem when using Requires_New transaction and we got the following exception:

      [STDERR] java.lang.IllegalStateException: Trying to change transaction TransactionImple < ac, BasicAction: a0359b1:4e3:4734f5c7:fc status: ActionStatus.RUNNING > in enlist!
      
       [STDERR] at org.jboss.resource.connectionmanager.TxConnectionManager$TxConnectionEventListener.enlist(TxConnectionManager.java:525)
      
       [STDERR] at org.jboss.resource.connectionmanager.TxConnectionManager.transactionStarted(TxConnectionManager.java:328)
      Our scenario was: Service-1 (Started with "Requires" transaction scope, hence a new transaction is started) -------> Calls Service-2 (Started with "Requires_New" transaction scope, hence the earlier outer transaction created in service-1 is suspended and a new transaction is started). We were getting the above exception when Service-1 called Service-2. Spring folks are saying that this issue is happening because connection used by outer transaction in service-1 was not released to the connection pool when outer transaction was suspended. They also said that this is only happening with Jboss and not with Websphere and Weblogic. Anyways, they resolved this issue in their recent release in Spring 2.5. I am just curious and trying to simulate the above scenario by using naked Jboss transaction manager from jboss 4.2.1 and stripping out Spring/hibernate to better understand the problem. Following is the piece of code I wrote in a servlet to simulate the above behaviour, which is :
      Start a transaction and check out a connection from datasource. Do some "work" on db using the connection Suspend the transaction and don't release the checked out connection Start a new transaction. I should get the IllegalStateException mentioned above as the connection used in outer transaction is not released upon suspension. Check out a new connection in inner transaction. Do some work and then rollback. Resume the outer transaction and then rollback.
      JtaTransactionManager jtaTm = (JtaTransactionManager) ourAppContext.getBean("transactionManager");
       jndiDS = (DataSource) ourAppContext.getBean("dataSource");
       System.out.println("TM Class: " + jtaTm.getTransactionManager().getClass());
       System.out.println("JNDI Datasource Class: " + jndiDS.getClass());
      
       // start transaction
       TransactionManager tm = jtaTm.getTransactionManager();
       System.out.println("Starting first transaction");
       tm.begin();
      
       // Retrieve a connection and insert into testJbossTrans
       Connection con1 = jndiDS.getConnection();
       Statement stmt1 = con1.createStatement();
       System.out.println("Connection's autocommit property: " + con1.getAutoCommit());
       stmt1.execute("insert into testJbossTrans values(1)");
       System.out.println("Suspending first transaction");
       Transaction trans1 = tm.suspend();
      
       // start a new transaction
       System.out.println("Starting second transaction; Connection from previous transaction not released");
       tm.begin();
      
       // Retrieve a connection and insert into testJbossTrans
       Connection con2 = jndiDS.getConnection();
       Statement stmt2 = con2.createStatement();
       stmt2.execute("insert into testJbossTrans values(2)");
       System.out.println("Marking for rollback second transaction");
       // mark for rollback and then, rollback
       tm.setRollbackOnly();
       tm.rollback();
      
       stmt2.close();
       stmt2 = null;
       System.out.println("Resuming first transaction");
       tm.resume(trans1);
      
       // mark for rollback again
       System.out.println("Marking for rollback first transaction");
       tm.setRollbackOnly();
       ResultSet rset = stmt1.executeQuery("select count(*) from testJbossTrans");
      
       while (rset.next()) {
       System.out.println("Trans-1 after suspension and using con1: id = " + rset.getInt(1));
       }
      
       // rollback and release connection
       tm.rollback();
       con1.close();
       con1 = null;
       stmt1.close();
       stmt1 = null;
      Note that I am not releasing connection back to the connection pool when I suspend the first transaction. When I begin the second inner transaction, I was expecting to see java.lang.IllegalStateException: Trying to change transaction TransactionImple < ac, BasicAction: a0359b1:4e3:4734f5c7:fc status: ActionStatus.RUNNING > in enlist! but nothing happened. Instead, the above code ran fine and gave following output: [STDOUT] TM Class: class com.arjuna.ats.jbossatx.jta.TransactionManagerDelegate
      [STDOUT] JNDI Datasource Class: class org.jboss.resource.adapter.jdbc.WrapperDataSource
      [STDOUT] Starting first transaction
      [STDOUT] Connection's autocommit property: false
      [STDOUT] Suspending first transaction
      [STDOUT] Starting second transaction; Connection from previous transaction not released
      [STDOUT] Marking for rollback second transaction
      [STDOUT] Resuming first transaction
      [STDOUT] Marking for rollback first transaction
      [STDOUT] Trans-1 after suspension and using con1: id = 1
      Why am I not getting the IllegalStateException? What am I missing here? Once again, I am using Jboss 4.2.1 with Oracle Data source.

        • 1. Re: Transaction suspend and resume: Spring 2.0 and Jboss 4.2
          Mark Little Master

          Why do you believe you should get that exception when suspending the transaction from the thread? Have you read the JTA standard or the JBossTS documentation?

          • 2. Re: Transaction suspend and resume: Spring 2.0 and Jboss 4.2
            deepak aggarwal Newbie

            I am just trying to verify why this exception was happening when using Spring and Hibernate. When looking under the hood, Spring seems to be making the above mentioned calls to Jboss Transaction Manager. But Spring forums are claiming that the IllegalStateException is happening because connection has not been released from the outer transaction on suspension and saying that this is an issue with Jboss and not with Weblogic or Websphere. I also looked at latest Spring code which seems to have the bug fix and the only thing different they are doing this time is release the connection checked out in outer transaction.

            My issue is getting resolved by using the latest spring jar, but I just got curious and wanted to dig deeper as to why this issue was happening in the first place. I have gone through JTA spec and JBoss Transactions API 4.2.3 but it is silent about releasing resources on transaction suspension (which is to be interpreted as it is ok to not release?).

            • 3. Re: Transaction suspend and resume: Spring 2.0 and Jboss 4.2
              Mark Little Master

              The suspend call does nothing with connections (or any other type of participant). It can't because the TM doesn't know about database connections, JMS sessions etc: it only knows about XAResources. So the transaction manager is doing exactly what it should when you call suspend: it is disassociating the thread-to-transaction mapping. The JTA is pretty clear on this, as is the OTS/JTS and the JBossTS documentation. If the Spring guys want to argue that point then please push them to our forums ;-)

              • 4. Re: Transaction suspend and resume: Spring 2.0 and Jboss 4.2
                Mark Little Master

                BTW, if you care to take a look at the JBossTS code you'll see that all we do during suspend (apart from breaking the thread-to-tx association) is call suspend on the XAResource (with the right flags). I'd be surprised if CICS or Encina did anything different, or Tux for that matter. Now maybe the application server intercepts the suspend calls and does something funky for you, but that's not the transaction manager.

                • 5. Re: Transaction suspend and resume: Spring 2.0 and Jboss 4.2
                  deepak aggarwal Newbie

                  Please refer the following site where Spring is claiming that connection has to be released on suspension: http://jira.springframework.org/browse/SPR-2497?focusedCommentId=19257#action_19257

                  Btw, I am not using XA connections, so there is no question of xaresource.end(Xid, XAResource.TMSUSPEND) getting called. My oracle datsource config file deployed with JBoss is this:

                  <datasources>
                   <local-tx-datasource>
                   <jndi-name>testDS</jndi-name>
                   <use-java-context>false</use-java-context>
                   <connection-url>jdbc:oracle:thin:@agentrics1:1523:ezm_tst</connection-url>
                   <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
                   <user-name>deepak</user-name>
                   <password>deepak</password>
                   <min-pool-size>5</min-pool-size>
                   <max-pool-size>60</max-pool-size>
                   <idle-timeout-minutes>5</idle-timeout-minutes>
                  
                   <!-- Uses the pingDatabase method to check a connection is still valid before handing it out from the pool -->
                   <!--valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleValidConnectionChecker</valid-connection-checker-class-name-->
                   <!-- Checks the Oracle error codes and messages for fatal errors -->
                   <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>
                   <!-- sql to call when connection is created
                   <new-connection-sql>some arbitrary sql</new-connection-sql>
                   -->
                  
                   <!-- sql to call on an existing pooled connection when it is obtained from pool - the OracleValidConnectionChecker is prefered
                   <check-valid-connection-sql>some arbitrary sql</check-valid-connection-sql>
                   -->
                  
                   <!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml -->
                   <metadata>
                   <type-mapping>Oracle9i</type-mapping>
                   </metadata>
                   </local-tx-datasource>
                  
                  </datasources>
                  


                  I think I need to run my debugger on jboss connectionmanager code now :-)




                  • 6. Re: Transaction suspend and resume: Spring 2.0 and Jboss 4.2
                    Jonathan Halliday Master

                    Try

                    <track-connection-by-tx>true</track-connection-by-tx>



                    • 7. Re: Transaction suspend and resume: Spring 2.0 and Jboss 4.2
                      deepak aggarwal Newbie

                      I tried the setting <track-connection-by-tx>true</track-connection-by-tx> and also ran the code through a debugger but still no success. When I run the transaction suspend/resume through the test case mentioned in my post#1 above, there is no IllegalStateException thrown. After the transaction is suspended, Jboss connection pool retrieves a new managed connection for the new inner transaction and there is no question of an IllegalStateException getting thrown on enlistment.

                      But when I use the spring/hibernate code for doing the same, when the outer transaction is suspended and the new inner transaction started, the managed connection that Jboss connection pool retrieves for the inner transaction is the same as that of the outer transaction, which is causing the IllegalStateException to get thrown! This should not be the case as we have set trackByTx=true. Also, note that managed connection is being enlisted on inner transaction start-up and CachedConnectionManager.userTransactionStarted is trying to enlist the managedconnection of the outer transaction, for some reason. Why?

                      • 8. Re: Transaction suspend and resume: Spring 2.0 and Jboss 4.2
                        deepak aggarwal Newbie

                        Found the culprit. It is happening because of "Lazy JCA enlistment" feature of Jboss JCA. Is there a way I can turn this off? Looked at the JCA config docs and FAQ but didn't find anything.

                        • 9. Re: Transaction suspend and resume: Spring 2.0 and Jboss 4.2
                          Mark Little Master

                          Try the JCA forum. But glad to know it's not JBossTS ;-)