Merge "TSDR H2 user and install documentation"
[docs.git] / manuals / developer-guide / src / main / asciidoc / controller / md-sal-data-tx.adoc
1 === MD-SAL Data Transactions
2
3 MD-SAL *Data Broker* provides transactional access to conceptual *data trees*
4 representing configuration and operational state.
5
6 NOTE: *Data tree* usually represents state of the modeled data, usually this
7       is state of controller, applications and also external systems (network
8       devices).
9
10 *Transactions* provide *<<_transaction_isolation, stable and isolated view>>*
11 from other currently running transactions. The state of running transaction and
12 underlying data tree is not affected by other concurrently running transactions.
13
14 .Transaction Types
15 Write-Only::
16     Transaction provides only modification capabilities, but does not provide
17     read capabilities. Write-only transaction is allocated using
18     `newWriteOnlyTransaction()`.
19 +
20 NOTE: This allows less state tracking for
21       write-only transactions and allows MD-SAL Clustering to optimize
22       internal representation of transaction in cluster.
23 Read-Write::
24     Transaction provides both read and write capabilities. It is allocated using
25     `newReadWriteTransaction()`.
26 Read-Only::
27     Transaction provides stable read-only view based on current data tree.
28     Read-only view is not affected by any subsequent write transactions.
29     Read-only transaction is allocated using `newReadOnlyTransaction()`.
30 +
31 NOTE: If an application needs to observe changes itself in data tree, it should use
32 *data tree listeners* instead of read-only transactions and polling data tree.
33
34 Transactions may be allocated using the *data broker* itself or using
35 *transaction chain*. In the case of *transaction chain*, the new allocated transaction
36 is not based on current state of data tree, but rather on state introduced by
37 previous transaction from the same chain, even if the commit for previous transaction
38 has not yet occurred (but transaction was submitted).
39
40
41 ==== Write-Only & Read-Write Transaction
42
43 Write-Only and Read-Write transactions provide modification capabilities for
44 the conceptual data trees.
45
46 .Usual workflow for data tree modifications
47 1. application allocates new transactions using `newWriteOnlyTransaction()`
48    or `newReadWriteTransaction()`.
49 2. application <<_modification_of_data_tree,modifies data tree>> using `put`,
50    `merge` and/or `delete`.
51 3. application finishes transaction using <<_submitting_transaction,`submit()`>>,
52    which seals transaction and submits it to be processed.
53 4. application observes the result of the transaction commit using either blocking
54    or asynchronous calls.
55
56 The *initial state* of the write transaction is a *stable snapshot* of the current
57 data tree state captured when transaction was created and it's state and
58 underlying data tree are not affected by other concurrently running transactions.
59
60 Write transactions are *isolated* from other concurrent write transactions. All
61 *<<_transaction_local_state,writes are local>>* to the transaction and
62 represents only a *proposal of state change* for data tree and *are not visible*
63 to any other concurrently running transactions (including read-only transactions).
64
65 The transaction *<<_commit_failure_scenarios,commit may fail>>* due to failing
66 verification of data or concurrent transaction modifying and affected data
67 in an incompatible way.
68
69 ===== Modification of Data Tree
70
71 Write-only and read-write transaction provides following methods to modify
72 data tree:
73
74 put::
75 +
76 [source, java]
77 ----
78 <T> void put(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
79 ----
80 +
81 Stores a piece of data at a specified path. This acts as an *add / replace*
82 operation, which is to say that whole subtree will be replaced by the
83 specified data.
84
85
86 merge::
87 +
88 [source, java]
89 ----
90 <T> void merge(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
91 ----
92 +
93 Merges a piece of data with the existing data at a specified path.
94 Any *pre-existing data* which are not explicitly overwritten *will be preserved*.
95 This means that if you store a container, its child subtrees will be merged.
96
97 delete::
98 +
99 [source, java]
100 ----
101 void delete(LogicalDatastoreType store, InstanceIdentifier<?> path);
102 ----
103 +
104 Removes a whole subtree from a specified path.
105
106 ===== Submitting transaction
107
108 Transaction is submitted to be processed and committed using following method:
109
110 [source, java]
111 ----
112 CheckedFuture<Void,TransactionCommitFailedException> submit();
113 ----
114
115 Applications publish the changes proposed in the transaction by calling `submit()`
116 on the transaction.
117 This *seals the transaction* (preventing any further writes using this transaction)
118 and submits it to be processed and applied to global conceptual data tree.
119 The `submit()` method does not block, but rather returns `ListenableFuture`, which
120 will complete successfully once processing of transaction is finished and changes
121 are applied to data tree. If *commit* of data failed, the future will fail with
122 `TransactionFailedException`.
123
124 Application may listen on commit state asynchronously using `ListenableFuture`.
125
126 [source, java]
127 ----
128 Futures.addCallback( writeTx.submit(), new FutureCallback<Void>() { // <1>
129         public void onSuccess( Void result ) { // <2>
130             LOG.debug("Transaction committed successfully.");
131         }
132
133         public void onFailure( Throwable t ) { // <3>
134             LOG.error("Commit failed.",e);
135         }
136     });
137 ----
138
139 <1> Submits `writeTx` and registers application provided `FutureCallback`
140     on returned future.
141 <2> Invoked when future completed successfully - transaction `writeTx` was
142     successfully committed to data tree.
143 <3> Invoked when future failed - commit of transaction `writeTx` failed.
144     Supplied exception provides additional details and cause of failure.
145
146 If application need to block till commit is finished it may use `checkedGet()`
147 to wait till commit is finished.
148
149 [source, java]
150 ----
151 try {
152     writeTx.submit().checkedGet(); // <1>
153 } catch (TransactionCommitFailedException e) { // <2>
154     LOG.error("Commit failed.",e);
155 }
156 ----
157
158 <1> Submits `writeTx` and blocks till commit of `writeTx` is finished. If
159     commit fails `TransactionCommitFailedException` will be thrown.
160 <2> Catches `TransactionCommitFailedException` and logs it.
161
162 ===== Transaction local state
163
164 Read-Write transactions maintain transaction-local state, which renders all
165 modifications as if they happened, but this is only local to transaction.
166
167 Reads from the transaction returns data as if the previous modifications in
168 transaction already happened.
169
170 Let assume initial state of data tree for `PATH` is `A`.
171 [source, java]
172 ----
173 ReadWriteTransaction rwTx = broker.newReadWriteTransaction(); // <1>
174
175 rwRx.read(OPERATIONAL,PATH).get(); // <2>
176 rwRx.put(OPERATIONAL,PATH,B); // <3>
177 rwRx.read(OPERATIONAL,PATH).get(); // <4>
178 rwRx.put(OPERATIONAL,PATH,C); // <5>
179 rwRx.read(OPERATIONAL,PATH).get(); // <6>
180 ----
181
182 <1> Allocates new `ReadWriteTransaction`.
183 <2> Read from `rwTx` will return value `A` for `PATH`.
184 <3> Writes value `B` to `PATH` using `rwTx`.
185 <4> Read will return value `B` for `PATH`, since previous write occurred in same
186     transaction.
187 <5> Writes value `C` to `PATH` using `rwTx`.
188 <6> Read will return value `C` for `PATH`, since previous write occurred in same
189     transaction.
190
191 ==== Transaction isolation
192
193 Running (not submitted) transactions are isolated from each other and changes
194 done in one transaction are not observable in other currently running
195 transaction.
196
197 Lets assume initial state of data tree for `PATH` is `A`.
198
199 [source, java]
200 ----
201 ReadOnlyTransaction txRead = broker.newReadOnlyTransaction(); // <1>
202 ReadWriteTransaction txWrite = broker.newReadWriteTransaction(); // <2>
203
204 txRead.read(OPERATIONAL,PATH).get(); // <3>
205 txWrite.put(OPERATIONAL,PATH,B); // <4>
206 txWrite.read(OPERATIONAL,PATH).get(); // <5>
207 txWrite.submit().get(); // <6>
208 txRead.read(OPERATIONAL,PATH).get(); // <7>
209 txAfterCommit = broker.newReadOnlyTransaction(); // <8>
210 txAfterCommit.read(OPERATIONAL,PATH).get(); // <9>
211 ----
212
213 <1> Allocates read only transaction, which is based on data tree which
214     contains value  `A` for `PATH`.
215 <2> Allocates read write transaction, which is based on data tree which
216     contains value `A` for `PATH`.
217 <3> Read from read-only transaction returns value `A` for `PATH`.
218 <4> Data tree is updated using read-write transaction, `PATH` contains `B`.
219     Change is not public and only local to transaction.
220 <5> Read from read-write transaction returns value `B` for `PATH`.
221 <6> Submits changes in read-write transaction to be committed to data tree.
222     Once commit will finish, changes will be published and `PATH` will be
223     updated for value `B`. Previously allocated transactions are not affected by
224     this change.
225 <7> Read from previously allocated read-only transaction still returns value `A`
226     for `PATH`, since it provides stable and isolated view.
227 <8> Allocates new read-only transaction, which is based on data tree,
228     which contains value `B` for `PATH`.
229 <9> Read from new read-only transaction return value `B` for `PATH` since
230     read-write transaction was committed.
231
232 NOTE: Examples contain blocking calls on future only to illustrate
233 that action happened after other asynchronous action. The use of the blocking call
234 `ListenableFuture#get()` is discouraged for most use-cases and you should use
235 `Futures#addCallback(ListenableFuture, FutureCallback)` to listen asynchronously
236 for result.
237
238
239 ==== Commit failure scenarios
240
241 A transaction commit may fail because of following reasons:
242
243 Optimistic Lock Failure::
244 Another transaction finished earlier and *modified the same node in a
245 non-compatible way*. The commit (and the returned future) will fail
246 with an `OptimisticLockFailedException`.
247 +
248 It is the responsibility of the
249 caller to create a new transaction and submit the same modification again in
250 order to update data tree.
251 +
252 [WARNING]
253 ====
254 `OptimisticLockFailedException` usually exposes *multiple writers* to
255 the same data subtree, which may conflict on same resources.
256
257 In most cases, retrying may result in a probability of success.
258
259 There are scenarios, albeit unusual, where any number of retries will
260 not succeed. Therefore it is strongly recommended to limit the number of
261 retries (2 or 3) to avoid an endless loop.
262 ====
263
264 Data Validation::
265 The data change introduced by this transaction *did not pass validation* by
266 commit handlers or data was incorrectly structured. The returned future will
267 fail with a `DataValidationFailedException`. User *should not retry* to
268 create new transaction with same data, since it probably will fail again.
269
270 ====== Example conflict of two transactions
271
272 This example illustrates two concurrent transactions, which derived from
273 same initial state of data tree and proposes conflicting modifications.
274
275 [source, java]
276 ----
277 WriteTransaction txA = broker.newWriteTransaction();
278 WriteTransaction txB = broker.newWriteTransaction();
279
280 txA.put(CONFIGURATION, PATH, A);    // <1>
281 txB.put(CONFIGURATION, PATH, B);     // <2>
282
283 CheckedFuture<?,?> futureA = txA.submit(); // <3>
284 CheckedFuture<?,?> futureB = txB.submit(); // <4>
285 ----
286
287 <1> Updates `PATH` to value `A` using `txA`
288 <2> Updates `PATH` to value `B` using `txB`
289 <3> Seals & submits `txA`. The commit will be processed asynchronously and
290     data tree will be updated to contain value `A` for `PATH`.
291     The returned `ListenableFuture' will complete successfully once
292     state is applied to data tree.
293 <4> Seals & submits `txB`. Commit of `txB` will fail, because previous transaction
294     also modified path in a concurrent way. The state introduced by `txB` will
295     not be applied. The returned `ListenableFuture` will fail
296     with `OptimisticLockFailedException` exception, which indicates
297     that concurrent transaction prevented the submitted transaction from being
298     applied.
299
300 ===== Example asynchronous retry-loop
301
302 [source, java]
303 ----
304 private void doWrite( final int tries ) {
305     WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
306
307     MyDataObject data = ...;
308     InstanceIdentifier<MyDataObject> path = ...;
309     writeTx.put( LogicalDatastoreType.OPERATIONAL, path, data );
310
311     Futures.addCallback( writeTx.submit(), new FutureCallback<Void>() {
312         public void onSuccess( Void result ) {
313             // succeeded
314         }
315
316         public void onFailure( Throwable t ) {
317             if( t instanceof OptimisticLockFailedException && (( tries - 1 ) > 0)) {
318                 doWrite( tries - 1 );
319             }
320         }
321       });
322 }
323 ...
324 doWrite( 2 );
325 ----
326
327 ==== Concurrent change compatibility
328
329 There are several sets of changes which could be considered incompatible
330 between two transactions which are derived from same initial state.
331 Rules for conflict detection applies recursively for each subtree
332 level.
333
334 Following table shows  state changes and failures between two concurrent
335 transactions, which are based on same initial state, `tx1` is submitted before
336 `tx2`.
337
338 // FIXME: Providing model and concrete data structures will be probably better.
339
340 INFO: Following tables stores numeric values and shows data using `toString()`
341 to simplify examples.
342
343 .Concurrent change resolution for leaves and leaf-list items
344 [options="header"]
345 |===========================================================
346 |Initial state | tx1  | tx2 | Observable Result
347 |Empty |`put(A,1)` |`put(A,2)` |`tx2` will fail, value of `A` is `1`
348 |Empty |`put(A,1)` |`merge(A,2)` |value of `A` is `2`
349 |Empty |`merge(A,1)` |`put(A,2)` |`tx2` will fail, value of `A` is `1`
350 |Empty |`merge(A,1)` |`merge(A,2)` |`A` is `2`
351 |A=0 |`put(A,1)` |`put(A,2)` |`tx2` will fail, `A` is `1`
352 |A=0 |`put(A,1)` |`merge(A,2)` |`A` is `2`
353 |A=0 |`merge(A,1)` |`put(A,2)` |`tx2` will fail, value of `A` is `1`
354 |A=0 |`merge(A,1)` |`merge(A,2)` |`A` is `2`
355 |A=0 |`delete(A)` |`put(A,2)` |`tx2` will fail, `A` does not exists
356 |A=0 |`delete(A)` |`merge(A,2)` |`A` is `2`
357 |===========================================================
358
359 .Concurrent change resolution for containers, lists, list items
360 [options="header"]
361 |=======================================================================
362 |Initial state |`tx1` |`tx2` |Result
363 |Empty |put(TOP,[]) |put(TOP,[]) |`tx2` will fail, state is TOP=[]
364
365 |Empty |put(TOP,[]) |merge(TOP,[]) |TOP=[]
366
367 |Empty |put(TOP,[FOO=1]) |put(TOP,[BAR=1]) |`tx2` will fail, state is
368 TOP=[FOO=1]
369
370 |Empty |put(TOP,[FOO=1]) |merge(TOP,[BAR=1]) |TOP=[FOO=1,BAR=1]
371
372 |Empty |merge(TOP,[FOO=1]) |put(TOP,[BAR=1]) |`tx2` will fail, state is
373 TOP=[FOO=1]
374
375 |Empty |merge(TOP,[FOO=1]) |merge(TOP,[BAR=1]) |TOP=[FOO=1,BAR=1]
376
377 |TOP=[] |put(TOP,[FOO=1]) |put(TOP,[BAR=1]) |`tx2` will fail, state is
378 TOP=[FOO=1]
379
380 |TOP=[] |put(TOP,[FOO=1]) |merge(TOP,[BAR=1]) |state is
381 TOP=[FOO=1,BAR=1]
382
383 |TOP=[] |merge(TOP,[FOO=1]) |put(TOP,[BAR=1]) |`tx2` will fail, state is
384 TOP=[FOO=1]
385
386 |TOP=[] |merge(TOP,[FOO=1]) |merge(TOP,[BAR=1]) |state is
387 TOP=[FOO=1,BAR=1]
388
389 |TOP=[] |delete(TOP) |put(TOP,[BAR=1]) |`tx2` will fail, state is empty
390 store
391
392 |TOP=[] |delete(TOP) |merge(TOP,[BAR=1]) |state is TOP=[BAR=1]
393
394 |TOP=[] |put(TOP/FOO,1) |put(TOP/BAR,1]) |state is TOP=[FOO=1,BAR=1]
395
396 |TOP=[] |put(TOP/FOO,1) |merge(TOP/BAR,1) |state is TOP=[FOO=1,BAR=1]
397
398 |TOP=[] |merge(TOP/FOO,1) |put(TOP/BAR,1) |state is TOP=[FOO=1,BAR=1]
399
400 |TOP=[] |merge(TOP/FOO,1) |merge(TOP/BAR,1) |state is TOP=[FOO=1,BAR=1]
401
402 |TOP=[] |delete(TOP) |put(TOP/BAR,1) |`tx2` will fail, state is empty
403 store
404
405 |TOP=[] |delete(TOP) |merge(TOP/BAR,1] |`tx2` will fail, state is empty
406 store
407
408 |TOP=[FOO=1] |put(TOP/FOO,2) |put(TOP/BAR,1) |state is TOP=[FOO=2,BAR=1]
409
410 |TOP=[FOO=1] |put(TOP/FOO,2) |merge(TOP/BAR,1) |state is
411 TOP=[FOO=2,BAR=1]
412
413 |TOP=[FOO=1] |merge(TOP/FOO,2) |put(TOP/BAR,1) |state is
414 TOP=[FOO=2,BAR=1]
415
416 |TOP=[FOO=1] |merge(TOP/FOO,2) |merge(TOP/BAR,1) |state is
417 TOP=[FOO=2,BAR=1]
418
419 |TOP=[FOO=1] |delete(TOP/FOO) |put(TOP/BAR,1) |state is TOP=[BAR=1]
420
421 |TOP=[FOO=1] |delete(TOP/FOO) |merge(TOP/BAR,1] |state is TOP=[BAR=1]
422 |=======================================================================