You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by km...@apache.org on 2012/02/28 18:43:44 UTC

svn commit: r1294744 - in /db/derby/code/branches/10.6: ./ java/engine/org/apache/derby/jdbc/ java/testing/org/apache/derbyTesting/functionTests/master/ java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/

Author: kmarsden
Date: Tue Feb 28 17:43:43 2012
New Revision: 1294744

URL: http://svn.apache.org/viewvc?rev=1294744&view=rev
Log:
DERBY-5552 Derby threads hanging when using ClientXADataSource and a deadlock or lock timeout occurs

Code patch contributed by Brett Bergquist - brett at thebergquistfamily dot com
Derby will no longer null out the connection on lock timeout or deadlock.

I added test to verify proper behavior for the conneciton and transaction
state after the lock timeout occurs. 



Modified:
    db/derby/code/branches/10.6/   (props changed)
    db/derby/code/branches/10.6/java/engine/org/apache/derby/jdbc/XATransactionState.java
    db/derby/code/branches/10.6/java/testing/org/apache/derbyTesting/functionTests/master/xaSimpleNegative.out
    db/derby/code/branches/10.6/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATest.java

Propchange: db/derby/code/branches/10.6/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Tue Feb 28 17:43:43 2012
@@ -1,4 +1,4 @@
-/db/derby/code/branches/10.7:1140196
+/db/derby/code/branches/10.7:1140196,1294731
 /db/derby/code/branches/10.8:1209227-1209228,1209284
-/db/derby/code/trunk:938547,938796,938959,939231,940462,940469,941627,942031,942286,942476,942480,942587,944152,946794,948045,948069,951346,951366,952138,952237,952581,954344,954421,954544,954748,955001,955540,955634,956075,956234,956445,956569,956659,957260,957902,958163,958257,958264,958508,958522,958555,958618,958939,959550,961892,962716,963206,963705,964039,964115,964402,965647,966393,967201,967304,980089,980684,986689,986834,987539,989099,990292,997325,998170,999119,999479,999485,1002291,1002682,1002853,1021426,1024511,1024528,1025615,1025795,1028716,1030043,1033485,1033864,1035164,1038514,1040658,1053724,1055169,1062096,1063809,1065061,1067250,1069661,1071886,1078461,1081455,1097247,1103681,1103718,1136363,1138341,1138444,1141924,1164370,1203050,1207729,1208775
+/db/derby/code/trunk:938547,938796,938959,939231,940462,940469,941627,942031,942286,942476,942480,942587,944152,946794,948045,948069,951346,951366,952138,952237,952581,954344,954421,954544,954748,955001,955540,955634,956075,956234,956445,956569,956659,957260,957902,958163,958257,958264,958508,958522,958555,958618,958939,959550,961892,962716,963206,963705,964039,964115,964402,965647,966393,967201,967304,980089,980684,986689,986834,987539,989099,990292,997325,998170,999119,999479,999485,1002291,1002682,1002853,1021426,1024511,1024528,1025615,1025795,1028716,1030043,1033485,1033864,1035164,1038514,1040658,1053724,1055169,1062096,1063809,1065061,1067250,1069661,1071886,1078461,1081455,1097247,1103681,1103718,1136363,1138341,1138444,1141924,1164370,1203050,1207729,1208775,1293494
 /db/derby/docs/trunk:954344

