Expose executor related settings for InMemoryDOMDataTreeShard
[mdsal.git] / dom / mdsal-dom-broker / src / test / java / org / opendaylight / mdsal / dom / broker / ShardedDOMDataTreeProducerMultiShardTest.java
1 package org.opendaylight.mdsal.dom.broker;
2
3 import static org.junit.Assert.assertEquals;
4 import static org.junit.Assert.assertTrue;
5 import static org.mockito.Matchers.any;
6 import static org.mockito.Matchers.anyCollection;
7 import static org.mockito.Matchers.anyMap;
8 import static org.mockito.Mockito.doNothing;
9 import static org.mockito.Mockito.doReturn;
10 import static org.mockito.Mockito.inOrder;
11 import static org.mockito.Mockito.timeout;
12 import static org.mockito.Mockito.verify;
13 import static org.mockito.Mockito.verifyNoMoreInteractions;
14
15 import com.google.common.util.concurrent.Futures;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.concurrent.ExecutorService;
21 import java.util.concurrent.Executors;
22 import org.junit.Before;
23 import org.junit.Test;
24 import org.mockito.ArgumentCaptor;
25 import org.mockito.Captor;
26 import org.mockito.InOrder;
27 import org.mockito.Mockito;
28 import org.mockito.MockitoAnnotations;
29 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
30 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
31 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
32 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
33 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteCursor;
34 import org.opendaylight.mdsal.dom.broker.util.TestModel;
35 import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardProducer;
36 import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardWriteTransaction;
37 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataTreeShard;
38 import org.opendaylight.mdsal.dom.store.inmemory.WriteableDOMDataTreeShard;
39 import org.opendaylight.yangtools.concepts.ListenerRegistration;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
43 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
47 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
48 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
49 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
50 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
51 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 public class ShardedDOMDataTreeProducerMultiShardTest {
56
57     private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTreeProducerMultiShardTest.class);
58
59     private static SchemaContext schemaContext = null;
60
61     static {
62         try {
63             schemaContext = TestModel.createTestContext();
64         } catch (final ReactorException e) {
65             LOG.error("Unable to create schema context for TestModel", e);
66         }
67     }
68
69     private static final DOMDataTreeIdentifier ROOT_ID =
70             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.EMPTY);
71     private static final DOMDataTreeIdentifier TEST_ID =
72             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST_PATH);
73
74     private static final DOMDataTreeIdentifier TEST2_ID =
75             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST2_PATH);
76
77     private static final DOMDataTreeIdentifier INNER_CONTAINER_ID =
78             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.INNER_CONTAINER_PATH);
79     private static final DOMDataTreeIdentifier ANOTHER_SHARD_ID =
80             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.ANOTHER_SHARD_PATH);
81
82     private InMemoryDOMDataTreeShard rootShard;
83     private InMemoryDOMDataTreeShard anotherInnerShard;
84
85     private ShardedDOMDataTree dataTreeService;
86     private ListenerRegistration<InMemoryDOMDataTreeShard> rootShardReg;
87     private ListenerRegistration<InMemoryDOMDataTreeShard> innerShardReg;
88
89     private final ExecutorService executor = Executors.newSingleThreadExecutor();
90
91     @Captor
92     private ArgumentCaptor<Collection<DataTreeCandidate>> captorForChanges;
93     @Captor
94     private ArgumentCaptor<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> captorForSubtrees;
95
96     private final ContainerNode crossShardContainer = createCrossShardContainer();
97
98     @Before
99     public void setUp() throws Exception {
100         MockitoAnnotations.initMocks(this);
101
102         rootShard = InMemoryDOMDataTreeShard.create(ROOT_ID, executor, 1, 1);
103         rootShard.onGlobalContextUpdated(schemaContext);
104
105         final ShardedDOMDataTree dataTree = new ShardedDOMDataTree();
106         final DOMDataTreeProducer shardRegProducer = dataTree.createProducer(Collections.singletonList(ROOT_ID));
107         rootShardReg = dataTree.registerDataTreeShard(ROOT_ID, rootShard, shardRegProducer);
108         shardRegProducer.close();
109
110         dataTreeService = dataTree;
111     }
112
113     @Test(expected=IllegalStateException.class)
114     public void testTxReadyMultiples() throws Exception {
115         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
116         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
117         final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
118
119         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
120                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
121                 .build();
122         cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
123         transaction.ready();
124
125         transaction.ready();
126     }
127
128     @Test(expected=IllegalStateException.class)
129     public void testSubmitUnclosedCursor() throws Exception {
130         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
131         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
132         final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
133
134         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
135                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
136                 .build();
137
138         cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
139         transaction.ready();
140     }
141
142     @Test
143     public void testMultipleCursorsFromOneTx() throws Exception {
144         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
145         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
146
147         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true, Collections.emptyList());
148
149         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
150         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
151         final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
152
153         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
154                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
155                 .build();
156
157         cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
158         cursor.close();
159
160         final DOMDataTreeWriteCursor newCursor = transaction.createCursor(ROOT_ID);
161         newCursor.enter(TestModel.TEST_PATH.getLastPathArgument());
162         final ContainerNode innerContainer = ImmutableContainerNodeBuilder.create()
163                 .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
164                 .withChild(ImmutableLeafNodeBuilder.<String>create()
165                         .withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1))
166                         .withValue("inner-value")
167                         .build())
168                 .build();
169
170         newCursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(), innerContainer);
171         newCursor.close();
172         transaction.ready();
173         transaction.submit();
174
175         verify(mockedDataTreeListener, timeout(1000)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
176         final Collection<DataTreeCandidate> capturedValue = captorForChanges.getValue();
177         assertTrue(capturedValue.size() == 1);
178
179         final ContainerNode dataAfter = (ContainerNode) capturedValue.iterator().next().getRootNode().getDataAfter().get();
180         assertEquals(innerContainer, dataAfter);
181         verifyNoMoreInteractions(mockedDataTreeListener);
182     }
183
184     @Test
185     public void testSingleShardListener() throws Exception {
186         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
187         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
188
189         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true, Collections.emptyList());
190
191         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
192         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
193         writeCrossShardContainer(transaction);
194
195         verify(mockedDataTreeListener, timeout(1000)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
196         final Collection<DataTreeCandidate> capturedValue = captorForChanges.getValue();
197         assertTrue(capturedValue.size() == 1);
198
199         final ContainerNode dataAfter = (ContainerNode) capturedValue.iterator().next().getRootNode().getDataAfter().get();
200         assertEquals(crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(), dataAfter);
201
202         final Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>> capturedSubtrees = captorForSubtrees.getValue();
203         assertTrue(capturedSubtrees.size() == 1);
204         assertTrue(capturedSubtrees.containsKey(INNER_CONTAINER_ID));
205         assertEquals(crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(), capturedSubtrees.get(INNER_CONTAINER_ID));
206
207         verifyNoMoreInteractions(mockedDataTreeListener);
208     }
209
210     @Test
211     public void testMultipleShards() throws Exception {
212         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
213         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
214
215         final InMemoryDOMDataTreeShard innerShard = InMemoryDOMDataTreeShard.create(INNER_CONTAINER_ID, executor, 1, 1);
216         innerShard.onGlobalContextUpdated(schemaContext);
217         final DOMDataTreeProducer shardRegProducer = dataTreeService.createProducer(Collections.singletonList(INNER_CONTAINER_ID));
218         innerShardReg = dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, innerShard, shardRegProducer);
219         shardRegProducer.close();
220
221         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(TEST_ID), true, Collections.emptyList());
222
223         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
224         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
225         writeCrossShardContainer(transaction);
226
227         final ContainerNode testContainerVerificationNode = ImmutableContainerNodeBuilder.create()
228                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
229                 .build();
230
231         //verify listeners have been notified
232         verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
233         final List<Collection<DataTreeCandidate>> capturedChanges = captorForChanges.getAllValues();
234         final List<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> capturedSubtrees = captorForSubtrees.getAllValues();
235         final DataTreeCandidate firstNotificationCandidate = capturedChanges.get(0).iterator().next();
236
237         assertTrue(capturedSubtrees.get(0).size() == 1);
238         assertEquals(testContainerVerificationNode, firstNotificationCandidate.getRootNode().getDataAfter().get());
239         assertEquals(testContainerVerificationNode, capturedSubtrees.get(0).get(TEST_ID));
240
241         final DataTreeCandidate secondNotificationCandidate = capturedChanges.get(1).iterator().next();
242         assertTrue(capturedSubtrees.get(1).size() == 1);
243         assertEquals(crossShardContainer, secondNotificationCandidate.getRootNode().getDataAfter().get());
244         assertEquals(crossShardContainer, capturedSubtrees.get(1).get(TEST_ID));
245
246         verifyNoMoreInteractions(mockedDataTreeListener);
247     }
248
249     @Test
250     public void testMultipleWritesIntoSingleShard() throws Exception {
251         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
252         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
253
254         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true, Collections.emptyList());
255
256         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
257         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
258         writeCrossShardContainer(transaction);
259
260         final DOMDataTreeShardWriteTransaction newTx = producer.createTransaction();
261         final DOMDataTreeWriteCursor newCursor = newTx.createCursor(ROOT_ID);
262
263         newCursor.delete(TestModel.TEST_PATH.getLastPathArgument());
264     }
265
266     @Test
267     public void testMockedSubshards() throws Exception {
268         final WriteableDOMDataTreeShard mockedInnerShard = Mockito.mock(WriteableDOMDataTreeShard.class);
269         final ShardedDOMDataTreeProducer shardRegProducer = Mockito.mock(ShardedDOMDataTreeProducer.class);
270         doReturn(Collections.singleton(INNER_CONTAINER_ID)).when(shardRegProducer).getSubtrees();
271         doNothing().when(shardRegProducer).subshardAdded(anyMap());
272
273         dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, mockedInnerShard, shardRegProducer);
274         final DOMDataTreeShardProducer mockedProducer = Mockito.mock(DOMDataTreeShardProducer.class);
275         doReturn(mockedProducer).when(mockedInnerShard).createProducer(any(Collection.class));
276
277         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
278
279         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
280         final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
281         cursor.enter(TestModel.TEST_PATH.getLastPathArgument());
282
283         final LeafNode<String> shardedValue1 =
284                 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
285         final LeafNode<String> shardedValue2 =
286                 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
287
288         final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerNodeBuilder = ImmutableContainerNodeBuilder.create();
289         final ContainerNode containerNode =
290                 containerNodeBuilder
291                         .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
292                         .withChild(shardedValue1)
293                         .withChild(shardedValue2)
294                         .build();
295
296         final DOMDataTreeShardWriteTransaction mockedTx = Mockito.mock(DOMDataTreeShardWriteTransaction.class);
297         doReturn(mockedTx).when(mockedProducer).createTransaction();
298
299         doNothing().when(mockedTx).ready();
300         doReturn(Futures.immediateFuture(true)).when(mockedTx).validate();
301         doReturn(Futures.immediateFuture(null)).when(mockedTx).prepare();
302         doReturn(Futures.immediateFuture(null)).when(mockedTx).commit();
303
304         final DOMDataTreeWriteCursor mockedCursor = Mockito.mock(DOMDataTreeWriteCursor.class);
305         doNothing().when(mockedCursor).write(any(PathArgument.class), any(NormalizedNode.class));
306         doNothing().when(mockedCursor).close();
307         doReturn(mockedCursor).when(mockedTx).createCursor(any(DOMDataTreeIdentifier.class));
308
309         cursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(), containerNode);
310         cursor.enter(TestModel.INNER_CONTAINER_PATH.getLastPathArgument());
311
312         final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
313                 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
314                 .withChild(ImmutableLeafNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE)).build())
315                 .build();
316
317         cursor.write(TestModel.ANOTHER_SHARD_PATH.getLastPathArgument(), lowerShardContainer);
318         cursor.close();
319         transaction.ready();
320         transaction.submit().get();
321
322         final InOrder inOrder = inOrder(mockedTx);
323         inOrder.verify(mockedTx).ready();
324         inOrder.verify(mockedTx).validate();
325         inOrder.verify(mockedTx).prepare();
326         inOrder.verify(mockedTx).commit();
327
328     }
329
330     private ContainerNode createCrossShardContainer() {
331         final LeafNode<String> shardedValue1 =
332                 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
333         final LeafNode<String> shardedValue2 =
334                 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
335
336
337         final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
338                 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
339                 .withChild(ImmutableLeafNodeBuilder.create()
340                         .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE))
341                         .withValue("testing-value")
342                         .build())
343                 .build();
344
345         final ContainerNode containerNode =
346                 ImmutableContainerNodeBuilder.create()
347                         .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
348                         .withChild(shardedValue1)
349                         .withChild(shardedValue2)
350                         .withChild(lowerShardContainer)
351                         .build();
352
353         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
354                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
355                 .withChild(containerNode)
356                 .build();
357
358         return testContainer;
359     }
360
361     private void writeCrossShardContainer(final DOMDataTreeShardWriteTransaction transaction) throws Exception{
362         final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
363
364         cursor.write(TestModel.TEST_PATH.getLastPathArgument(), crossShardContainer);
365
366         cursor.close();
367         transaction.ready();
368         transaction.submit().get();
369     }
370 }