From dbf8f44f09ea99a128ecfccea7a94928e4439fe1 Mon Sep 17 00:00:00 2001
From: "Brian S. O'Neill" <bronee@gmail.com>
Date: Wed, 19 Jan 2011 04:23:06 +0000
Subject: Add Transaction preCommit method.

---
 .../java/com/amazon/carbonado/Transaction.java     |  16 +++
 .../carbonado/repo/logging/LoggingTransaction.java |   5 +
 .../com/amazon/carbonado/txn/TransactionPair.java  |   4 +
 .../com/amazon/carbonado/txn/TransactionScope.java | 150 ++++++++++++++-------
 4 files changed, 127 insertions(+), 48 deletions(-)

(limited to 'src/main/java/com')

diff --git a/src/main/java/com/amazon/carbonado/Transaction.java b/src/main/java/com/amazon/carbonado/Transaction.java
index 960cd24..a216b39 100644
--- a/src/main/java/com/amazon/carbonado/Transaction.java
+++ b/src/main/java/com/amazon/carbonado/Transaction.java
@@ -143,4 +143,20 @@ public interface Transaction {
      * @since 1.2
      */
     void attach();
+
+    /**
+     * Calling this method commits all nested child transactions, closes all
+     * scoped cursors, and locks out some interactions from other threads. The
+     * commit method must still be called to finish the commit. Most applications
+     * have no use for pre-commit and should only ever call commit.
+     *
+     * <p>The intent of this method is to complete as many operations as
+     * possible leading up to the actual commit. If pre-commit succeeds, then
+     * commit will most likely succeed as well. While in a pre-commit state, the
+     * transaction can still be used by the current thread. Calling pre-commit
+     * again ensures that child transactions and cursors are closed.
+     *
+     * @return false if transaction has exited
+     */
+    boolean preCommit() throws PersistException;
 }
diff --git a/src/main/java/com/amazon/carbonado/repo/logging/LoggingTransaction.java b/src/main/java/com/amazon/carbonado/repo/logging/LoggingTransaction.java
index 532159c..40ae99e 100644
--- a/src/main/java/com/amazon/carbonado/repo/logging/LoggingTransaction.java
+++ b/src/main/java/com/amazon/carbonado/repo/logging/LoggingTransaction.java
@@ -97,6 +97,11 @@ class LoggingTransaction implements Transaction {
         mTxn.attach();
     }
 
+    public boolean preCommit() throws PersistException {
+        mLog.write("Transaction.preCommit() on " + idChain());
+        return mTxn.preCommit();
+    }
+
     private String idChain() {
         if (mParent == null) {
             return String.valueOf(mID);
diff --git a/src/main/java/com/amazon/carbonado/txn/TransactionPair.java b/src/main/java/com/amazon/carbonado/txn/TransactionPair.java
index a247d0f..3b96895 100644
--- a/src/main/java/com/amazon/carbonado/txn/TransactionPair.java
+++ b/src/main/java/com/amazon/carbonado/txn/TransactionPair.java
@@ -106,4 +106,8 @@ public class TransactionPair implements Transaction {
             throw e;
         }
     }
+
+    public boolean preCommit() throws PersistException {
+        return mPrimaryTransaction.preCommit() ? mSecondaryTransaction.preCommit() : false;
+    }
 }
