[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