Delay snapshot backed transaction ready error
[controller.git] / opendaylight / md-sal / sal-inmemory-datastore / src / test / java / org / opendaylight / controller / md / sal / dom / store / impl / InMemoryDataStoreTest.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.md.sal.dom.store.impl;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertTrue;
14 import com.google.common.base.Optional;
15 import com.google.common.util.concurrent.CheckedFuture;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import com.google.common.util.concurrent.MoreExecutors;
18 import java.util.concurrent.ExecutionException;
19 import org.junit.Assert;
20 import org.junit.Before;
21 import org.junit.Ignore;
22 import org.junit.Test;
23 import org.mockito.Mockito;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
26 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
27 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
28 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
29 import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
30 import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
31 import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedTransactions;
32 import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
37 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
39 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
40 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
41 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
43 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
44
45 public class InMemoryDataStoreTest {
46
47     private SchemaContext schemaContext;
48     private InMemoryDOMDataStore domStore;
49
50     @Before
51     public void setupStore() {
52         domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
53         schemaContext = TestModel.createTestContext();
54         domStore.onGlobalContextUpdated(schemaContext);
55     }
56
57     @Test
58     public void testTransactionIsolation() throws InterruptedException, ExecutionException {
59
60         assertNotNull(domStore);
61
62         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
63         assertNotNull(readTx);
64
65         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
66         assertNotNull(writeTx);
67
68         /**
69          * Writes /test in writeTx
70          */
71         NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
72         writeTx.write(TestModel.TEST_PATH, testNode);
73
74         /**
75          * Reads /test from writeTx Read should return container.
76          */
77         ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
78         assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
79         assertEquals("read: data", testNode, writeTxContainer.get().get());
80
81         /**
82          * Reads /test from readTx Read should return Absent.
83          */
84         ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx.read(TestModel.TEST_PATH);
85         assertEquals("read: isPresent", false, readTxContainer.get().isPresent());
86     }
87
88     @Test
89     public void testTransactionCommit() throws InterruptedException, ExecutionException {
90
91         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
92         assertNotNull(writeTx);
93
94         /**
95          * Writes /test in writeTx
96          */
97         NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
98         writeTx.write(TestModel.TEST_PATH, testNode);
99
100         /**
101          * Reads /test from writeTx Read should return container.
102          */
103         ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
104         assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
105         assertEquals("read: data", testNode, writeTxContainer.get().get());
106
107         DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
108
109         assertThreePhaseCommit(cohort);
110
111         Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
112                 .get();
113         assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
114         assertEquals("After commit read: data", testNode, afterCommitRead.get());
115     }
116
117     @Test
118     public void testDelete() throws Exception {
119
120         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
121         assertNotNull( writeTx );
122
123         // Write /test and commit
124
125         writeTx.write( TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
126
127         assertThreePhaseCommit( writeTx.ready() );
128
129         Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().
130                 read(TestModel.TEST_PATH ).get();
131         assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
132
133         // Delete /test and verify
134
135         writeTx = domStore.newWriteOnlyTransaction();
136
137         writeTx.delete( TestModel.TEST_PATH );
138
139         assertThreePhaseCommit( writeTx.ready() );
140
141         afterCommitRead = domStore.newReadOnlyTransaction().
142                 read(TestModel.TEST_PATH ).get();
143         assertEquals( "After commit read: isPresent", false, afterCommitRead.isPresent() );
144     }
145
146     @Test
147     public void testMerge() throws Exception {
148
149         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
150         assertNotNull( writeTx );
151
152         ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
153                 .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
154                 .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
155                         .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
156                                                             TestModel.ID_QNAME, 1 ) ).build() ).build();
157
158         writeTx.merge( TestModel.TEST_PATH, containerNode );
159
160         assertThreePhaseCommit( writeTx.ready() );
161
162         Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().
163                 read(TestModel.TEST_PATH ).get();
164         assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
165         assertEquals( "After commit read: data", containerNode, afterCommitRead.get() );
166
167         // Merge a new list entry node
168
169         writeTx = domStore.newWriteOnlyTransaction();
170         assertNotNull( writeTx );
171
172         containerNode = ImmutableContainerNodeBuilder.create()
173                 .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
174                 .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
175                         .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
176                                                             TestModel.ID_QNAME, 1 ) )
177                         .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
178                                                             TestModel.ID_QNAME, 2 ) ).build() ).build();
179
180         writeTx.merge( TestModel.TEST_PATH, containerNode );
181
182         assertThreePhaseCommit( writeTx.ready() );
183
184         afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH ).get();
185         assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
186         assertEquals( "After commit read: data", containerNode, afterCommitRead.get() );
187     }
188
189
190     @Test
191     public void testExistsForExistingData() throws Exception {
192
193         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
194         assertNotNull( writeTx );
195
196         ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
197             .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
198             .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
199                 .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
200                     TestModel.ID_QNAME, 1 ) ).build() ).build();
201
202         writeTx.merge( TestModel.TEST_PATH, containerNode );
203
204         CheckedFuture<Boolean, ReadFailedException> exists =
205             writeTx.exists(TestModel.TEST_PATH);
206
207         assertEquals(true, exists.checkedGet());
208
209         DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
210
211         ready.preCommit().get();
212
213         ready.commit().get();
214
215         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
216         assertNotNull( readTx );
217
218         exists =
219             readTx.exists(TestModel.TEST_PATH);
220
221         assertEquals(true, exists.checkedGet());
222     }
223
224     @Test
225     public void testExistsForNonExistingData() throws Exception {
226
227         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
228         assertNotNull( writeTx );
229
230         CheckedFuture<Boolean, ReadFailedException> exists =
231             writeTx.exists(TestModel.TEST_PATH);
232
233         assertEquals(false, exists.checkedGet());
234
235         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
236         assertNotNull( readTx );
237
238         exists =
239             readTx.exists(TestModel.TEST_PATH);
240
241         assertEquals(false, exists.checkedGet());
242     }
243
244     @Test(expected=ReadFailedException.class)
245     public void testExistsThrowsReadFailedException() throws Exception {
246
247         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
248         assertNotNull( readTx );
249
250         readTx.close();
251
252         readTx.exists(TestModel.TEST_PATH).checkedGet();
253     }
254
255
256
257     @Test(expected=ReadFailedException.class)
258     public void testReadWithReadOnlyTransactionClosed() throws Throwable {
259
260         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
261         assertNotNull( readTx );
262
263         readTx.close();
264
265         doReadAndThrowEx( readTx );
266     }
267
268     @Test(expected=ReadFailedException.class)
269     public void testReadWithReadOnlyTransactionFailure() throws Throwable {
270
271         DataTreeSnapshot mockSnapshot = Mockito.mock( DataTreeSnapshot.class );
272         Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockSnapshot )
273         .readNode( Mockito.any( YangInstanceIdentifier.class ) );
274
275         DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
276
277         doReadAndThrowEx( readTx );
278     }
279
280     @Test(expected=ReadFailedException.class)
281     public void testReadWithReadWriteTransactionClosed() throws Throwable {
282
283         DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
284         assertNotNull( readTx );
285
286         readTx.close();
287
288         doReadAndThrowEx( readTx );
289     }
290
291     @Test(expected=ReadFailedException.class)
292     public void testReadWithReadWriteTransactionFailure() throws Throwable {
293
294         DataTreeSnapshot mockSnapshot = Mockito.mock( DataTreeSnapshot.class );
295         DataTreeModification mockModification = Mockito.mock( DataTreeModification.class );
296         Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockModification )
297         .readNode( Mockito.any( YangInstanceIdentifier.class ) );
298         Mockito.doReturn( mockModification ).when( mockSnapshot ).newModification();
299         @SuppressWarnings("unchecked")
300         TransactionReadyPrototype<String> mockReady = Mockito.mock( TransactionReadyPrototype.class );
301         DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction("1", false, mockSnapshot, mockReady);
302
303         doReadAndThrowEx( readTx );
304     }
305
306     private static void doReadAndThrowEx( final DOMStoreReadTransaction readTx ) throws Throwable {
307         try {
308             readTx.read(TestModel.TEST_PATH).get();
309         } catch( ExecutionException e ) {
310             throw e.getCause();
311         }
312     }
313
314     @Test(expected=IllegalStateException.class)
315     public void testWriteWithTransactionReady() throws Exception {
316
317         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
318
319         writeTx.ready();
320
321         // Should throw ex
322         writeTx.write( TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
323     }
324
325     @Test(expected=IllegalStateException.class)
326     public void testReadyWithTransactionAlreadyReady() throws Exception {
327
328         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
329
330         writeTx.ready();
331
332         // Should throw ex
333         writeTx.ready();
334     }
335
336     @Test
337     public void testReadyWithMissingMandatoryData() throws InterruptedException {
338         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
339         NormalizedNode<?, ?> testNode = ImmutableContainerNodeBuilder.create()
340                 .withNodeIdentifier(new NodeIdentifier(TestModel.MANDATORY_DATA_TEST_QNAME))
341                 .addChild(ImmutableNodes.leafNode(TestModel.OPTIONAL_QNAME, "data"))
342                 .build();
343         writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
344         DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
345         try {
346             ready.canCommit().get();
347             Assert.fail("Expected exception on canCommit");
348         } catch (ExecutionException e) {
349             // nop
350         }
351     }
352
353     @Test
354     public void testTransactionAbort() throws InterruptedException, ExecutionException {
355
356         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
357         assertNotNull(writeTx);
358
359         assertTestContainerWrite(writeTx);
360
361         DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
362
363         assertTrue(cohort.canCommit().get().booleanValue());
364         cohort.preCommit().get();
365         cohort.abort().get();
366
367         Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
368                 .get();
369         assertFalse(afterCommitRead.isPresent());
370     }
371
372     @Test
373     public void testTransactionChain() throws InterruptedException, ExecutionException {
374         DOMStoreTransactionChain txChain = domStore.createTransactionChain();
375         assertNotNull(txChain);
376
377         /**
378          * We alocate new read-write transaction and write /test
379          *
380          *
381          */
382         DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
383         assertTestContainerWrite(firstTx);
384
385         /**
386          * First transaction is marked as ready, we are able to allocate chained
387          * transactions
388          */
389         DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
390
391         /**
392          * We alocate chained transaction - read transaction, note first one is
393          * still not commited to datastore.
394          */
395         DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
396
397         /**
398          *
399          * We test if we are able to read data from tx, read should not fail
400          * since we are using chained transaction.
401          *
402          *
403          */
404         assertTestContainerExists(secondReadTx);
405
406         /**
407          *
408          * We alocate next transaction, which is still based on first one, but
409          * is read-write.
410          *
411          */
412         DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
413
414         /**
415          * We test existence of /test in third transaction container should
416          * still be visible from first one (which is still uncommmited).
417          *
418          *
419          */
420         assertTestContainerExists(thirdDeleteTx);
421
422         /**
423          * We delete node in third transaction
424          */
425         thirdDeleteTx.delete(TestModel.TEST_PATH);
426
427         /**
428          * third transaction is sealed.
429          */
430         DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
431
432         /**
433          * We commit first transaction
434          *
435          */
436         assertThreePhaseCommit(firstWriteTxCohort);
437
438         // Alocates store transacion
439         DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
440         /**
441          * We verify transaction is commited to store, container should exists
442          * in datastore.
443          */
444         assertTestContainerExists(storeReadTx);
445         /**
446          * We commit third transaction
447          *
448          */
449         assertThreePhaseCommit(thirdDeleteTxCohort);
450     }
451
452     @Test
453     @Ignore
454     public void testTransactionConflict() throws InterruptedException, ExecutionException {
455         DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
456         DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
457         assertTestContainerWrite(txOne);
458         assertTestContainerWrite(txTwo);
459
460         /**
461          * Commits transaction
462          */
463         assertThreePhaseCommit(txOne.ready());
464
465         /**
466          * Asserts that txTwo could not be commited
467          */
468         assertFalse(txTwo.ready().canCommit().get());
469     }
470
471     private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort)
472             throws InterruptedException, ExecutionException {
473         assertTrue(cohort.canCommit().get().booleanValue());
474         cohort.preCommit().get();
475         cohort.commit().get();
476     }
477
478     private static Optional<NormalizedNode<?, ?>> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
479             throws InterruptedException, ExecutionException {
480         /**
481          *
482          * Writes /test in writeTx
483          *
484          */
485         writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
486
487         return assertTestContainerExists(writeTx);
488     }
489
490     /**
491      * Reads /test from readTx Read should return container.
492      */
493     private static Optional<NormalizedNode<?, ?>> assertTestContainerExists(final DOMStoreReadTransaction readTx)
494             throws InterruptedException, ExecutionException {
495
496         ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
497         assertTrue(writeTxContainer.get().isPresent());
498         return writeTxContainer.get();
499     }
500
501 }