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