From: Robert Varga Date: Wed, 22 Apr 2015 12:36:13 +0000 (+0200) Subject: Publish SnapshotBacked transactions X-Git-Tag: release/lithium~233^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=978152c5de3bf78ab6da5da4c2db391eec063429;hp=0f3ae2f9e4151a60245a2b295cc7998f0d0e745f Publish SnapshotBacked transactions IMDS-internal transaction implementations can be used by the CDS frontend. Fix them up for publishing and make them available in SPI as a Beta API. Change-Id: Iaa4cd6792db0f262ce41281280f2ccfde6146532 Signed-off-by: Robert Varga --- diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractDOMStoreTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractDOMStoreTransaction.java index f043d2cb84..106abca3ec 100644 --- a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractDOMStoreTransaction.java +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractDOMStoreTransaction.java @@ -49,7 +49,8 @@ public abstract class AbstractDOMStoreTransaction implements DOMStoreTransact * @return The context in which this transaction was allocated, or null * if the context was not recorded. */ - @Nullable public final Throwable getDebugContext() { + @Nullable + public final Throwable getDebugContext() { return debugContext; } diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractSnapshotBackedTransactionChain.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractSnapshotBackedTransactionChain.java new file mode 100644 index 0000000000..b7776b2a39 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractSnapshotBackedTransactionChain.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.core.spi.data; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.util.AbstractMap.SimpleEntry; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Abstract implementation of the {@link DOMStoreTransactionChain} interface relying on {@link DataTreeSnapshot} supplier + * and backend commit coordinator. + * + * @param transaction identifier type + */ +@Beta +public abstract class AbstractSnapshotBackedTransactionChain extends TransactionReadyPrototype implements DOMStoreTransactionChain { + private static abstract class State { + /** + * Allocate a new snapshot. + * + * @return A new snapshot + */ + protected abstract DataTreeSnapshot getSnapshot(); + } + + private static final class Idle extends State { + private final AbstractSnapshotBackedTransactionChain chain; + + Idle(final AbstractSnapshotBackedTransactionChain chain) { + this.chain = Preconditions.checkNotNull(chain); + } + + @Override + protected DataTreeSnapshot getSnapshot() { + return chain.takeSnapshot(); + } + } + + /** + * We have a transaction out there. + */ + private static final class Allocated extends State { + private static final AtomicReferenceFieldUpdater SNAPSHOT_UPDATER = + AtomicReferenceFieldUpdater.newUpdater(Allocated.class, DataTreeSnapshot.class, "snapshot"); + private final DOMStoreWriteTransaction transaction; + private volatile DataTreeSnapshot snapshot; + + Allocated(final DOMStoreWriteTransaction transaction) { + this.transaction = Preconditions.checkNotNull(transaction); + } + + public DOMStoreWriteTransaction getTransaction() { + return transaction; + } + + @Override + protected DataTreeSnapshot getSnapshot() { + final DataTreeSnapshot ret = snapshot; + Preconditions.checkState(ret != null, "Previous transaction %s is not ready yet", 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()); + } + } + + /** + * Chain is logically shut down, no further allocation allowed. + */ + private static final class Shutdown extends State { + private final String message; + + Shutdown(final String message) { + this.message = Preconditions.checkNotNull(message); + } + + @Override + protected DataTreeSnapshot getSnapshot() { + throw new IllegalStateException(message); + } + } + + @SuppressWarnings("rawtypes") + private static final AtomicReferenceFieldUpdater STATE_UPDATER = + AtomicReferenceFieldUpdater.newUpdater(AbstractSnapshotBackedTransactionChain.class, State.class, "state"); + private static final Logger LOG = LoggerFactory.getLogger(AbstractSnapshotBackedTransactionChain.class); + private static final Shutdown CLOSED = new Shutdown("Transaction chain is closed"); + private static final Shutdown FAILED = new Shutdown("Transaction chain has failed"); + private final Idle idleState; + private volatile State state; + + protected AbstractSnapshotBackedTransactionChain() { + idleState = new Idle(this); + state = idleState; + } + + private Entry getSnapshot() { + final State localState = state; + return new SimpleEntry<>(localState, localState.getSnapshot()); + } + + private boolean recordTransaction(final State expected, final DOMStoreWriteTransaction transaction) { + final State state = new Allocated(transaction); + return STATE_UPDATER.compareAndSet(this, expected, state); + } + + @Override + public final DOMStoreReadTransaction newReadOnlyTransaction() { + final Entry entry = getSnapshot(); + return SnapshotBackedTransactions.newReadTransaction(nextTransactionIdentifier(), getDebugTransactions(), entry.getValue()); + } + + @Override + public final DOMStoreReadWriteTransaction newReadWriteTransaction() { + Entry entry; + DOMStoreReadWriteTransaction ret; + + do { + entry = getSnapshot(); + ret = new SnapshotBackedReadWriteTransaction(nextTransactionIdentifier(), + getDebugTransactions(), entry.getValue(), this); + } while (!recordTransaction(entry.getKey(), ret)); + + return ret; + } + + @Override + public final DOMStoreWriteTransaction newWriteOnlyTransaction() { + Entry entry; + DOMStoreWriteTransaction ret; + + do { + entry = getSnapshot(); + ret = new SnapshotBackedWriteTransaction(nextTransactionIdentifier(), + getDebugTransactions(), entry.getValue(), this); + } while (!recordTransaction(entry.getKey(), ret)); + + return ret; + } + + @Override + protected final void transactionAborted(final SnapshotBackedWriteTransaction tx) { + final State localState = state; + if (localState instanceof Allocated) { + final Allocated allocated = (Allocated)localState; + 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", + tx, this, localState, state); + } + } + } + } + + @Override + protected final DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction tx, final DataTreeModification tree) { + 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); + allocated.setSnapshot(tree); + } else { + LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState); + } + + return createCohort(tx, tree); + } + + @Override + public final void close() { + final State localState = state; + + do { + Preconditions.checkState(!CLOSED.equals(localState), "Transaction chain {} has been closed", this); + + if (FAILED.equals(localState)) { + LOG.debug("Ignoring user close in failed state"); + return; + } + } while (!STATE_UPDATER.compareAndSet(this, localState, CLOSED)); + } + + /** + * Notify the base logic that a previously-submitted transaction has been committed successfully. + * + * @param transaction Transaction which completed successfully. + */ + protected final void onTransactionCommited(final SnapshotBackedWriteTransaction transaction) { + // If the committed transaction was the one we allocated last, + // we clear it and the ready snapshot, so the next transaction + // allocated refers to the data tree directly. + final State localState = state; + + if (!(localState instanceof Allocated)) { + // This can legally happen if the chain is shut down before the transaction was committed + // by the backend. + LOG.debug("Ignoring successful transaction {} in state {}", transaction, localState); + return; + } + + final Allocated allocated = (Allocated)localState; + final DOMStoreWriteTransaction tx = allocated.getTransaction(); + if (!tx.equals(transaction)) { + LOG.debug("Ignoring non-latest successful transaction {} in state {}", transaction, allocated); + return; + } + + if (!STATE_UPDATER.compareAndSet(this, localState, idleState)) { + LOG.debug("Transaction chain {} has already transitioned from {} to {}, not making it idle", this, localState, state); + } + } + + /** + * Notify the base logic that a previously-submitted transaction has failed. + * + * @param transaction Transaction which failed. + * @param cause Failure cause + */ + protected final void onTransactionFailed(final SnapshotBackedWriteTransaction transaction, final Throwable cause) { + LOG.debug("Transaction chain {} failed on transaction {}", this, transaction, cause); + state = FAILED; + } + + /** + * Return the next transaction identifier. + * + * @return transaction identifier. + */ + protected abstract T nextTransactionIdentifier(); + + /** + * Inquire as to whether transactions should record their allocation context. + * + * @return True if allocation context should be recorded. + */ + protected abstract boolean getDebugTransactions(); + + /** + * Take a fresh {@link DataTreeSnapshot} from the backend. + * + * @return A new snapshot. + */ + protected abstract DataTreeSnapshot takeSnapshot(); + + /** + * Create a cohort for driving the transaction through the commit process. + * + * @param transaction Transaction handle + * @param modification {@link DataTreeModification} which needs to be applied to the backend + * @return A {@link DOMStoreThreePhaseCommitCohort} cohort. + */ + protected abstract DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction transaction, final DataTreeModification modification); +} diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedReadTransaction.java similarity index 80% rename from opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadTransaction.java rename to opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedReadTransaction.java index 8b18be432a..8e5957c71a 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadTransaction.java +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedReadTransaction.java @@ -5,16 +5,15 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.md.sal.dom.store.impl; +package org.opendaylight.controller.sal.core.spi.data; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.annotations.Beta; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.controller.sal.core.spi.data.AbstractDOMStoreTransaction; -import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; @@ -28,14 +27,21 @@ import org.slf4j.LoggerFactory; * Implementation of read-only transaction backed by {@link DataTreeSnapshot} * which delegates most of its calls to similar methods provided by underlying snapshot. * + * identifier type */ -final class SnapshotBackedReadTransaction extends AbstractDOMStoreTransaction - implements DOMStoreReadTransaction { - +@Beta +public final class SnapshotBackedReadTransaction extends AbstractDOMStoreTransaction implements DOMStoreReadTransaction { private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadTransaction.class); private volatile DataTreeSnapshot stableSnapshot; - public SnapshotBackedReadTransaction(final Object identifier, final boolean debug, final DataTreeSnapshot snapshot) { + /** + * Creates a new read-only transaction. + * + * @param identifier Transaction Identifier + * @param debug Enable transaction debugging + * @param snapshot Snapshot which will be modified. + */ + SnapshotBackedReadTransaction(final T identifier, final boolean debug, final DataTreeSnapshot snapshot) { super(identifier, debug); this.stableSnapshot = Preconditions.checkNotNull(snapshot); LOG.debug("ReadOnly Tx: {} allocated with snapshot {}", identifier, snapshot); @@ -71,8 +77,7 @@ final class SnapshotBackedReadTransaction extends AbstractDOMStoreTransaction identifier type */ -final class SnapshotBackedReadWriteTransaction extends SnapshotBackedWriteTransaction implements DOMStoreReadWriteTransaction { +@Beta +public final class SnapshotBackedReadWriteTransaction extends SnapshotBackedWriteTransaction implements DOMStoreReadWriteTransaction { private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadWriteTransaction.class); - /** - * Creates new read-write transaction. - * - * @param identifier transaction Identifier - * @param snapshot Snapshot which will be modified. - * @param readyImpl Implementation of ready method. - */ - protected SnapshotBackedReadWriteTransaction(final Object identifier, final boolean debug, - final DataTreeSnapshot snapshot, final TransactionReadyPrototype store) { - super(identifier, debug, snapshot, store); + SnapshotBackedReadWriteTransaction(final T identifier, final boolean debug, + final DataTreeSnapshot snapshot, final TransactionReadyPrototype readyImpl) { + super(identifier, debug, snapshot, readyImpl); } @Override diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedTransactions.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedTransactions.java new file mode 100644 index 0000000000..3368c8aee1 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedTransactions.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.core.spi.data; + +import com.google.common.annotations.Beta; +import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; + +/** + * Public utility class for instantiating snapshot-backed transactions. + */ +@Beta +public final class SnapshotBackedTransactions { + private SnapshotBackedTransactions() { + throw new UnsupportedOperationException("Utility class"); + } + + /** + * Creates a new read-only transaction. + * + * @param identifier Transaction Identifier + * @param debug Enable transaction debugging + * @param snapshot Snapshot which will be modified. + */ + public static SnapshotBackedReadTransaction newReadTransaction(final T identifier, final boolean debug, final DataTreeSnapshot snapshot) { + return new SnapshotBackedReadTransaction(identifier, debug, snapshot); + } + + /** + * Creates a new read-write transaction. + * + * @param identifier transaction Identifier + * @param debug Enable transaction debugging + * @param snapshot Snapshot which will be modified. + * @param readyImpl Implementation of ready method. + */ + public static SnapshotBackedReadWriteTransaction newReadWriteTransaction(final T identifier, final boolean debug, + final DataTreeSnapshot snapshot, final TransactionReadyPrototype readyImpl) { + return new SnapshotBackedReadWriteTransaction(identifier, debug, snapshot, readyImpl); + } + + /** + * Creates a new write-only transaction. + * + * @param identifier transaction Identifier + * @param debug Enable transaction debugging + * @param snapshot Snapshot which will be modified. + * @param readyImpl Implementation of ready method. + */ + public static SnapshotBackedWriteTransaction newWriteTransaction(final T identifier, final boolean debug, + final DataTreeSnapshot snapshot, final TransactionReadyPrototype readyImpl) { + return new SnapshotBackedWriteTransaction(identifier, debug, snapshot, readyImpl); + } +} diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedWriteTransaction.java similarity index 81% rename from opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java rename to opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedWriteTransaction.java index 10e3a7df10..a02d768370 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedWriteTransaction.java @@ -5,17 +5,15 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.md.sal.dom.store.impl; +package org.opendaylight.controller.sal.core.spi.data; import static com.google.common.base.Preconditions.checkState; +import com.google.common.annotations.Beta; import com.google.common.base.MoreObjects.ToStringHelper; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import org.opendaylight.controller.sal.core.spi.data.AbstractDOMStoreTransaction; -import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; -import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; @@ -26,33 +24,27 @@ import org.slf4j.LoggerFactory; /** * Implementation of Write transaction which is backed by * {@link DataTreeSnapshot} and executed according to - * {@link org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype}. + * {@link org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype}. * + * @param Identifier type */ -class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction implements DOMStoreWriteTransaction { +@Beta +public class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction implements DOMStoreWriteTransaction { private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedWriteTransaction.class); + @SuppressWarnings("rawtypes") private static final AtomicReferenceFieldUpdater READY_UPDATER = AtomicReferenceFieldUpdater.newUpdater(SnapshotBackedWriteTransaction.class, TransactionReadyPrototype.class, "readyImpl"); + @SuppressWarnings("rawtypes") private static final AtomicReferenceFieldUpdater TREE_UPDATER = AtomicReferenceFieldUpdater.newUpdater(SnapshotBackedWriteTransaction.class, DataTreeModification.class, "mutableTree"); // non-null when not ready - private volatile TransactionReadyPrototype readyImpl; + private volatile TransactionReadyPrototype readyImpl; // non-null when not committed/closed private volatile DataTreeModification mutableTree; - /** - * Creates new write-only transaction. - * - * @param identifier - * transaction Identifier - * @param snapshot - * Snapshot which will be modified. - * @param readyImpl - * Implementation of ready method. - */ - public SnapshotBackedWriteTransaction(final Object identifier, final boolean debug, - final DataTreeSnapshot snapshot, final TransactionReadyPrototype readyImpl) { + SnapshotBackedWriteTransaction(final T identifier, final boolean debug, + final DataTreeSnapshot snapshot, final TransactionReadyPrototype readyImpl) { super(identifier, debug); this.readyImpl = Preconditions.checkNotNull(readyImpl, "readyImpl must not be null."); mutableTree = snapshot.newModification(); @@ -126,7 +118,7 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction * @param path Path to read * @return null if the the transaction has been closed; */ - protected final Optional> readSnapshotNode(final YangInstanceIdentifier path) { + final Optional> readSnapshotNode(final YangInstanceIdentifier path) { return readyImpl == null ? null : mutableTree.readNode(path); } @@ -136,7 +128,8 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction @Override public DOMStoreThreePhaseCommitCohort ready() { - final TransactionReadyPrototype wasReady = READY_UPDATER.getAndSet(this, null); + @SuppressWarnings("unchecked") + final TransactionReadyPrototype wasReady = READY_UPDATER.getAndSet(this, null); checkState(wasReady != null, "Transaction %s is no longer open", getIdentifier()); LOG.debug("Store transaction: {} : Ready", getIdentifier()); @@ -149,7 +142,8 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction @Override public void close() { - final TransactionReadyPrototype wasReady = READY_UPDATER.getAndSet(this, null); + @SuppressWarnings("unchecked") + final TransactionReadyPrototype wasReady = READY_UPDATER.getAndSet(this, null); if (wasReady != null) { LOG.debug("Store transaction: {} : Closed", getIdentifier()); TREE_UPDATER.lazySet(this, null); @@ -166,21 +160,22 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction /** * Prototype implementation of - * {@link #ready(org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction)} + * {@link #ready(org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction)} * * This class is intended to be implemented by Transaction factories - * responsible for allocation of {@link org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction} and + * responsible for allocation of {@link org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction} and * providing underlying logic for applying implementation. * + * @param identifier type */ - abstract static class TransactionReadyPrototype { + public abstract static class TransactionReadyPrototype { /** * Called when a transaction is closed without being readied. This is not invoked for * transactions which are ready. * * @param tx Transaction which got aborted. */ - protected abstract void transactionAborted(final SnapshotBackedWriteTransaction tx); + protected abstract void transactionAborted(final SnapshotBackedWriteTransaction tx); /** * Returns a commit coordinator associated with supplied transactions. @@ -193,6 +188,6 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction * Modified data tree which has been constructed. * @return DOMStoreThreePhaseCommitCohort associated with transaction */ - protected abstract DOMStoreThreePhaseCommitCohort transactionReady(SnapshotBackedWriteTransaction tx, DataTreeModification tree); + protected abstract DOMStoreThreePhaseCommitCohort transactionReady(SnapshotBackedWriteTransaction tx, DataTreeModification tree); } } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChainedTransactionCommitImpl.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChainedTransactionCommitImpl.java index 05e3d5cb26..ff55c01a25 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChainedTransactionCommitImpl.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChainedTransactionCommitImpl.java @@ -13,13 +13,14 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; import org.opendaylight.controller.sal.core.spi.data.ForwardingDOMStoreThreePhaseCommitCohort; +import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction; final class ChainedTransactionCommitImpl extends ForwardingDOMStoreThreePhaseCommitCohort { - private final SnapshotBackedWriteTransaction transaction; + private final SnapshotBackedWriteTransaction transaction; private final DOMStoreThreePhaseCommitCohort delegate; private final DOMStoreTransactionChainImpl txChain; - ChainedTransactionCommitImpl(final SnapshotBackedWriteTransaction transaction, + ChainedTransactionCommitImpl(final SnapshotBackedWriteTransaction transaction, final DOMStoreThreePhaseCommitCohort delegate, final DOMStoreTransactionChainImpl txChain) { this.transaction = Preconditions.checkNotNull(transaction); this.delegate = Preconditions.checkNotNull(delegate); @@ -37,12 +38,12 @@ final class ChainedTransactionCommitImpl extends ForwardingDOMStoreThreePhaseCom Futures.addCallback(commitFuture, new FutureCallback() { @Override public void onFailure(final Throwable t) { - txChain.onTransactionFailed(transaction, t); + txChain.transactionFailed(transaction, t); } @Override public void onSuccess(final Void result) { - txChain.onTransactionCommited(transaction); + txChain.transactionCommited(transaction); } }); return commitFuture; diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMStoreTransactionChainImpl.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMStoreTransactionChainImpl.java index 3f731cf18b..e357c8dc9c 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMStoreTransactionChainImpl.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMStoreTransactionChainImpl.java @@ -8,217 +8,44 @@ package org.opendaylight.controller.md.sal.dom.store.impl; import com.google.common.base.Preconditions; -import java.util.AbstractMap.SimpleEntry; -import java.util.Map.Entry; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype; -import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction; -import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; +import org.opendaylight.controller.sal.core.spi.data.AbstractSnapshotBackedTransactionChain; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; -import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain; -import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; +import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -final class DOMStoreTransactionChainImpl extends TransactionReadyPrototype implements DOMStoreTransactionChain { - private static abstract class State { - /** - * Allocate a new snapshot. - * - * @return A new snapshot - */ - protected abstract DataTreeSnapshot getSnapshot(); - } - - private static final class Idle extends State { - private final InMemoryDOMDataStore store; - - Idle(final InMemoryDOMDataStore store) { - this.store = Preconditions.checkNotNull(store); - } - - @Override - protected DataTreeSnapshot getSnapshot() { - return store.takeSnapshot(); - } - } - - /** - * We have a transaction out there. - */ - private static final class Allocated extends State { - private static final AtomicReferenceFieldUpdater SNAPSHOT_UPDATER = - AtomicReferenceFieldUpdater.newUpdater(Allocated.class, DataTreeSnapshot.class, "snapshot"); - private final DOMStoreWriteTransaction transaction; - private volatile DataTreeSnapshot snapshot; - - Allocated(final DOMStoreWriteTransaction transaction) { - this.transaction = Preconditions.checkNotNull(transaction); - } - - public DOMStoreWriteTransaction getTransaction() { - return transaction; - } - - @Override - protected DataTreeSnapshot getSnapshot() { - final DataTreeSnapshot ret = snapshot; - Preconditions.checkState(ret != null, "Previous transaction %s is not ready yet", 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()); - } - } - - /** - * Chain is logically shut down, no further allocation allowed. - */ - private static final class Shutdown extends State { - private final String message; - - Shutdown(final String message) { - this.message = Preconditions.checkNotNull(message); - } - - @Override - protected DataTreeSnapshot getSnapshot() { - throw new IllegalStateException(message); - } - } - - private static final AtomicReferenceFieldUpdater STATE_UPDATER = - AtomicReferenceFieldUpdater.newUpdater(DOMStoreTransactionChainImpl.class, State.class, "state"); - private static final Logger LOG = LoggerFactory.getLogger(DOMStoreTransactionChainImpl.class); - private static final Shutdown CLOSED = new Shutdown("Transaction chain is closed"); - private static final Shutdown FAILED = new Shutdown("Transaction chain has failed"); +final class DOMStoreTransactionChainImpl extends AbstractSnapshotBackedTransactionChain { private final InMemoryDOMDataStore store; - private final Idle idleState; - private volatile State state; DOMStoreTransactionChainImpl(final InMemoryDOMDataStore store) { this.store = Preconditions.checkNotNull(store); - idleState = new Idle(store); - state = idleState; - } - - private Entry getSnapshot() { - final State localState = state; - return new SimpleEntry<>(localState, localState.getSnapshot()); - } - - private boolean recordTransaction(final State expected, final DOMStoreWriteTransaction transaction) { - final State state = new Allocated(transaction); - return STATE_UPDATER.compareAndSet(this, expected, state); } @Override - public DOMStoreReadTransaction newReadOnlyTransaction() { - final Entry entry = getSnapshot(); - return new SnapshotBackedReadTransaction(store.nextIdentifier(), store.getDebugTransactions(), entry.getValue()); - } - - @Override - public DOMStoreReadWriteTransaction newReadWriteTransaction() { - Entry entry; - DOMStoreReadWriteTransaction ret; - - do { - entry = getSnapshot(); - ret = new SnapshotBackedReadWriteTransaction(store.nextIdentifier(), - store.getDebugTransactions(), entry.getValue(), this); - } while (!recordTransaction(entry.getKey(), ret)); - - return ret; - } - - @Override - public DOMStoreWriteTransaction newWriteOnlyTransaction() { - Entry entry; - DOMStoreWriteTransaction ret; - - do { - entry = getSnapshot(); - ret = new SnapshotBackedWriteTransaction(store.nextIdentifier(), - store.getDebugTransactions(), entry.getValue(), this); - } while (!recordTransaction(entry.getKey(), ret)); - - return ret; + protected DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction tx, final DataTreeModification tree) { + return new ChainedTransactionCommitImpl(tx, store.transactionReady(tx, tree), this); } @Override - protected void transactionAborted(final SnapshotBackedWriteTransaction tx) { - final State localState = state; - if (localState instanceof Allocated) { - final Allocated allocated = (Allocated)localState; - if (allocated.getTransaction().equals(tx)) { - final boolean success = STATE_UPDATER.compareAndSet(this, localState, idleState); - if (!success) { - LOG.info("State already transitioned from {} to {}", localState, state); - } - } - } + protected DataTreeSnapshot takeSnapshot() { + return store.takeSnapshot(); } @Override - protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction tx, final DataTreeModification tree) { - 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); - allocated.setSnapshot(tree); - } else { - LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState); - } - - return new ChainedTransactionCommitImpl(tx, store.transactionReady(tx, tree), this); + protected String nextTransactionIdentifier() { + return store.nextIdentifier(); } @Override - public void close() { - final State localState = state; - - do { - Preconditions.checkState(!CLOSED.equals(localState), "Transaction chain {} has been closed", this); - - if (FAILED.equals(localState)) { - LOG.debug("Ignoring user close in failed state"); - return; - } - } while (!STATE_UPDATER.compareAndSet(this, localState, CLOSED)); + protected boolean getDebugTransactions() { + return store.getDebugTransactions(); } - void onTransactionFailed(final SnapshotBackedWriteTransaction transaction, final Throwable t) { - LOG.debug("Transaction chain {} failed on transaction {}", this, transaction, t); - state = FAILED; + void transactionFailed(final SnapshotBackedWriteTransaction transaction, final Throwable cause) { + super.onTransactionFailed(transaction, cause); } - void onTransactionCommited(final SnapshotBackedWriteTransaction transaction) { - // If the committed transaction was the one we allocated last, - // we clear it and the ready snapshot, so the next transaction - // allocated refers to the data tree directly. - final State localState = state; - - if (!(localState instanceof Allocated)) { - LOG.debug("Ignoring successful transaction {} in state {}", transaction, localState); - return; - } - - final Allocated allocated = (Allocated)localState; - final DOMStoreWriteTransaction tx = allocated.getTransaction(); - if (!tx.equals(transaction)) { - LOG.debug("Ignoring non-latest successful transaction {} in state {}", transaction, allocated); - return; - } - - if (!STATE_UPDATER.compareAndSet(this, localState, idleState)) { - LOG.debug("Transaction chain {} has already transitioned from {} to {}, not making it idle", this, localState, state); - } + void transactionCommited(final SnapshotBackedWriteTransaction transaction) { + super.onTransactionCommited(transaction); } -} \ No newline at end of file +} diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java index 354abcf69f..7e4056a190 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java @@ -20,7 +20,6 @@ import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListene import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener; -import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype; import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree; import org.opendaylight.controller.sal.core.spi.data.AbstractDOMStoreTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStore; @@ -30,6 +29,9 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCoh import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain; import org.opendaylight.controller.sal.core.spi.data.DOMStoreTreeChangePublisher; import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; +import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedTransactions; +import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction; +import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype; import org.opendaylight.yangtools.concepts.AbstractListenerRegistration; import org.opendaylight.yangtools.concepts.Identifiable; import org.opendaylight.yangtools.concepts.ListenerRegistration; @@ -55,11 +57,11 @@ import org.slf4j.LoggerFactory; * * Implementation of {@link DOMStore} which uses {@link DataTree} and other * classes such as {@link SnapshotBackedWriteTransaction}. - * {@link SnapshotBackedReadTransaction} and {@link ResolveDataChangeEventsTask} + * {@link org.opendaylight.controller.sal.core.spi.data.SnapshotBackedReadTransaction} and {@link ResolveDataChangeEventsTask} * to implement {@link DOMStore} contract. * */ -public class InMemoryDOMDataStore extends TransactionReadyPrototype implements DOMStore, Identifiable, SchemaContextListener, AutoCloseable, DOMStoreTreeChangePublisher { +public class InMemoryDOMDataStore extends TransactionReadyPrototype implements DOMStore, Identifiable, SchemaContextListener, AutoCloseable, DOMStoreTreeChangePublisher { private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataStore.class); private static final ListenableFuture SUCCESSFUL_FUTURE = Futures.immediateFuture(null); private static final ListenableFuture CAN_COMMIT_FUTURE = Futures.immediateFuture(Boolean.TRUE); @@ -120,17 +122,17 @@ public class InMemoryDOMDataStore extends TransactionReadyPrototype implements D @Override public DOMStoreReadTransaction newReadOnlyTransaction() { - return new SnapshotBackedReadTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot()); + return SnapshotBackedTransactions.newReadTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot()); } @Override public DOMStoreReadWriteTransaction newReadWriteTransaction() { - return new SnapshotBackedReadWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this); + return SnapshotBackedTransactions.newReadWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this); } @Override public DOMStoreWriteTransaction newWriteOnlyTransaction() { - return new SnapshotBackedWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this); + return SnapshotBackedTransactions.newWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this); } @Override @@ -214,17 +216,17 @@ public class InMemoryDOMDataStore extends TransactionReadyPrototype implements D } @Override - protected void transactionAborted(final SnapshotBackedWriteTransaction tx) { + protected void transactionAborted(final SnapshotBackedWriteTransaction tx) { LOG.debug("Tx: {} is closed.", tx.getIdentifier()); } @Override - protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction tx, final DataTreeModification tree) { + protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction tx, final DataTreeModification tree) { LOG.debug("Tx: {} is submitted. Modifications: {}", tx.getIdentifier(), tree); return new ThreePhaseCommitImpl(tx, tree); } - Object nextIdentifier() { + String nextIdentifier() { return name + "-" + txCounter.getAndIncrement(); } @@ -236,13 +238,13 @@ public class InMemoryDOMDataStore extends TransactionReadyPrototype implements D } private final class ThreePhaseCommitImpl implements DOMStoreThreePhaseCommitCohort { - private final SnapshotBackedWriteTransaction transaction; + private final SnapshotBackedWriteTransaction transaction; private final DataTreeModification modification; private ResolveDataChangeEventsTask listenerResolver; private DataTreeCandidate candidate; - public ThreePhaseCommitImpl(final SnapshotBackedWriteTransaction writeTransaction, final DataTreeModification modification) { + public ThreePhaseCommitImpl(final SnapshotBackedWriteTransaction writeTransaction, final DataTreeModification modification) { this.transaction = writeTransaction; this.modification = modification; } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java index 9de4892d91..568f88376c 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java @@ -21,12 +21,13 @@ import org.junit.Ignore; import org.junit.Test; import org.mockito.Mockito; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain; import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; +import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedTransactions; +import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; @@ -37,7 +38,6 @@ import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; import org.opendaylight.yangtools.yang.model.api.SchemaContext; - public class InMemoryDataStoreTest { private SchemaContext schemaContext; @@ -268,7 +268,7 @@ public class InMemoryDataStoreTest { Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockSnapshot ) .readNode( Mockito.any( YangInstanceIdentifier.class ) ); - DOMStoreReadTransaction readTx = new SnapshotBackedReadTransaction("1", true, mockSnapshot); + DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot); doReadAndThrowEx( readTx ); } @@ -292,14 +292,14 @@ public class InMemoryDataStoreTest { Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockModification ) .readNode( Mockito.any( YangInstanceIdentifier.class ) ); Mockito.doReturn( mockModification ).when( mockSnapshot ).newModification(); - TransactionReadyPrototype mockReady = Mockito.mock( TransactionReadyPrototype.class ); - DOMStoreReadTransaction readTx = new SnapshotBackedReadWriteTransaction("1", false, mockSnapshot, mockReady); + @SuppressWarnings("unchecked") + TransactionReadyPrototype mockReady = Mockito.mock( TransactionReadyPrototype.class ); + DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction("1", false, mockSnapshot, mockReady); doReadAndThrowEx( readTx ); } - private void doReadAndThrowEx( final DOMStoreReadTransaction readTx ) throws Throwable { - + private static void doReadAndThrowEx( final DOMStoreReadTransaction readTx ) throws Throwable { try { readTx.read(TestModel.TEST_PATH).get(); } catch( ExecutionException e ) {