[Mulgara-svn] r1013 - branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver

ronald at mulgara.org ronald at mulgara.org
Mon Jun 23 02:42:46 UTC 2008


Author: ronald
Date: 2008-06-22 19:42:45 -0700 (Sun, 22 Jun 2008)
New Revision: 1013

Modified:
   branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java
   branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransaction.java
Log:
 * operation failures should trigger heuristicRollback(), not plain rollback()
 * created tests for behaviour after an operation failure


Modified: branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java
===================================================================
--- branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java	2008-06-23 02:39:45 UTC (rev 1012)
+++ branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java	2008-06-23 02:42:45 UTC (rev 1013)
@@ -30,6 +30,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import javax.transaction.xa.XAException;
 import javax.transaction.xa.XAResource;
 import javax.transaction.xa.Xid;
 
@@ -120,6 +121,7 @@
     suite.addTest(new ExternalTransactionUnitTest("testInternalExternalConcurrentTxnRollback"));
     suite.addTest(new ExternalTransactionUnitTest("testExternalInternalConcurrentTxnRollback"));
     suite.addTest(new ExternalTransactionUnitTest("testInternalSerialMultipleSessions"));
+    suite.addTest(new ExternalTransactionUnitTest("testTransactionFailure"));
 
     return suite;
   }
@@ -1484,8 +1486,7 @@
    * Tests cleaning up a transaction on close.  This test added in the process
    * of fixing a bug reported by Ronald on the JTA-beta.
    */
-  public void testInternalSerialMultipleSessions() throws URISyntaxException
-  {
+  public void testInternalSerialMultipleSessions() throws URISyntaxException {
     logger.info("testInternalSerialMultipleSessions");
     URI fileURI  = new File("data/xatest-model1.rdf").toURI();
 
@@ -1510,6 +1511,203 @@
     }
   }
 
