[Mulgara-svn] r99 - branches/xafix/src/jar/resolver/java/org/mulgara/resolver

andrae at mulgara.org andrae at mulgara.org
Wed Oct 11 12:35:42 UTC 2006


Author: andrae
Date: 2006-10-11 07:35:42 -0500 (Wed, 11 Oct 2006)
New Revision: 99

Modified:
   branches/xafix/src/jar/resolver/java/org/mulgara/resolver/AnswerOperation.java
   branches/xafix/src/jar/resolver/java/org/mulgara/resolver/AnswerOperationResult.java
   branches/xafix/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransaction.java
   branches/xafix/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionManager.java
Log:
Mostly sketching The Manager atm (the Answer* edits are just copyright headers).

Thinking about user-demarcated transactions.  I'm remebering all the boundary conditions I stumbled across the first 2 times I did this.

The trick is that if an error occurs we need to release the writelock (there's no point in holding on to it, we've failed), but we need to remember not to enter any implicit transactions (autocommit stays off until explicitly turned back on).

The reason for this is scripts -

setAutocommit off
load1
load2
laod3
setAutocommit on

if load2 fails, then load1 will be rolledback (as it should be), however if we impliclty turn autocommit on in response to this, then load3 will trigger an implicit transaction and may subsequently succed.  This would violate ACID --- either ALL three should commit, or ALL three should be rolledback.

Making sure this happens is not overly challenging, it's just a lovely gotcha that pretty much everyone misses the first time they approach this.  And of course it does complicate the manager even more.



