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 ka...@apache.org on 2012/01/12 11:50:55 UTC

svn commit: r1230480 - in /db/derby/code/trunk/java: engine/org/apache/derby/jdbc/XATransactionState.java testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATransactionTest.java

Author: kahatlen
Date: Thu Jan 12 10:50:54 2012
New Revision: 1230480

URL: http://svn.apache.org/viewvc?rev=1230480&view=rev
Log:
DERBY-5562: A read-only XA transaction that has a timeout never has the timer canceled when the transaction is complete

Cancel the timer when a read-only transaction is prepared and implicitly committed.

Based on a fix contributed by Brett Bergquist.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/jdbc/XATransactionState.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATransactionTest.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/jdbc/XATransactionState.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/jdbc/XATransactionState.java?rev=1230480&r1=1230479&r2=1230480&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/jdbc/XATransactionState.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/jdbc/XATransactionState.java Thu Jan 12 10:50:54 2012
@@ -33,6 +33,7 @@ import org.apache.derby.iapi.services.co
 import org.apache.derby.iapi.services.context.ContextManager;
 import org.apache.derby.iapi.error.ExceptionSeverity;
 import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.store.access.XATransactionController;
 import org.apache.derby.iapi.store.access.xa.XAXactId;
 import org.apache.derby.iapi.reference.SQLState;
 import java.util.HashMap;
@@ -357,13 +358,21 @@ final class XATransactionState extends C
      */
     synchronized int xa_prepare() throws SQLException {
         int retVal = conn.xa_prepare();
+
+        if (retVal == XATransactionController.XA_RDONLY) {
+            // Read-only transactions are implicitly committed when they are
+            // prepared. Since the transaction has completed, the timeout task
+            // should be cancelled now. DERBY-5562.
+            xa_finalize();
+        }
+
         return retVal;
     }
 
     /** This method cancels timeoutTask and assigns
       * 'performTimeoutRollback = false'.
       */
-    synchronized void xa_finalize() {
+    private void xa_finalize() {
         if (timeoutTask != null) {
             timeoutTask.cancel();
             timeoutTask = null;

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATransactionTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATransactionTest.java?rev=1230480&r1=1230479&r2=1230480&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATransactionTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATransactionTest.java Thu Jan 12 10:50:54 2012
@@ -498,6 +498,88 @@ public class XATransactionTest extends B
         }
     }
 
+    /**
+     * <p>
+     * Regression test case for DERBY-5562.
+     * </p>
+     *
+     * <p>
+     * The timer that aborts long-running transactions if a transaction timeout
+     * has been specified, was not cancelled when preparing a read-only
+     * transaction. Since read-only transactions are implicitly committed when
+     * they are prepared, this meant that the timer would try to abort an
+     * already completed transaction. In addition to printing a confusing
+     * message in derby.log about the transaction being rolled back, when it
+     * actually had been committed, this could also make the timer roll back
+     * the wrong transaction, if a new transaction with the same Xid was
+     * started later.
+     * </p>
+     *
+     * <p>
+     * This test case exposes the bug by running a read-only transaction with
+     * a timeout and preparing it, and then starting a new transaction with the
+     * same Xid and no timeout. The bug would cause the second transaction to
+     * time out.
+     * </p>
+     */
+    public void testDerby5562ReadOnlyTimeout()
+            throws InterruptedException, SQLException, XAException {
+        XADataSource xads = J2EEDataSource.getXADataSource();
+        XAConnection xac = xads.getXAConnection();
+        XAResource xar = xac.getXAResource();
+
+        Xid xid = createXid(55, 62);
+
+        // Set a transaction timeout. This should be relatively short so that
+        // the test case doesn't need to wait very long to trigger the timeout.
+        // However, it needs to be long enough to let the first transaction go
+        // through without hitting the timeout. Hopefully, four seconds is
+        // enough. If the test case starts failing intermittently during the
+        // first transaction, we might have to raise the timeout (and raise the
+        // sleep time in the second transaction correspondingly).
+        assertTrue(xar.setTransactionTimeout(4));
+
+        // Start first transaction.
+        xar.start(xid, XAResource.TMNOFLAGS);
+        Connection c = xac.getConnection();
+        Statement s = c.createStatement();
+        JDBC.assertSingleValueResultSet(
+                s.executeQuery("select * from sysibm.sysdummy1"),
+                "Y");
+        s.close();
+        c.close();
+        xar.end(xid, XAResource.TMSUCCESS);
+
+        // Prepare the first transaction. Since it's a read-only transaction,
+        // it'll be automatically committed, so there's no need to call commit.
+        assertEquals("XA_RDONLY", XAResource.XA_RDONLY, xar.prepare(xid));
+
+        // Reset the timeout for the second transaction.
+        assertTrue(xar.setTransactionTimeout(0));
+
+        // Start second transaction.
+        xar.start(xid, XAResource.TMNOFLAGS);
+        c = xac.getConnection();
+        s = c.createStatement();
+        JDBC.assertSingleValueResultSet(
+                s.executeQuery("select * from sysibm.sysdummy1"),
+                "Y");
+        s.close();
+        c.close();
+
+        // Keep the transaction running so long that it must have exceeded the
+        // timeout for the previous transaction.
+        Thread.sleep(5000);
+
+        // End the transaction. Since there's no timeout on this transaction,
+        // it should work. Before DERBY-5562 was fixed, it would fail because
+        // it had been rolled back by the timer from the previous transaction.
+        xar.end(xid, XAResource.TMSUCCESS);
+        assertEquals("XA_RDONLY", XAResource.XA_RDONLY, xar.prepare(xid));
+
+        xac.close();
+    }
+
     /* ------------------- end helper methods  -------------------------- */
 
     /** Create the Xid object for global transaction identification