From dbf8f44f09ea99a128ecfccea7a94928e4439fe1 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" 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(-) 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. + * + *

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 { } private static class TransactionImpl implements Transaction { + private static final int READY = 0, PRE_COMMITED = 1, EXITED = 2; + private final TransactionScope mScope; private final TransactionImpl mParent; private final boolean mTop; @@ -340,7 +342,7 @@ public class TransactionScope { private TimeUnit mTimeoutUnit; private TransactionImpl mChild; - private boolean mExited; + private int mState; private Txn mTxn; // Tracks all registered cursors. @@ -361,36 +363,77 @@ public class TransactionScope { } } + 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 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 { TransactionScope 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