import org.slf4j.LoggerFactory;
/**
- * Abstract implementation of the {@link DOMStoreTransactionChain} interface relying on {@link DataTreeSnapshot} supplier
- * and backend commit coordinator.
+ * Abstract implementation of the {@link DOMStoreTransactionChain} interface relying on {@link DataTreeSnapshot}
+ * supplier and backend commit coordinator.
*
* @param <T> transaction identifier type
*/
@Beta
-public abstract class AbstractSnapshotBackedTransactionChain<T> extends TransactionReadyPrototype<T> implements DOMStoreTransactionChain {
- private static abstract class State {
+public abstract class AbstractSnapshotBackedTransactionChain<T> extends TransactionReadyPrototype<T>
+ implements DOMStoreTransactionChain {
+ private abstract static class State {
/**
* Allocate a new snapshot.
*
* @return A new snapshot
*/
- protected abstract DataTreeSnapshot getSnapshot();
+ protected abstract DataTreeSnapshot getSnapshot(Object transactionId);
}
private static final class Idle extends State {
}
@Override
- protected DataTreeSnapshot getSnapshot() {
+ protected DataTreeSnapshot getSnapshot(Object transactionId) {
return chain.takeSnapshot();
}
}
}
@Override
- protected DataTreeSnapshot getSnapshot() {
+ protected DataTreeSnapshot getSnapshot(Object transactionId) {
final DataTreeSnapshot ret = snapshot;
- Preconditions.checkState(ret != null, "Previous transaction %s is not ready yet", transaction.getIdentifier());
+ Preconditions.checkState(ret != null,
+ "Could not get snapshot for transaction %s - previous transaction %s is not ready yet",
+ transactionId, transaction.getIdentifier());
return ret;
}
void setSnapshot(final DataTreeSnapshot snapshot) {
final boolean success = SNAPSHOT_UPDATER.compareAndSet(this, null, snapshot);
- Preconditions.checkState(success, "Transaction %s has already been marked as ready", transaction.getIdentifier());
+ Preconditions.checkState(success, "Transaction %s has already been marked as ready",
+ transaction.getIdentifier());
}
}
}
@Override
- protected DataTreeSnapshot getSnapshot() {
+ protected DataTreeSnapshot getSnapshot(Object transactionId) {
throw new IllegalStateException(message);
}
}
state = idleState;
}
- private Entry<State, DataTreeSnapshot> getSnapshot() {
+ private Entry<State, DataTreeSnapshot> getSnapshot(T transactionId) {
final State localState = state;
- return new SimpleEntry<>(localState, localState.getSnapshot());
+ return new SimpleEntry<>(localState, localState.getSnapshot(transactionId));
}
private boolean recordTransaction(final State expected, final DOMStoreWriteTransaction transaction) {
- final State state = new Allocated(transaction);
- return STATE_UPDATER.compareAndSet(this, expected, state);
+ final State localState = new Allocated(transaction);
+ return STATE_UPDATER.compareAndSet(this, expected, localState);
}
@Override
public final DOMStoreReadTransaction newReadOnlyTransaction() {
- final Entry<State, DataTreeSnapshot> entry = getSnapshot();
- return SnapshotBackedTransactions.newReadTransaction(nextTransactionIdentifier(), getDebugTransactions(), entry.getValue());
+ return newReadOnlyTransaction(nextTransactionIdentifier());
+ }
+
+ protected DOMStoreReadTransaction newReadOnlyTransaction(T transactionId) {
+ final Entry<State, DataTreeSnapshot> entry = getSnapshot(transactionId);
+ return SnapshotBackedTransactions.newReadTransaction(transactionId, getDebugTransactions(), entry.getValue());
}
@Override
public final DOMStoreReadWriteTransaction newReadWriteTransaction() {
+ return newReadWriteTransaction(nextTransactionIdentifier());
+ }
+
+ protected DOMStoreReadWriteTransaction newReadWriteTransaction(T transactionId) {
Entry<State, DataTreeSnapshot> entry;
DOMStoreReadWriteTransaction ret;
do {
- entry = getSnapshot();
- ret = new SnapshotBackedReadWriteTransaction<T>(nextTransactionIdentifier(),
- getDebugTransactions(), entry.getValue(), this);
+ entry = getSnapshot(transactionId);
+ ret = new SnapshotBackedReadWriteTransaction<>(transactionId, getDebugTransactions(), entry.getValue(),
+ this);
} while (!recordTransaction(entry.getKey(), ret));
return ret;
@Override
public final DOMStoreWriteTransaction newWriteOnlyTransaction() {
+ return newWriteOnlyTransaction(nextTransactionIdentifier());
+ }
+
+ protected DOMStoreWriteTransaction newWriteOnlyTransaction(T transactionId) {
Entry<State, DataTreeSnapshot> entry;
DOMStoreWriteTransaction ret;
do {
- entry = getSnapshot();
- ret = new SnapshotBackedWriteTransaction<T>(nextTransactionIdentifier(),
- getDebugTransactions(), entry.getValue(), this);
+ entry = getSnapshot(transactionId);
+ ret = new SnapshotBackedWriteTransaction<>(transactionId, getDebugTransactions(), entry.getValue(), this);
} while (!recordTransaction(entry.getKey(), ret));
return ret;
if (allocated.getTransaction().equals(tx)) {
final boolean success = STATE_UPDATER.compareAndSet(this, localState, idleState);
if (!success) {
- LOG.warn("Transaction {} aborted, but chain {} state already transitioned from {} to {}, very strange",
+ LOG.warn(
+ "Transaction {} aborted, but chain {} state already transitioned from {} to {}, very strange",
tx, this, localState, state);
}
}
}
@Override
- protected final DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<T> tx, final DataTreeModification tree) {
+ protected final DOMStoreThreePhaseCommitCohort transactionReady(
+ final SnapshotBackedWriteTransaction<T> tx,
+ final DataTreeModification tree,
+ final Exception readyError) {
+
final State localState = state;
if (localState instanceof Allocated) {
final Allocated allocated = (Allocated)localState;
final DOMStoreWriteTransaction transaction = allocated.getTransaction();
- Preconditions.checkState(tx.equals(transaction), "Mis-ordered ready transaction %s last allocated was %s", tx, transaction);
+ Preconditions.checkState(tx.equals(transaction), "Mis-ordered ready transaction %s last allocated was %s",
+ tx, transaction);
allocated.setSnapshot(tree);
} else {
LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
}
- return createCohort(tx, tree);
+ return createCohort(tx, tree, readyError);
}
@Override
final State localState = state;
do {
- Preconditions.checkState(!CLOSED.equals(localState), "Transaction chain {} has been closed", this);
+ Preconditions.checkState(!CLOSED.equals(localState), "Transaction chain %s has been closed", this);
if (FAILED.equals(localState)) {
LOG.debug("Ignoring user close in failed state");
}
if (!STATE_UPDATER.compareAndSet(this, localState, idleState)) {
- LOG.debug("Transaction chain {} has already transitioned from {} to {}, not making it idle", this, localState, state);
+ LOG.debug("Transaction chain {} has already transitioned from {} to {}, not making it idle",
+ this, localState, state);
}
}
* @param transaction Transaction which failed.
* @param cause Failure cause
*/
- protected final void onTransactionFailed(final SnapshotBackedWriteTransaction<T> transaction, final Throwable cause) {
+ protected final void onTransactionFailed(final SnapshotBackedWriteTransaction<T> transaction,
+ final Throwable cause) {
LOG.debug("Transaction chain {} failed on transaction {}", this, transaction, cause);
state = FAILED;
}
*
* @param transaction Transaction handle
* @param modification {@link DataTreeModification} which needs to be applied to the backend
+ * @param operationError Any previous error that could be reported through three phase commit
* @return A {@link DOMStoreThreePhaseCommitCohort} cohort.
*/
- protected abstract DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<T> transaction, final DataTreeModification modification);
+ protected abstract DOMStoreThreePhaseCommitCohort createCohort(SnapshotBackedWriteTransaction<T> transaction,
+ DataTreeModification modification,
+ Exception operationError);
}