diff options
Diffstat (limited to 'src/main/java/com/amazon/carbonado')
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.
+ *
+ * <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 {
|