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

ronald at mulgara.org ronald at mulgara.org
Mon Jul 7 12:54:27 UTC 2008


Author: ronald
Date: 2008-07-07 05:54:26 -0700 (Mon, 07 Jul 2008)
New Revision: 1061

Modified:
   branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/AdvDatabaseSessionUnitTest.java
   branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java
   branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransactionFactory.java
   branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraInternalTransactionFactory.java
   branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionFactory.java
Log:
On session close, interrupt anybody active on the session. This kicks out
waiters for the write-lock, thus preventing a problem under heavy load
where writers are queued up waiting for the lock, and then even though the
client terminates these waiters will each get the write-lock and then hang
around until the transaction timeout kills them, thereby rendering mulgara
useless for updates for an extended period until the queue has been drained.


Modified: branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/AdvDatabaseSessionUnitTest.java
===================================================================
--- branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/AdvDatabaseSessionUnitTest.java	2008-07-07 12:54:19 UTC (rev 1060)
+++ branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/AdvDatabaseSessionUnitTest.java	2008-07-07 12:54:26 UTC (rev 1061)
@@ -133,6 +133,7 @@
     suite.addTest(new AdvDatabaseSessionUnitTest("testAutoCommitTransactionOps"));
     suite.addTest(new AdvDatabaseSessionUnitTest("testExplicitTransactionFailure"));
     suite.addTest(new AdvDatabaseSessionUnitTest("testAutoCommitTransactionFailure"));
+    suite.addTest(new AdvDatabaseSessionUnitTest("testSessionClose"));
 
     return suite;
   }
@@ -1420,11 +1421,65 @@
     }
   }
 
-
   private static interface TestOp {
     public void run(Session session) throws Exception;
   }
 
+  /**
+   * Test session close on still-active session.
+   */
+  public void testSessionClose() {
+    logger.info("Testing sessionClose");
+
+    // test close while waiting for write-lock
+    try {
+      Session session1 = database.newSession();
+      session1.setAutoCommit(false);
+
+      try {
+        final Session session2 = database.newSession();
+
+        Thread t1 = new Thread("closeTest") {
+          public void run() {
+            try {
+              session2.setAutoCommit(false);
+              fail("Acquired write-lock unexpectedly");
+            } catch (QueryException qe) {
+              logger.debug("Caught expected exception", qe);
+            } finally {
+              try {
+                session2.close();
+              } catch (QueryException qe) {
+                logger.debug("Caught expected exception", qe);
+              }
+            }
+          }
+        };
+        t1.start();
+        Thread.sleep(100L);     // give thread some time to start and block
+
+        assertTrue("second session should still be active", t1.isAlive());
+
+        session2.close();
+
+        try {
+          t1.join(100L);
+        } catch (InterruptedException ie) {
+          logger.error("wait for thread-termination interrupted", ie);
+          fail(ie);
+        }
+        assertFalse("second session should've terminated", t1.isAlive());
+
+      } finally {
+        logger.debug("Releasing autocommit for session1");
+        session1.setAutoCommit(true);
+        session1.close();
+      }
+    } catch (Exception e) {
+      fail(e);
+    }
+  }
+
   //
   // Internal methods
   //

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-07-07 12:54:19 UTC (rev 1060)
+++ branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java	2008-07-07 12:54:26 UTC (rev 1061)
@@ -122,6 +122,7 @@
     suite.addTest(new ExternalTransactionUnitTest("testInternalSerialMultipleSessions"));
     suite.addTest(new ExternalTransactionUnitTest("testTransactionTimeout"));
     suite.addTest(new ExternalTransactionUnitTest("testTransactionFailure"));
+    suite.addTest(new ExternalTransactionUnitTest("testSessionClose"));
 
     return suite;
   }
@@ -1887,6 +1888,71 @@
     public void run(Session session, Xid xid) throws Exception;
   }
 
