2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.sal.core.spi.data;
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.annotations.Beta;
14 import java.util.AbstractMap.SimpleEntry;
15 import java.util.Map.Entry;
16 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
17 import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedReadTransaction.TransactionClosePrototype;
18 import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
19 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
20 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
25 * Abstract implementation of the {@link DOMStoreTransactionChain} interface relying on {@link DataTreeSnapshot}
26 * supplier and backend commit coordinator.
28 * @param <T> transaction identifier type
29 * @deprecated Use {@link org.opendaylight.mdsal.dom.spi.store.AbstractSnapshotBackedTransactionChain} instead.
33 public abstract class AbstractSnapshotBackedTransactionChain<T> extends TransactionReadyPrototype<T>
34 implements DOMStoreTransactionChain, TransactionClosePrototype<T> {
35 private abstract static class State {
37 * Allocate a new snapshot.
39 * @return A new snapshot
41 protected abstract DataTreeSnapshot getSnapshot(Object transactionId);
44 private static final class Idle extends State {
45 private final AbstractSnapshotBackedTransactionChain<?> chain;
47 Idle(final AbstractSnapshotBackedTransactionChain<?> chain) {
48 this.chain = requireNonNull(chain);
52 protected DataTreeSnapshot getSnapshot(final Object transactionId) {
53 return chain.takeSnapshot();
58 * We have a transaction out there.
60 private static final class Allocated extends State {
61 private static final AtomicReferenceFieldUpdater<Allocated, DataTreeSnapshot> SNAPSHOT_UPDATER =
62 AtomicReferenceFieldUpdater.newUpdater(Allocated.class, DataTreeSnapshot.class, "snapshot");
63 private final DOMStoreWriteTransaction transaction;
64 private volatile DataTreeSnapshot snapshot;
66 Allocated(final DOMStoreWriteTransaction transaction) {
67 this.transaction = requireNonNull(transaction);
70 public DOMStoreWriteTransaction getTransaction() {
75 protected DataTreeSnapshot getSnapshot(final Object transactionId) {
76 final DataTreeSnapshot ret = snapshot;
77 checkState(ret != null,
78 "Could not get snapshot for transaction %s - previous transaction %s is not ready yet",
79 transactionId, transaction.getIdentifier());
83 void setSnapshot(final DataTreeSnapshot snapshot) {
84 final boolean success = SNAPSHOT_UPDATER.compareAndSet(this, null, snapshot);
85 checkState(success, "Transaction %s has already been marked as ready", transaction.getIdentifier());
90 * Chain is logically shut down, no further allocation allowed.
92 private static final class Shutdown extends State {
93 private final String message;
95 Shutdown(final String message) {
96 this.message = requireNonNull(message);
100 protected DataTreeSnapshot getSnapshot(final Object transactionId) {
101 throw new IllegalStateException(message);
105 @SuppressWarnings("rawtypes")
106 private static final AtomicReferenceFieldUpdater<AbstractSnapshotBackedTransactionChain, State> STATE_UPDATER =
107 AtomicReferenceFieldUpdater.newUpdater(AbstractSnapshotBackedTransactionChain.class, State.class, "state");
108 private static final Logger LOG = LoggerFactory.getLogger(AbstractSnapshotBackedTransactionChain.class);
109 private static final Shutdown CLOSED = new Shutdown("Transaction chain is closed");
110 private static final Shutdown FAILED = new Shutdown("Transaction chain has failed");
111 private final Idle idleState;
112 private volatile State state;
114 protected AbstractSnapshotBackedTransactionChain() {
115 idleState = new Idle(this);
119 private Entry<State, DataTreeSnapshot> getSnapshot(final T transactionId) {
120 final State localState = state;
121 return new SimpleEntry<>(localState, localState.getSnapshot(transactionId));
124 private boolean recordTransaction(final State expected, final DOMStoreWriteTransaction transaction) {
125 final State localState = new Allocated(transaction);
126 return STATE_UPDATER.compareAndSet(this, expected, localState);
130 public final DOMStoreReadTransaction newReadOnlyTransaction() {
131 return newReadOnlyTransaction(nextTransactionIdentifier());
134 protected DOMStoreReadTransaction newReadOnlyTransaction(final T transactionId) {
135 final Entry<State, DataTreeSnapshot> entry = getSnapshot(transactionId);
136 return SnapshotBackedTransactions.newReadTransaction(transactionId, getDebugTransactions(), entry.getValue(),
141 public void transactionClosed(final SnapshotBackedReadTransaction<T> tx) {
146 public final DOMStoreReadWriteTransaction newReadWriteTransaction() {
147 return newReadWriteTransaction(nextTransactionIdentifier());
150 protected DOMStoreReadWriteTransaction newReadWriteTransaction(final T transactionId) {
151 Entry<State, DataTreeSnapshot> entry;
152 DOMStoreReadWriteTransaction ret;
155 entry = getSnapshot(transactionId);
156 ret = new SnapshotBackedReadWriteTransaction<>(transactionId, getDebugTransactions(), entry.getValue(),
158 } while (!recordTransaction(entry.getKey(), ret));
164 public final DOMStoreWriteTransaction newWriteOnlyTransaction() {
165 return newWriteOnlyTransaction(nextTransactionIdentifier());
168 protected DOMStoreWriteTransaction newWriteOnlyTransaction(final T transactionId) {
169 Entry<State, DataTreeSnapshot> entry;
170 DOMStoreWriteTransaction ret;
173 entry = getSnapshot(transactionId);
174 ret = new SnapshotBackedWriteTransaction<>(transactionId, getDebugTransactions(), entry.getValue(), this);
175 } while (!recordTransaction(entry.getKey(), ret));
181 protected final void transactionAborted(final SnapshotBackedWriteTransaction<T> tx) {
182 final State localState = state;
183 if (localState instanceof Allocated) {
184 final Allocated allocated = (Allocated)localState;
185 if (allocated.getTransaction().equals(tx)) {
186 final boolean success = STATE_UPDATER.compareAndSet(this, localState, idleState);
189 "Transaction {} aborted, but chain {} state already transitioned from {} to {}, very strange",
190 tx, this, localState, state);
197 protected final DOMStoreThreePhaseCommitCohort transactionReady(
198 final SnapshotBackedWriteTransaction<T> tx,
199 final DataTreeModification tree,
200 final Exception readyError) {
202 final State localState = state;
204 if (localState instanceof Allocated) {
205 final Allocated allocated = (Allocated)localState;
206 final DOMStoreWriteTransaction transaction = allocated.getTransaction();
207 checkState(tx.equals(transaction), "Mis-ordered ready transaction %s last allocated was %s", tx,
209 allocated.setSnapshot(tree);
211 LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
214 return createCohort(tx, tree, readyError);
218 public final void close() {
219 final State localState = state;
222 checkState(!CLOSED.equals(localState), "Transaction chain %s has been closed", this);
223 if (FAILED.equals(localState)) {
224 LOG.debug("Ignoring user close in failed state");
227 } while (!STATE_UPDATER.compareAndSet(this, localState, CLOSED));
231 * Notify the base logic that a previously-submitted transaction has been committed successfully.
233 * @param transaction Transaction which completed successfully.
235 protected final void onTransactionCommited(final SnapshotBackedWriteTransaction<T> transaction) {
236 // If the committed transaction was the one we allocated last,
237 // we clear it and the ready snapshot, so the next transaction
238 // allocated refers to the data tree directly.
239 final State localState = state;
241 if (!(localState instanceof Allocated)) {
242 // This can legally happen if the chain is shut down before the transaction was committed
244 LOG.debug("Ignoring successful transaction {} in state {}", transaction, localState);
248 final Allocated allocated = (Allocated)localState;
249 final DOMStoreWriteTransaction tx = allocated.getTransaction();
250 if (!tx.equals(transaction)) {
251 LOG.debug("Ignoring non-latest successful transaction {} in state {}", transaction, allocated);
255 if (!STATE_UPDATER.compareAndSet(this, localState, idleState)) {
256 LOG.debug("Transaction chain {} has already transitioned from {} to {}, not making it idle",
257 this, localState, state);
262 * Notify the base logic that a previously-submitted transaction has failed.
264 * @param transaction Transaction which failed.
265 * @param cause Failure cause
267 protected final void onTransactionFailed(final SnapshotBackedWriteTransaction<T> transaction,
268 final Throwable cause) {
269 LOG.debug("Transaction chain {} failed on transaction {}", this, transaction, cause);
274 * Return the next transaction identifier.
276 * @return transaction identifier.
278 protected abstract T nextTransactionIdentifier();
281 * Inquire as to whether transactions should record their allocation context.
283 * @return True if allocation context should be recorded.
285 protected abstract boolean getDebugTransactions();
288 * Take a fresh {@link DataTreeSnapshot} from the backend.
290 * @return A new snapshot.
292 protected abstract DataTreeSnapshot takeSnapshot();
295 * Create a cohort for driving the transaction through the commit process.
297 * @param transaction Transaction handle
298 * @param modification {@link DataTreeModification} which needs to be applied to the backend
299 * @param operationError Any previous error that could be reported through three phase commit
300 * @return A {@link DOMStoreThreePhaseCommitCohort} cohort.
302 protected abstract DOMStoreThreePhaseCommitCohort createCohort(SnapshotBackedWriteTransaction<T> transaction,
303 DataTreeModification modification,
304 Exception operationError);