diff --git a/src/main/java/com/amazon/carbonado/txn/TransactionScope.java b/src/main/java/com/amazon/carbonado/txn/TransactionScope.java
index 98a1210..7cf4903 100644
--- a/src/main/java/com/amazon/carbonado/txn/TransactionScope.java
+++ b/src/main/java/com/amazon/carbonado/txn/TransactionScope.java
@@ -330,6 +330,8 @@ public class TransactionScope<Txn> {
     }
 
     private static class TransactionImpl<Txn> implements Transaction {
+        private static final int READY = 0, PRE_COMMITED = 1, EXITED = 2;
+
         private final TransactionScope<Txn> mScope;
         private final TransactionImpl<Txn> mParent;
         private final boolean mTop;
@@ -340,7 +342,7 @@ public class TransactionScope<Txn> {
         private TimeUnit mTimeoutUnit;
 
         private TransactionImpl<Txn> mChild;
-        private boolean mExited;
+        private int mState;
         private Txn mTxn;
 
         // Tracks all registered cursors.
@@ -361,36 +363,77 @@ public class TransactionScope<Txn> {
             }
         }
 
+        public boolean preCommit() throws PersistException {
+            mScope.mLock.lock();
+
+            switch (mState) {
+            case EXITED:
+                mScope.mLock.unlock();
+                return false;
+
+            case PRE_COMMITED:
+                // Release extra lock, and then fallthrough to next case.
+                mScope.mLock.unlock();
+
+            case READY: default:
+                // Perform pre-commit actions and return with lock held.
+                try {
+                    if (mChild != null) {
+                        mChild.commit();
+                    }
+                    closeCursors();
+                    mState = PRE_COMMITED;
+                    return true;
+                } catch (Throwable e) {
+                    // Restore state if any exception.
+                    mState = READY;
+                    mScope.mLock.unlock();
+                    throw ExceptionTransformer.getInstance().toPersistException(e);
+                }
+            }
+        }
+
         public void commit() throws PersistException {
             TransactionScope<Txn> scope = mScope;
             scope.mLock.lock();
             try {
-                if (!mExited) {
+                switch (mState) {
+                case EXITED:
+                    return;
+
+                case PRE_COMMITED:
+                    // Restore state and release extra lock.
+                    mState = READY;
+                    scope.mLock.unlock();
+                    break;
+
+                case READY: default:
+                    // Perform pre-commit actions.
                     if (mChild != null) {
                         mChild.commit();
                     }
-
                     closeCursors();
+                    break;
+                }
 
-                    if (mTxn != null) {
-                        if (mParent == null || mParent.mTxn != mTxn) {
-                            try {
-                                if (!scope.mTxnMgr.commitTxn(mTxn)) {
-                                    mTxn = null;
-                                }
-                            } catch (Throwable e) {
-                                try {
-                                    scope.mTxnMgr.abortTxn(mTxn);
-                                } catch (Throwable e2) {
-                                    // Ignore. At least we tried to clean up.
-                                }
+                if (mTxn != null) {
+                    if (mParent == null || mParent.mTxn != mTxn) {
+                        try {
+                            if (!scope.mTxnMgr.commitTxn(mTxn)) {
                                 mTxn = null;
-                                throw ExceptionTransformer.getInstance().toPersistException(e);
                             }
-                        } else {
-                            // Indicate fake nested transaction committed.
+                        } catch (Throwable e) {
+                            try {
+                                scope.mTxnMgr.abortTxn(mTxn);
+                            } catch (Throwable e2) {
+                                // Ignore. At least we tried to clean up.
+                            }
                             mTxn = null;
+                            throw ExceptionTransformer.getInstance().toPersistException(e);
                         }
+                    } else {
+                        // Indicate fake nested transaction committed.
+                        mTxn = null;
                     }
                 }
             } finally {
@@ -402,48 +445,59 @@ public class TransactionScope<Txn> {
             TransactionScope<Txn> scope = mScope;
             scope.mLock.lock();
             try {
-                if (!mExited) {
-                    Exception exception = null;
-                    try {
-                        if (mChild != null) {
-                            try {
-                                mChild.exit();
-                            } catch (Exception e) {
-                                if (exception == null) {
-                                    exception = e;
-                                }
-                            }
-                        }
+                switch (mState) {
+                case EXITED:
+                    return;
+
+                case PRE_COMMITED:
+                    // Release extra lock.
+                    scope.mLock.unlock();
+                    break;
+
+                case READY: default:
+                    break;
+                }
 
+                Exception exception = null;
+                try {
+                    if (mChild != null) {
                         try {
-                            closeCursors();
+                            mChild.exit();
                         } catch (Exception e) {
                             if (exception == null) {
                                 exception = e;
                             }
                         }
+                    }
 
-                        if (mTxn != null) {
-                            try {
-                                if (mParent == null || mParent.mTxn != mTxn) {
-                                    try {
-                                        scope.mTxnMgr.abortTxn(mTxn);
-                                    } catch (Exception e) {
-                                        if (exception == null) {
-                                            exception = e;
-                                        }
+                    try {
+                        closeCursors();
+                    } catch (Exception e) {
+                        if (exception == null) {
+                            exception = e;
+                        }
+                    }
+
+                    if (mTxn != null) {
+                        try {
+                            if (mParent == null || mParent.mTxn != mTxn) {
+                                try {
+                                    scope.mTxnMgr.abortTxn(mTxn);
+                                } catch (Exception e) {
+                                    if (exception == null) {
+                                        exception = e;
                                     }
                                 }
-                            } finally {
-                                mTxn = null;
                             }
+                        } finally {
+                            mTxn = null;
                         }
-                    } finally {
-                        scope.mActive = mParent;
-                        mExited = true;
-                        if (exception != null) {
-                            throw ExceptionTransformer.getInstance().toPersistException(exception);
-                        }
+                    }
+                } finally {
+                    scope.mActive = mParent;
+                    mState = EXITED;
+                    if (exception != null) {
+                        throw ExceptionTransformer.getInstance().toPersistException(exception);
                     }
                 }
             } finally {
-- 
cgit v1.2.3