P
- Type of path (subtree identifier), which represents location in
treeD
- Type of data (payload), which represents data payloadpublic interface AsyncWriteTransaction<P extends org.opendaylight.yangtools.concepts.Path<P>,D> extends AsyncTransaction<P,D>
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 submit()
on the transaction. This seals the transaction
(preventing any further writes using this transaction) and submits 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 submit()
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.
Modifier and Type | Field and Description |
---|---|
static org.opendaylight.yangtools.util.concurrent.ExceptionMapper<TransactionCommitFailedException> |
SUBMIT_EXCEPTION_MAPPER
Deprecated.
|
Modifier and Type | Method and Description |
---|---|
boolean |
cancel()
Cancels the transaction.
|
@NonNull com.google.common.util.concurrent.FluentFuture<? extends org.opendaylight.mdsal.common.api.CommitInfo> |
commit()
Submits this transaction to be asynchronously applied to update the logical data tree.
|
void |
delete(LogicalDatastoreType store,
P path)
Removes a piece of data from specified path.
|
default com.google.common.util.concurrent.CheckedFuture<Void,TransactionCommitFailedException> |
submit()
Deprecated.
Use
commit() instead. |
getIdentifier
@Deprecated static final org.opendaylight.yangtools.util.concurrent.ExceptionMapper<TransactionCommitFailedException> SUBMIT_EXCEPTION_MAPPER
submit()
method and is not intended for general use.boolean cancel()
Transactions can only be cancelled if it's state is new or submitted.
Invoking cancel() on a failed or cancelled transaction will have no effect, and transaction is considered cancelled.
Invoking cancel() on a finished transaction (future returned by submit()
already completed will always
fail (return false).
void delete(LogicalDatastoreType store, P path)
store
- Logical data store which should be modifiedpath
- Data object pathIllegalStateException
- if the transaction as already been submitted or cancelled@Deprecated @CheckReturnValue default com.google.common.util.concurrent.CheckedFuture<Void,TransactionCommitFailedException> submit()
commit()
instead.Note: It is strongly recommended to process the CheckedFuture result in an asynchronous manner rather than using the blocking get() method. See example usage below.
This call logically seals the transaction, which prevents the client from
further changing data tree using this transaction. Any subsequent calls to
delete(LogicalDatastoreType, Path)
will fail with
IllegalStateException
.
The transaction is marked as submitted 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
(AsyncConfigurationCommitHandler
) if the transaction changes the data tree.
The effects of a successful commit of data depends on data tree change listeners and commit participants
(AsyncConfigurationCommitHandler
) 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.submit(), new FutureCallback<Void>() { public void onSuccess( Void 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
OptimisticLockFailedException
. It is the responsibility of the
caller to create a new transaction and submit the same modification again in
order to update data tree. Warning: In most cases, retrying after an
OptimisticLockFailedException will result in a high probability of success.
However, there are scenarios, albeit unusual, where any number of retries will
not succeed. Therefore it is strongly recommended to limit the number of retries (2 or 3)
to avoid an endless loop.
DataValidationFailedException
. User should not retry to
create new transaction with same data, since it probably will fail again.
There are several sets of changes which could be considered incompatible between two transactions which are derived from same initial state. Rules for conflict detection applies recursively for each subtree level.
Following table shows state changes and failures between two concurrent transactions, which are based on same initial state, Tx 1 completes successfully before Tx 2 is submitted.
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 |
Following table shows state changes and failures between two concurrent transactions, which are based on same initial state, Tx 1 completes successfully before Tx 2 is submitted.
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] |
This example illustrates two concurrent transactions, which derived from same initial state of data tree and proposes conflicting modifications.
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.submit(); // transaction A is sealed and submitted ListenebleFuture futureB = txB.submit(); // transaction B is sealed and submitted
Commit of transaction A will be processed asynchronously and data tree
will be updated to contain value A
for PATH
.
Returned ListenableFuture
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 ListenableFuture
object will fail
with OptimisticLockFailedException
exception, which indicates to
client that concurrent transaction prevented the submitted transaction from being
applied.
TransactionCommitFailedException
or an exception
derived from TransactionCommitFailedException.IllegalStateException
- if the transaction is not new@CheckReturnValue @NonNull com.google.common.util.concurrent.FluentFuture<? extends org.opendaylight.mdsal.common.api.CommitInfo> commit()
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 IllegalStateException
. The transaction is marked as submitted 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.
A successful commit produces implementation-specific 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.
TransactionCommitFailedException
or an exception derived from TransactionCommitFailedException.IllegalStateException
- if the transaction is already committed or was canceled.Copyright © 2019 OpenDaylight. All rights reserved.