+
+  /**
+   * Test session close on still-active session.
+   */
+  public void testSessionClose() {
+    logger.info("Testing sessionClose");
+
+    // test close while waiting for write-lock
+    try {
+      Session session1 = database.newSession();
+
+      XAResource resource1 = session1.getXAResource();
+      Xid xid1 = new TestXid(1);
+      resource1.start(xid1, XAResource.TMNOFLAGS);
+
+      try {
+        final Session session2 = database.newSession();
+
+        Thread t1 = new Thread("closeTest") {
+          public void run() {
+            try {
+              XAResource resource2 = session2.getXAResource();
+              Xid xid2 = new TestXid(2);
+              resource2.start(xid2, XAResource.TMNOFLAGS);
+
+              fail("Acquired write-lock unexpectedly");
+            } catch (XAException xae) {
+              logger.debug("Caught expected exception", xae);
+            } catch (QueryException qe) {
+              logger.error("Caught unexpected exception", qe);
+              fail("Caught unexpected exception " + qe);
+            } finally {
+              try {
+                session2.close();
+              } catch (QueryException qe) {
+                logger.debug("Caught expected exception", qe);
+              }
+            }
+          }
+        };
+        t1.start();
+        Thread.sleep(100L);     // give thread some time to start and block
+
+        assertTrue("second session should still be active", t1.isAlive());
+
+        session2.close();
+
+        try {
+          t1.join(100L);
+        } catch (InterruptedException ie) {
+          logger.error("wait for thread-termination interrupted", ie);
+          fail(ie);
+        }
+        assertFalse("second session should've terminated", t1.isAlive());
+
+      } finally {
+        logger.debug("closing session1");
+        resource1.end(xid1, XAResource.TMSUCCESS);
+        resource1.commit(xid1, true);
+        session1.close();
+      }
+    } catch (Exception e) {
+      fail(e);
+    }
+  }
   //
   // Internal methods
   //

Modified: branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransactionFactory.java
===================================================================
--- branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransactionFactory.java	2008-07-07 12:54:19 UTC (rev 1060)
+++ branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransactionFactory.java	2008-07-07 12:54:26 UTC (rev 1061)
@@ -203,7 +203,7 @@
   }
 
   public void closingSession() throws MulgaraTransactionException {
-    acquireMutex(0, MulgaraTransactionException.class);
+    acquireMutexWithInterrupt(0, MulgaraTransactionException.class);
     try {
       try {
         super.closingSession();

Modified: branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraInternalTransactionFactory.java
===================================================================
--- branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraInternalTransactionFactory.java	2008-07-07 12:54:19 UTC (rev 1060)
+++ branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraInternalTransactionFactory.java	2008-07-07 12:54:26 UTC (rev 1061)
@@ -346,7 +346,7 @@
   }
 
   public void closingSession() throws MulgaraTransactionException {
-    acquireMutex(0, MulgaraTransactionException.class);
+    acquireMutexWithInterrupt(0, MulgaraTransactionException.class);
     try {
       try {
         super.closingSession();

Modified: branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionFactory.java
===================================================================
--- branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionFactory.java	2008-07-07 12:54:19 UTC (rev 1060)
+++ branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionFactory.java	2008-07-07 12:54:26 UTC (rev 1061)
@@ -272,7 +272,7 @@
         try {
           getMutexLock().wait(wait);
         } catch (InterruptedException ie) {
-          throw exc.cast(newException(exc, "Interrupted while waiting to acquire lock").initCause(ie));
+          throw newExceptionOrCause(exc, "Interrupted while waiting to acquire lock", ie);
         }
       }
 
@@ -282,6 +282,25 @@
   }
 
   /** 
+   * Acquire the mutex, interrupting the existing holder is there is one. 
+   * 
+   * @param timeout how many milliseconds to wait for the mutex, or 0 to wait indefinitely
+   * @param T       the type of exception to throw on failure
+   * @throws Exception if the mutex could not be acquired, either due to a timeout or due to an
+   *                   interrupt
+   * @see #acquireMutex
+   */
+  public <T extends Throwable> void acquireMutexWithInterrupt(long timeout, Class<T> exc) throws T {
+    synchronized (getMutexLock()) {
+      if (getMutexHolder() != null && getMutexHolder() != Thread.currentThread()) {
+        getMutexHolder().interrupt();
+      }
+
+      acquireMutex(timeout, exc);
+    }
+  }
+
+  /** 
    * Release the mutex. 
    *
    * @throws IllegalStateException is the mutex is not held by the current thread
@@ -316,7 +335,7 @@
     return mutexHolder;
   }
 
-  private static <T> T newException(Class<T> exc, String msg) {
+  private static <T extends Throwable> T newException(Class<T> exc, String msg) {
     try {
       return exc.getConstructor(String.class).newInstance(msg);
     } catch (Exception e) {
@@ -324,6 +343,12 @@
     }
   }
 
+  private static <T extends Throwable> T newExceptionOrCause(Class<T> exc, String msg, Throwable cause) {
+    if (exc.isAssignableFrom(cause.getClass()))
+      return exc.cast(cause);
+    return exc.cast(newException(exc, msg).initCause(cause));
+  }
+
   private class IdleReaper extends TimerTask {
     public IdleReaper(Timer timer, long periodMillis) {
       logger.info("Idle-reaper created: " + System.identityHashCode(this));




More information about the Mulgara-svn mailing list