Modified: branches/xafix/src/jar/resolver/java/org/mulgara/resolver/AnswerOperation.java
===================================================================
--- branches/xafix/src/jar/resolver/java/org/mulgara/resolver/AnswerOperation.java	2006-10-10 10:50:08 UTC (rev 98)
+++ branches/xafix/src/jar/resolver/java/org/mulgara/resolver/AnswerOperation.java	2006-10-11 12:35:42 UTC (rev 99)
@@ -1,3 +1,20 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.rosenlaw.com/OSL3.0.htm
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This file is an original work developed by Netymon Pty Ltd
+ * (http://www.netymon.com, mailto:mail at netymon.com). Portions created
+ * by Netymon Pty Ltd are Copyright (c) 2006 Netymon Pty Ltd.
+ * All Rights Reserved.
+ */
+
 package org.mulgara.resolver;
 
 public abstract class AnswerOperation {

Modified: branches/xafix/src/jar/resolver/java/org/mulgara/resolver/AnswerOperationResult.java
===================================================================
--- branches/xafix/src/jar/resolver/java/org/mulgara/resolver/AnswerOperationResult.java	2006-10-10 10:50:08 UTC (rev 98)
+++ branches/xafix/src/jar/resolver/java/org/mulgara/resolver/AnswerOperationResult.java	2006-10-11 12:35:42 UTC (rev 99)
@@ -1,3 +1,20 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.rosenlaw.com/OSL3.0.htm
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This file is an original work developed by Netymon Pty Ltd
+ * (http://www.netymon.com, mailto:mail at netymon.com). Portions created
+ * by Netymon Pty Ltd are Copyright (c) 2006 Netymon Pty Ltd.
+ * All Rights Reserved.
+ */
+
 package org.mulgara.resolver;
 
 public interface AnswerOperationResult {

Modified: branches/xafix/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransaction.java
===================================================================
--- branches/xafix/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransaction.java	2006-10-10 10:50:08 UTC (rev 98)
+++ branches/xafix/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransaction.java	2006-10-11 12:35:42 UTC (rev 99)
@@ -43,19 +43,37 @@
  * @licence Open Software License v3.0</a>
  */
 public class MulgaraTransaction {
-  private final TransactionManager transactionManager;
+  private TransactionManager manager;
+
   private Transaction transaction;
   private Thread currentThread;
 
   private int inuse;
   private int using;
 
-  public MulgaraTransaction() { 
+  private final int NO_ROLLBACK = 0;
+  private final int IMPLICIT_ROLLBACK = 1;
+  private final int EXPLICIT_ROLLBACK = 2;
+
+  private int rollback;
+  private Throwable rollbackCause;
+
+  public MulgaraTransaction(TransactionManager manager) { 
+    this.manager = manager;
+
+    manager.begin();
+    transaction = manager.getTransaction();
     inuse = 1; // Note: This implies implict activation as a part of construction.
     using = 0;
+
+    rollback = NO_ROLLBACK;
+    rollbackCause = null;
   }
 
-  public synchronized void activate() throws MulgaraTransactionException {
+  // FIXME: Not yet certain I have the error handling right here.
+  // Need to clarify semantics and ensure the error conditions are 
+  // properly handled.
+  private synchronized void activate() throws MulgaraTransactionException {
     if (currentThread == null) {
       currentThread = Thread.currentThread();
     } else if (!currentThread.equals(Thread.currentThread())) {
@@ -74,8 +92,12 @@
     inuse++;
   }
 
-  public synchronized void deactivate() {
 
+  // FIXME: Not yet certain I have the error handling right here.
+  // Need to clarify semantics and ensure the error conditions are 
+  // properly handled.
+  private synchronized void deactivate() {
+
     inuse--;
 
     if (inuse < 0) {
@@ -85,26 +107,31 @@
 
     if (inuse == 0) {
       if (using == 0) {
+        // END TRANSACTION HERE.  But commit might fail.
         TransactionManager.commit(transaction);
       } else {
+        // What happens if suspend fails?
+        // Rollback and terminate transaction.
         TransactionManager.suspend(transaction);
       }
       currentThread = null;
     }
   }
 
-  public void reference() {
+  // Do I want to check for currentThread here?  Do I want a seperate check() method to 
+  // cover precondition checks against currentThread?
+  void reference() {
     using++;
   }
 
-  public void defeference() {
+  void defeference() {
     using--;
     if (using < 0) {
-      rollback("ERROR: Transaction dereferenced more times than referenced!");
+      implicitRollback(new MulgaraTransactionException("ERROR: Transaction dereferenced more times than referenced!"));
     }
   }
 
-  public void execute(Operation operation) {
+  void execute(Operation operation) {
     activate();
     try {
       operation.execute(this);
@@ -116,7 +143,7 @@
   }
 
 
-  public AnswerOperationResult execute(AnswerOperation ao) {
+  AnswerOperationResult execute(AnswerOperation ao) {
     activate();
     try {
       ao.execute();
@@ -127,4 +154,33 @@
       deactivate();
     }
   }
+
+
+  private void implicitRollback(Throwable cause) throws MulgaraTransactionException {
+    rollback = IMPLICIT_ROLLBACK;
+    rollbackCause = cause;
+    failTransaction();
+  }
+
+  /**
+   * Note: I think this is the only one that matters. 
+   */
+  protected void explicitRollback() throws MulgaraTransactionException {
+    rollback = EXPLICIT_ROLLBACK;
+    // We don't throw an exception here when transaction fails - this is expected,
+    // after all we requested it.
+  }
+
+  private void terminateTransaction() throws MulgaraTransactionException {
+  }
+
+  private void failTransaction() {
+    // We need to handle the whole fact this is an error, but the core operation is rollback.
+    transaction.rollback();
+  }
+
+  private void finalizeTransaction() {
+    // We need a whole load of error handling here, but the core operation is commit.
+    transaction.commit();
+  }
 }

Modified: branches/xafix/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionManager.java
===================================================================
--- branches/xafix/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionManager.java	2006-10-10 10:50:08 UTC (rev 98)
+++ branches/xafix/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionManager.java	2006-10-11 12:35:42 UTC (rev 99)
@@ -50,10 +50,8 @@
   // Write lock is associated with a session.
   private Session currentWritingSession;
   private MulgaraTransaction writeTransaction;
-  private Object writeLockMutex = new Object();
 
-  private Map threadMap;
-  private Map answerMap;
+  private Map failedSessions;
 
   public MulgaraTransactionManager() {
   }
@@ -68,83 +66,88 @@
    *     obtain the write-lock, create a new transaction object and return it.</li>
    * </ul>
    */
-  public MulgaraTransaction getTransaction(Session session, boolean write) {
-    synchronized (writeLockMutex) {
-      if (session == currentWritingSession) {
-        return writeTransaction;
-      } 
-    }
-    if (!write) {
-      return new MulgaraTransaction(this);
-    } else {
-      obtainWriteLock(session);
-      writeTransaction.reference();
+  public synchronized MulgaraTransaction getTransaction(Session session, boolean write) {
+    if (session == currentWritingSession) {
       return writeTransaction;
-    }
-  }
+    } 
 
-  protected void obtainWriteLock(Session session) {
-    synchronized (writeLockMutex) {
-      while (currentWritingSession != null) {
-        writeLockMutex.wait();
-      }
-      currentWritingSession = session;
+    if (write) {
+      obtainWriteLock(session);
     }
-
     writeTransaction = new MulgaraTransaction(this);
   }
 
-  protected void releaseWriteLock() {
-    synchronized (writeLockMutex) {
-      currentWritingSession = null;
-      writeTransaction = null;
-      writeLockMutex.notify();
+  private synchronized void obtainWriteLock(Session session) {
+    while (currentWritingSession != null) {
+      writeLockMutex.wait();
     }
+    currentWritingSession = session;
   }
 
-  public MulgaraTransaction getTransaction(Answer answer) throws MulgaraTransactionException {
-    synchronized (answerMap) {
-      MulgaraTransaction xa = (MulgaraTransaction)answerMap.get(answer);
-      if (xa != null) {
-        return xa;
-      } else {
-        throw new MulgaraTransactionException("Unable to find transaction for answer: " + answer);
-      }
-    }
+  private synchronized void releaseWriteLock() {
+    currentWritingSession = null;
+    writeTransaction = null;
+    writeLockMutex.notify();
   }
 
 
-  public MulgaraTransaction getTransaction() throws MulgaraTransactionException {
-    synchronized(threadMap) {
-      MulgaraTransaction xa = (MulgaraTransaction)threadMap.get(Thread.currentThread());
-      if (xa != null) {
-        return xa;
-      } else {
-        throw new MulgaraTransactionException("Activated exception not found for current thread");
-      }
-    }
+  public synchronized void commit(Session session) {
+    setAutoCommit(session, true);
+    setAutoCommit(session, false);
   }
 
-  public void commit(Session session) {
-    synchronized (writeLockMutex) {
-      setAutoCommit(true);
-      setAutoCommit(false);
-    }
-  }
 
   /**
    * This is an explicit, user-specified rollback.
+   * This 
    * This needs to be distinguished from an implicit rollback triggered by failure.
    */
-  public void rollback(Session) {
-
+  public synchronized void rollback(Session session) {
+    if (session == currentWritingSession) {
+      try {
+        writeTransaction.explicitRollback();
+        finalizeTransaction();
+      } finally {
+        failedSessions.put(currentWritingSession, writeTransaction);
+        writeTransaction = null;
+        currentWritingSession = null;
+        releaseWriteLock();
+      }
+    } else {
+      // We have a problem - rollback called on session that doesn't have a transaction active.
+    }
   }
 
-  public void setAutocommit(Session session, boolean acommit) {
-    synchronized (writeLockMutex) {
+  public synchronized void setAutocommit(Session session, boolean autoCommit) {
+    if (session == currentWritingSession && failedSessions.containsKey(session)) {
+      // CRITICAL ERROR - transaction failed, but we did not finalise it.
     }
+
+    if (session == currentWritingSession || failedSessions.containsKey(session)) {
+      if (autoCommit) {
+        // AutoCommit off -> on === branch on current state of transaction.
+        if (session == currentWritingSession) {
+          // Within active transaction - commit and finalise.
+          try {
+          } finally {
+            releaseWriteLock();
+          }
+        } else {
+          // Within failed transaction - cleanup and finalise.
+          failedSessions.remove(session);
+        }
+      } else {
+        // AutoCommit off -> off === no-op. Log info.
+      }
+    } else {
+      if (autoCommit) {
+        // AutoCommit on -> on === no-op.  Log info.
+      } else {
+        // AutoCommit on -> off ==
+      }
+    }
   }
 
-  public close() {
+  public synchronized close() {
   }
 }




More information about the Mulgara-svn mailing list