/* * 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.mdsal.dom.api; import com.google.common.util.concurrent.FluentFuture; import edu.umd.cs.findbugs.annotations.CheckReturnValue; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.mdsal.common.api.CommitInfo; import org.opendaylight.mdsal.common.api.DataValidationFailedException; import org.opendaylight.mdsal.common.api.OptimisticLockFailedException; import org.opendaylight.mdsal.common.api.TransactionCommitFailedException; /** * Write transaction provides mutation capabilities for a data tree. * *
* Initial state of write transaction is a stable snapshot of the current data tree. * The state is captured when the transaction is created and its state and underlying * data tree are not affected by other concurrently running transactions. * *
* Write transactions are isolated from other concurrent write transactions. All * writes are local to the transaction and represent only a proposal of state * change for the data tree and it is not visible to any other concurrently running * transaction. * *
* Applications make changes to the local data tree in the transaction by via the * put, merge, and delete operations. * *
* Performing the following put operations: * *
* 1) container { list [ a ] } * 2) container { list [ b ] } ** will result in the following data being present: * *
* container { list [ b ] } **
* Performing the following merge operations: * *
* 1) container { list [ a ] } * 2) container { list [ b ] } ** will result in the following data being present: * *
* container { list [ a, b ] } ** This also means that storing the container will preserve any * augmentations which have been attached to it. * *
* After applying changes to the local data tree, applications publish the changes proposed in the * transaction by calling {@link #commit} on the transaction. This seals the transaction * (preventing any further writes using this transaction) and commits it to be * processed and applied to global conceptual data tree. * *
* The transaction commit may fail due to a concurrent transaction modifying and committing data in * an incompatible way. See {@link #commit} for more concrete commit failure examples. * *
* Implementation Note: This interface is not intended to be implemented * by users of MD-SAL, but only to be consumed by them. */ public interface DOMDataTreeWriteTransaction extends DOMDataTreeTransaction, DOMDataTreeWriteOperations { /** * Commits this transaction to be asynchronously applied to update the logical data tree. The returned * {@link FluentFuture} conveys the result of applying the data changes. * *
* This call logically seals the transaction, which prevents the client from further changing the data tree using
* this transaction. Any subsequent calls to put(LogicalDatastoreType, Path, Object)
,
* merge(LogicalDatastoreType, Path, Object)
, delete(LogicalDatastoreType, Path)
will fail
* with {@link IllegalStateException}. The transaction is marked as committed and enqueued into the data store
* back-end for processing.
*
*
* Whether or not the commit is successful is determined by versioning of the data tree and validation of registered * commit participants if the transaction changes the data tree. * *
* The effects of a successful commit of data depends on listeners and commit participants that are registered with * the data broker. * *
* private void doWrite(final int tries) { * WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction(); * MyDataObject data = ...; * InstanceIdentifier<MyDataObject> path = ...; * writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data); * Futures.addCallback(writeTx.commit(), new FutureCallback<CommitInfo>() { * public void onSuccess(CommitInfo result) { * // succeeded * } * public void onFailure(Throwable t) { * if (t instanceof OptimisticLockFailedException) { * if(( tries - 1) > 0 ) { * // do retry * doWrite(tries - 1); * } else { * // out of retries * } * } else { * // failed due to another type of TransactionCommitFailedException. * } * }); * } * ... * doWrite(2); ** *
* Transaction may fail because of multiple reasons, such as *
Initial state | *Tx 1 | *Tx 2 | *Result | *
---|---|---|---|
Empty | *put(A,1) | *put(A,2) | *Tx 2 will fail, state is A=1 | *
Empty | *put(A,1) | *merge(A,2) | *A=2 | *
Empty | *merge(A,1) | *put(A,2) | *Tx 2 will fail, state is A=1 | *
Empty | *merge(A,1) | *merge(A,2) | *A=2 | *
A=0 | *put(A,1) | *put(A,2) | *Tx 2 will fail, A=1 | *
A=0 | *put(A,1) | *merge(A,2) | *A=2 | *
A=0 | *merge(A,1) | *put(A,2) | *Tx 2 will fail, A=1 | *
A=0 | *merge(A,1) | *merge(A,2) | *A=2 | *
A=0 | *delete(A) | *put(A,2) | *Tx 2 will fail, A does not exists | *
A=0 | *delete(A) | *merge(A,2) | *A=2 | *
Initial state | *Tx 1 | *Tx 2 | *Result | *
---|---|---|---|
Empty | *put(TOP,[]) | *put(TOP,[]) | *Tx 2 will fail, state is TOP=[] | *
Empty | *put(TOP,[]) | *merge(TOP,[]) | *TOP=[] | *
Empty | *put(TOP,[FOO=1]) | *put(TOP,[BAR=1]) | *Tx 2 will fail, state is TOP=[FOO=1] | *
Empty | *put(TOP,[FOO=1]) | *merge(TOP,[BAR=1]) | *TOP=[FOO=1,BAR=1] | *
Empty | *merge(TOP,[FOO=1]) | *put(TOP,[BAR=1]) | *Tx 2 will fail, state is TOP=[FOO=1] | *
Empty | *merge(TOP,[FOO=1]) | *merge(TOP,[BAR=1]) | *TOP=[FOO=1,BAR=1] | *
TOP=[] | *put(TOP,[FOO=1]) | *put(TOP,[BAR=1]) | *Tx 2 will fail, state is TOP=[FOO=1] | *
TOP=[] | *put(TOP,[FOO=1]) | *merge(TOP,[BAR=1]) | *state is TOP=[FOO=1,BAR=1] | *
TOP=[] | *merge(TOP,[FOO=1]) | *put(TOP,[BAR=1]) | *Tx 2 will fail, state is TOP=[FOO=1] | *
TOP=[] | *merge(TOP,[FOO=1]) | *merge(TOP,[BAR=1]) | *state is TOP=[FOO=1,BAR=1] | *
TOP=[] | *delete(TOP) | *put(TOP,[BAR=1]) | *Tx 2 will fail, state is empty store | *
TOP=[] | *delete(TOP) | *merge(TOP,[BAR=1]) | *state is TOP=[BAR=1] | *
TOP=[] | *put(TOP/FOO,1) | *put(TOP/BAR,1]) | *state is TOP=[FOO=1,BAR=1] | *
TOP=[] | *put(TOP/FOO,1) | *merge(TOP/BAR,1) | *state is TOP=[FOO=1,BAR=1] | *
TOP=[] | *merge(TOP/FOO,1) | *put(TOP/BAR,1) | *state is TOP=[FOO=1,BAR=1] | *
TOP=[] | *merge(TOP/FOO,1) | *merge(TOP/BAR,1) | *state is TOP=[FOO=1,BAR=1] | *
TOP=[] | *delete(TOP) | *put(TOP/BAR,1) | *Tx 2 will fail, state is empty store | *
TOP=[] | *delete(TOP) | *merge(TOP/BAR,1] | *Tx 2 will fail, state is empty store | *
TOP=[FOO=1] | *put(TOP/FOO,2) | *put(TOP/BAR,1) | *state is TOP=[FOO=2,BAR=1] | *
TOP=[FOO=1] | *put(TOP/FOO,2) | *merge(TOP/BAR,1) | *state is TOP=[FOO=2,BAR=1] | *
TOP=[FOO=1] | *merge(TOP/FOO,2) | *put(TOP/BAR,1) | *state is TOP=[FOO=2,BAR=1] | *
TOP=[FOO=1] | *merge(TOP/FOO,2) | *merge(TOP/BAR,1) | *state is TOP=[FOO=2,BAR=1] | *
TOP=[FOO=1] | *delete(TOP/FOO) | *put(TOP/BAR,1) | *state is TOP=[BAR=1] | *
TOP=[FOO=1] | *delete(TOP/FOO) | *merge(TOP/BAR,1] | *state is TOP=[BAR=1] | *
* txA = broker.newWriteTransaction(); // allocates new transaction, data tree is empty * txB = broker.newWriteTransaction(); // allocates new transaction, data tree is empty * txA.put(CONFIGURATION, PATH, A); // writes to PATH value A * txB.put(CONFIGURATION, PATH, B) // writes to PATH value B * ListenableFuture futureA = txA.commit(); // transaction A is sealed and committed * ListenebleFuture futureB = txB.commit(); // transaction B is sealed and committed ** Commit of transaction A will be processed asynchronously and data tree will be updated to * contain value
A
for PATH
. Returned {@link FluentFuture} will
* successfully complete once state is applied to data tree.
* Commit of Transaction B will fail, because previous transaction also modified path in a
* concurrent way. The state introduced by transaction B will not be applied. Returned
* {@link FluentFuture} object will fail with {@link OptimisticLockFailedException}
* exception, which indicates to client that concurrent transaction prevented the committed
* transaction from being applied. * A successful commit produces implementation-specific {@link CommitInfo} structure, which is used to communicate * post-condition information to the caller. Such information can contain commit-id, timing information or any * other information the implementation wishes to share. * * @return a FluentFuture containing the result of the commit information. The Future blocks until the commit * operation is complete. A successful commit returns nothing. On failure, the Future will fail with a * {@link TransactionCommitFailedException} or an exception derived from TransactionCommitFailedException. * @throws IllegalStateException if the transaction is already committed or was canceled. */ @CheckReturnValue @NonNull FluentFuture extends @NonNull CommitInfo> commit(); /** * Cancels the transaction. Transactions can only be cancelled if it was not yet committed. * Invoking cancel() on failed or already cancelled will have no effect, and transaction is considered cancelled. * Invoking cancel() on finished transaction (future returned by {@link #commit()} already successfully completed) * will always fail (return false). * * @return {@code false} if the task could not be cancelled, typically because it has already completed normally; * {@code true} otherwise */ boolean cancel(); }