Modified: db/derby/code/branches/10.6/java/engine/org/apache/derby/jdbc/XATransactionState.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.6/java/engine/org/apache/derby/jdbc/XATransactionState.java?rev=1294744&r1=1294743&r2=1294744&view=diff
==============================================================================
--- db/derby/code/branches/10.6/java/engine/org/apache/derby/jdbc/XATransactionState.java (original)
+++ db/derby/code/branches/10.6/java/engine/org/apache/derby/jdbc/XATransactionState.java Tue Feb 28 17:43:43 2012
@@ -147,8 +147,9 @@ final class XATransactionState extends C
 			if (se.getSeverity() == ExceptionSeverity.TRANSACTION_SEVERITY) {
 
 				synchronized (this) {
-					// disable use of the connection until it is cleaned up.
-					conn.setApplicationConnection(null);
+					// prior to the DERBY-5552 fix, we would disable the connection
+					// here with conn.setApplicationConnection(null);
+					// which could cause a NPE
 					notifyAll();
 					associationState = TRO_FAIL;
 					if (SQLState.DEADLOCK.equals(se.getMessageId()))

Modified: db/derby/code/branches/10.6/java/testing/org/apache/derbyTesting/functionTests/master/xaSimpleNegative.out
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.6/java/testing/org/apache/derbyTesting/functionTests/master/xaSimpleNegative.out?rev=1294744&r1=1294743&r2=1294744&view=diff
==============================================================================
--- db/derby/code/branches/10.6/java/testing/org/apache/derbyTesting/functionTests/master/xaSimpleNegative.out (original)
+++ db/derby/code/branches/10.6/java/testing/org/apache/derbyTesting/functionTests/master/xaSimpleNegative.out Tue Feb 28 17:43:43 2012
@@ -199,7 +199,9 @@ A         |B          
 ERROR 40XL1: A lock could not be obtained within the time requested
 ij(XA)> -- ERROR: should have no connection underneath
 select * from APP.negative;
-ERROR 08003: No current connection.
+A         |B          
+----------------------
+ERROR 40XL1: A lock could not be obtained within the time requested
 ij(XA)> -- ERROR: should have no connection underneath and xid 2 is gone
 xa_end xa_suspend 2;
 IJ ERROR: XA_RBTIMEOUT 

Modified: db/derby/code/branches/10.6/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATest.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.6/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATest.java?rev=1294744&r1=1294743&r2=1294744&view=diff
==============================================================================
--- db/derby/code/branches/10.6/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATest.java (original)
+++ db/derby/code/branches/10.6/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATest.java Tue Feb 28 17:43:43 2012
@@ -21,6 +21,7 @@
 
 package org.apache.derbyTesting.functionTests.tests.jdbcapi;
 
+
 import java.sql.CallableStatement;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
@@ -41,13 +42,17 @@ import junit.framework.TestSuite;
 
 import org.apache.derbyTesting.junit.BaseJDBCTestCase;
 import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
+import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
 import org.apache.derbyTesting.junit.J2EEDataSource;
 import org.apache.derbyTesting.junit.JDBC;
 import org.apache.derbyTesting.junit.TestConfiguration;
+import org.apache.derbyTesting.junit.Utilities;
 import org.apache.derbyTesting.junit.XATestUtil;
 
 public class XATest extends BaseJDBCTestCase {
 
+    public static final String LOCKTIMEOUT="40XL1";
+    
     public XATest(String name) {
         super(name);
 
@@ -1129,6 +1134,99 @@ public class XATest extends BaseJDBCTest
           doXATempTableD4731Work(true, true, XATestUtil.getXid(998, 10, 50));
     }
  
+    /**
+     * DERBY-5552 Check that lock timeout does not destroy connection
+     * during an XA Transaction.
+     * 
+     * @throws SQLException
+     * @throws XAException
+     */
+    public void testXALockTimeout() throws SQLException, XAException {
+        XADataSource xads = J2EEDataSource.getXADataSource();
+        J2EEDataSource.setBeanProperty(xads, "databaseName", "wombat");
+       
+        // Get the first connection and lock table in 
+        // xa transaction
+        XAConnection xaconn = xads.getXAConnection();
+        XAResource xar = xaconn.getXAResource();
+        Xid xid = XATestUtil.getXid(998,10,50);
+   
+        Connection conn = xaconn.getConnection();
+        Statement s = conn.createStatement();
+        xar.start(xid, XAResource.TMNOFLAGS);
+        s.executeUpdate("INSERT INTO TABLT VALUES(2)");
+        
+        // Get a second connection and global xact
+        // and try to select causing lock timeout
+        XAConnection xaconn2 = xads.getXAConnection();
+        XAResource xar2 = xaconn2.getXAResource();
+        Xid xid2 = XATestUtil.getXid(999,11,51);
+        Connection conn2 = xaconn2.getConnection();
+        // Set to serializable so we get lock timeout
+        conn2.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
+        xar2.start(xid2, XAResource.TMNOFLAGS);
+        Statement s2 = conn2.createStatement();
+        s2.executeUpdate("INSERT INTO TABLT VALUES(3)");
+        assertGlobalXactCount(2);
+        try {
+            ResultSet rs = s2.executeQuery("SELECT * FROM TABLT");
+            fail("Should have gotten lock timeout error: " + LOCKTIMEOUT);
+        } catch (SQLException se) {
+            assertSQLState(LOCKTIMEOUT,se);
+            }
+        // After the lock timeout we just have one global transaction.
+        // lock timeout implicitly rolled back xid2
+        assertGlobalXactCount(1);
+        assertConnOK(conn);
+        // DERBY-5552 Make sure connection is ok after lock timeout
+        assertConnOK(conn2);
+        //Should be able to end and commit xid1
+        xar.end(xid, XAResource.TMSUCCESS);
+        xar.prepare(xid);
+        xar.commit(xid, false);
+        
+        // xid2 should have already been rolled back so end should fail
+        try {    
+            xar2.end(xid2, XAResource.TMSUCCESS);
+            fail("Should have gotten exception ending xid2");
+        } catch (XAException xae) {
+            //xae.printStackTrace();
+            assertEquals(XAException.XA_RBTIMEOUT, xae.errorCode);
+            
+        } 
+     
+        // Should have no locks on TABLT
+        Statement drops = createStatement();
+        drops.executeUpdate("DROP TABLE TABLT");
+        // verify there are no more global transactions
+        assertGlobalXactCount(0);
+        
+        // Need to explicitly rollback xid2 as it ended with
+        // an implicit rollback XA_RBTIMEOUT
+        xar2.rollback(xid2);
+        
+        // Make sure both connections can be used to make a new global xact
+        xar.start(xid, XAResource.TMNOFLAGS);
+        s.executeUpdate("CREATE TABLE TABLT (I INT)");
+        s.executeUpdate("INSERT INTO TABLT VALUES(1)");
+        xar.end(xid, XAResource.TMSUCCESS);
+        xar.prepare(xid);
+        xar.commit(xid, false);
+        
+        // now the other connection ..
+        xar2.start(xid2, XAResource.TMNOFLAGS);
+        s2.executeUpdate("INSERT INTO TABLT VALUES(2)");
+        xar2.end(xid2, XAResource.TMSUCCESS);
+        xar.prepare(xid2);
+        xar.commit(xid2, false);
+        assertGlobalXactCount(0);
+        conn.close();
+        xaconn.close();
+        conn2.close();
+        xaconn2.close();
+        
+    }
+    
     
     /**
      * The two cases for DERBY-4371 do essentially the same thing. Except doing
@@ -1267,11 +1365,50 @@ public class XATest extends BaseJDBCTest
              */
             protected void decorateSQL(Statement s) throws SQLException {
                 XATestUtil.createXATransactionView(s);
+                // Table for lock timeout test
+                s.executeUpdate("CREATE TABLE TABLT (I INT)");
+                s.executeUpdate("INSERT INTO TABLT VALUES(1)");
             }
 
         };
     }
-
+    
+    /**
+     * Excecute a simple SQL statement to assert that the connection is valid
+     * @param conn Connection to check
+     * @throws SQLException on error
+     */
+    private static void assertConnOK(Connection conn) throws SQLException{
+        Statement s = conn.createStatement();
+        ResultSet rs = s.executeQuery("VALUES(1)");
+        JDBC.assertSingleValueResultSet(rs, "1");
+      }
+    
+    
+    /**
+     * Verify the expected number of global transactions.
+     * Run test with -Dderby.tests.debug to see the full transaction table on the
+     * console
+     * 
+     * @param expectedCount expected number of global transaction
+     * @throws SQLException on error
+     */
+    private void assertGlobalXactCount(int expectedCount ) throws SQLException {
+        Connection conn = openDefaultConnection();
+        Statement s = conn.createStatement();
+        ResultSet rs = s.executeQuery(
+              "SELECT COUNT(*) FROM syscs_diag.transaction_table WHERE GLOBAL_XID IS NOT NULL");
+        rs.next();
+        int count = rs.getInt(1);
+        if (TestConfiguration.getCurrent().isVerbose()) {
+            System.out.println("assertGlobalXactCount(" + expectedCount +
+                    "): Full Transaction Table ...");
+            Utilities.showResultSet(s.executeQuery(
+                    "SELECT * FROM syscs_diag.transaction_table"));
+        }
+        assertTrue("Expected " + expectedCount + "global transactions but saw " + count, expectedCount == count);
+    }
+    
     /**
      * Runs the test fixtures in embedded and client.
      * 
@@ -1287,7 +1424,8 @@ public class XATest extends BaseJDBCTest
 
         suite.addTest(TestConfiguration
                 .clientServerDecorator(baseSuite("XATest:client")));
-        return suite;
+        
+        return DatabasePropertyTestSetup.setLockTimeouts(suite, 3, 5);
     }
 
 }