+
+  /**
+   * Test various operations after a transaction fails.
+   */
+  public void testTransactionFailure() {
+    logger.info("Testing transactionFailure");
+
+    try {
+      // query after failure should fail
+      shouldFailQE(new TestOp() {
+        public void run(Session session, Xid xid) throws Exception {
+          session.query(createQuery(modelURI)).close();
+        }
+      }, "Query in failed transaction did not fail");
+
+      // insert after failure should fail
+      shouldFailQE(new TestOp() {
+        public void run(Session session, Xid xid) throws Exception {
+          session.insert(modelURI, Collections.singleton(new TripleImpl(
+                                                    new URIReferenceImpl(URI.create("test:a")),
+                                                    new URIReferenceImpl(URI.create("test:b")),
+                                                    new URIReferenceImpl(URI.create("test:c")))));
+        }
+      }, "Insert in failed transaction did not fail");
+
+      // start w/o end after failure should fail
+      shouldFailXAErr(new TestOp() {
+        public void run(Session session, Xid xid) throws Exception {
+          session.getXAResource().start(xid, XAResource.TMNOFLAGS);
+        }
+      }, XAException.XAER_DUPID, true, "Start w/o end in failed transaction did not fail");
+
+      shouldFailXAErr(new TestOp() {
+        public void run(Session session, Xid xid) throws Exception {
+          session.getXAResource().start(xid, XAResource.TMRESUME);
+        }
+        // XXX: shouldn't this be XAER_PROTO ?
+      }, XAException.XA_RBROLLBACK, true, "Start w/o end in failed transaction did not fail");
+
+      shouldFailXAErr(new TestOp() {
+        public void run(Session session, Xid xid) throws Exception {
+          session.getXAResource().start(xid, XAResource.TMJOIN);
+        }
+      }, XAException.XAER_PROTO, true, "Start w/o end in failed transaction did not fail");
+
+      // prepare w/o end after failure should fail
+      shouldFailXAErr(new TestOp() {
+        public void run(Session session, Xid xid) throws Exception {
+          session.getXAResource().prepare(xid);
+        }
+        // XXX: shouldn't this be XAER_PROTO ?
+      }, XAException.XA_RBROLLBACK, true, "Prepare w/o end in failed transaction did not fail");
+
+      // commit w/o end after failure should fail
+      shouldFailXAErr(new TestOp() {
+        public void run(Session session, Xid xid) throws Exception {
+          session.getXAResource().commit(xid, true);
+        }
+        // XXX: shouldn't this be XAER_PROTO ?
+      }, XAException.XA_RBROLLBACK, true, "Commit w/o end in failed transaction did not fail");
+
+      shouldFailXAErr(new TestOp() {
+        public void run(Session session, Xid xid) throws Exception {
+          session.getXAResource().commit(xid, false);
+        }
+        // XXX: shouldn't this be XAER_PROTO ?
+      }, XAException.XA_RBROLLBACK, true, "Commit w/o end in failed transaction did not fail");
+
+      // rollback w/o end after failure should fail
+      shouldFailXAErr(new TestOp() {
+        public void run(Session session, Xid xid) throws Exception {
+          session.getXAResource().rollback(xid);
+        }
+        // XXX: shouldn't this be XAER_PROTO ?
+      }, XAException.XA_HEURRB, true, "Rollback w/o end in failed transaction did not fail");
+
+      // prepare after failure should fail
+      shouldFailXAErr(new TestOp() {
+        public void run(Session session, Xid xid) throws Exception {
+          session.getXAResource().prepare(xid);
+        }
+      }, XAException.XA_RBROLLBACK, false, "Prepare in failed transaction did not fail");
+
+      // commit after failure should fail
+      shouldFailXAErr(new TestOp() {
+        public void run(Session session, Xid xid) throws Exception {
+          session.getXAResource().commit(xid, true);
+        }
+      }, XAException.XA_RBROLLBACK, false, "Commit in failed transaction did not fail");
+
+      shouldFailXAErr(new TestOp() {
+        public void run(Session session, Xid xid) throws Exception {
+          session.getXAResource().commit(xid, false);
+        }
+        // XXX: shouldn't this be XAER_PROTO ?
+      }, XAException.XA_RBROLLBACK, false, "Commit w/o prepare in failed transaction did not fail");
+    } catch (Exception e) {
+      fail(e);
+    }
+  }
+
+  private void shouldFailQE(TestOp op, String msg) throws Exception {
+    testTransactionFailureOp(op, true, 0, true, msg);
+  }
+
+  private void shouldFailXAErr(TestOp op, int expXAErr, boolean beforeEnd, String msg) throws Exception {
+    testTransactionFailureOp(op, false, expXAErr, beforeEnd, msg);
+  }
+
+  /*
+  private void shouldSucceed(TestOp op) throws Exception {
+    testTransactionFailureOp(op, false, null);
+  }
+  */
+
+  private void testTransactionFailureOp(TestOp op, boolean shouldFailQE, int expXAErr,
+                                        boolean beforeEnd, String msg) throws Exception {
+    Session session = database.newSession();
+    try {
+      boolean shouldFail = shouldFailQE || expXAErr != 0;
+
+      // start tx
+      XAResource resource = session.getXAResource();
+
+      Xid xid = new TestXid(1);
+      resource.start(xid, XAResource.TMNOFLAGS);
+
+      // run bad query -> failed tx
+      try {
+        session.query(createQuery(URI.create("urn:no:such:model")));
+        fail("Bad query failed to throw exception");
+      } catch (QueryException qe) {
+      }
+
+      // run test op, verify it succeeds/fails, and reset
+      if (!beforeEnd) {
+        try {
+          resource.end(xid, XAResource.TMSUCCESS);
+        } catch (XAException xae) {
+          if (!isRollback(xae))
+            throw xae;
+        }
+      }
+
+      try {
+        op.run(session, xid);
+        if (shouldFail)
+          fail(msg);
+      } catch (QueryException qe) {
+        if (!shouldFailQE)
+          throw qe;
+      } catch (XAException xae) {
+        assertTrue(msg + ": " + xae.errorCode,
+                   xae.errorCode == expXAErr || xae.errorCode == XAException.XAER_NOTA);
+      }
+
+      try {
+        if (beforeEnd) {
+          try {
+            resource.end(xid, XAResource.TMSUCCESS);
+          } catch (XAException xae) {
+            if (!isRollback(xae))
+              throw xae;
+          }
+        }
+
+        try {
+          resource.rollback(xid);
+        } catch (XAException xae) {
+          if (xae.errorCode == XAException.XA_HEURRB)
+            resource.forget(xid);
+          else if (!isRollback(xae))
+            throw xae;
+        }
+      } catch (XAException xae) {
+        if (!shouldFail || xae.errorCode != XAException.XAER_NOTA)
+          throw xae;
+      }
+
+      // verify we're good to go
+      resource.start(xid, XAResource.TMNOFLAGS);
+      session.query(createQuery(modelURI)).close();
+      resource.end(xid, XAResource.TMSUCCESS);
+      resource.commit(xid, true);
+    } finally {
+      session.close();
+    }
+  }
+
+  private static boolean isRollback(XAException xae) {
+    return xae.errorCode >= XAException.XA_RBBASE && xae.errorCode <= XAException.XA_RBEND;
+  }
+
+  private static interface TestOp {
+    public void run(Session session, Xid xid) throws Exception;
+  }
+
   //
   // Internal methods
   //

Modified: branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransaction.java
===================================================================
--- branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransaction.java	2008-06-23 02:39:45 UTC (rev 1012)
+++ branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransaction.java	2008-06-23 02:42:45 UTC (rev 1013)
@@ -140,8 +140,8 @@
       lastActive = (la != -1) ? System.currentTimeMillis() : -1;
     } catch (Throwable th) {
       try {
-        rollback(xid);
-      } catch (XAException ex) {
+        heuristicRollback(th.toString());
+      } catch (MulgaraTransactionException ex) {
         logger.error("Error in rollback after operation failure", ex);
       }
       throw new MulgaraTransactionException("Operation failed", th);
@@ -161,8 +161,8 @@
     } catch (Throwable th) {
       try {
         logger.warn("Error in answer operation triggered rollback", th);
-        rollback(xid);
-      } catch (XAException ex) {
+        heuristicRollback(th.toString());
+      } catch (MulgaraTransactionException ex) {
         logger.error("Error in rollback after answer-operation failure", ex);
       }
       throw new TuplesException("Request failed", th);




More information about the Mulgara-svn mailing list