X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=dom%2Fmdsal-dom-broker%2Fsrc%2Ftest%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fdom%2Fbroker%2FShardedDOMDataTreeTest.java;h=6ac62cfb57bebc9d09cc27c2fa302cedb00e0d9e;hb=14fec8bfb0c42bcf81036b7133895dbd9382252f;hp=d0d5ffbcac4fd6cf94e3fefc6c114665a565e75a;hpb=dc0610a1948724ba3d877cc334ff9893a3ecbdf3;p=mdsal.git diff --git a/dom/mdsal-dom-broker/src/test/java/org/opendaylight/mdsal/dom/broker/ShardedDOMDataTreeTest.java b/dom/mdsal-dom-broker/src/test/java/org/opendaylight/mdsal/dom/broker/ShardedDOMDataTreeTest.java index d0d5ffbcac..6ac62cfb57 100644 --- a/dom/mdsal-dom-broker/src/test/java/org/opendaylight/mdsal/dom/broker/ShardedDOMDataTreeTest.java +++ b/dom/mdsal-dom-broker/src/test/java/org/opendaylight/mdsal/dom/broker/ShardedDOMDataTreeTest.java @@ -1,16 +1,28 @@ +/* + * Copyright (c) 2016 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.broker; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyCollection; import static org.mockito.Matchers.anyMap; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import com.google.common.util.concurrent.CheckedFuture; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -21,9 +33,9 @@ import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.common.api.TransactionCommitFailedException; import org.opendaylight.mdsal.dom.api.DOMDataTreeCursorAwareTransaction; import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier; import org.opendaylight.mdsal.dom.api.DOMDataTreeListener; @@ -32,14 +44,20 @@ import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteCursor; import org.opendaylight.mdsal.dom.broker.util.TestModel; import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataTreeShard; import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; import org.slf4j.Logger; @@ -60,12 +78,17 @@ public class ShardedDOMDataTreeTest { } private static final DOMDataTreeIdentifier ROOT_ID = - new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.EMPTY); + new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.EMPTY); private static final DOMDataTreeIdentifier TEST_ID = - new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST_PATH); + new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH); private static final DOMDataTreeIdentifier INNER_CONTAINER_ID = - new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.INNER_CONTAINER_PATH); + new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, TestModel.INNER_CONTAINER_PATH); + + private static final YangInstanceIdentifier OUTER_LIST_YID = TestModel.OUTER_LIST_PATH.node( + new NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1)); + private static final DOMDataTreeIdentifier OUTER_LIST_ID = + new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, OUTER_LIST_YID); private InMemoryDOMDataTreeShard rootShard; @@ -85,7 +108,7 @@ public class ShardedDOMDataTreeTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - rootShard = InMemoryDOMDataTreeShard.create(ROOT_ID, executor, 5000); + rootShard = InMemoryDOMDataTreeShard.create(ROOT_ID, executor, 1); rootShard.onGlobalContextUpdated(schemaContext); final ShardedDOMDataTree dataTree = new ShardedDOMDataTree(); @@ -98,16 +121,58 @@ public class ShardedDOMDataTreeTest { @Test(expected = IllegalArgumentException.class) public void testProducerPathContention() throws Exception { - final DOMDataTreeProducer p1 = dataTreeService.createProducer(Collections.singletonList(ROOT_ID)); - final DOMDataTreeProducer p2 = dataTreeService.createProducer(Collections.singletonList(TEST_ID)); + dataTreeService.createProducer(Collections.singletonList(ROOT_ID)); + dataTreeService.createProducer(Collections.singletonList(TEST_ID)); + } + + @Test + public void testShardRegistrationClose() throws Exception { + rootShardReg.close(); + + final InMemoryDOMDataTreeShard newRootShard = InMemoryDOMDataTreeShard.create(ROOT_ID, executor, 1); + newRootShard.onGlobalContextUpdated(schemaContext); + final DOMDataTreeProducer shardRegProducer = dataTreeService.createProducer(Collections.singletonList(ROOT_ID)); + + final ListenerRegistration newRootShardReg = + dataTreeService.registerDataTreeShard(ROOT_ID, rootShard, shardRegProducer); + shardRegProducer.close(); + + final InMemoryDOMDataTreeShard innerShard = InMemoryDOMDataTreeShard.create(INNER_CONTAINER_ID, executor, 1); + innerShard.onGlobalContextUpdated(schemaContext); + final DOMDataTreeProducer shardRegProducer2 = + dataTreeService.createProducer(Collections.singletonList(INNER_CONTAINER_ID)); + ListenerRegistration innerShardReg = + dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, innerShard, shardRegProducer2); + + innerShardReg.close(); + // try to register the shard again + innerShardReg = dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, innerShard, shardRegProducer2); + final DOMDataTreeCursorAwareTransaction tx = shardRegProducer2.createTransaction(false); + final DOMDataTreeWriteCursor cursor = tx.createCursor(INNER_CONTAINER_ID); + assertNotNull(cursor); + + cursor.close(); + tx.cancel(); + shardRegProducer2.close(); + + innerShardReg.close(); + newRootShardReg.close(); + } + + @Test(expected = IllegalStateException.class) + public void testEmptyShardMapProducer() throws Exception { + final ShardedDOMDataTree dataTree = new ShardedDOMDataTree(); + final DOMDataTreeProducer producer = dataTree.createProducer(Collections.singletonList(ROOT_ID)); + producer.createTransaction(false); } @Test public void testSingleShardWrite() throws Exception { - final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class); + final DOMDataTreeListener mockedDataTreeListener = mock(DOMDataTreeListener.class); doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap()); - dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true, Collections.emptyList()); + dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), + true, Collections.emptyList()); final DOMDataTreeProducer producer = dataTreeService.createProducer(Collections.singletonList(ROOT_ID)); DOMDataTreeCursorAwareTransaction tx = producer.createTransaction(false); @@ -134,17 +199,128 @@ public class ShardedDOMDataTreeTest { cursor.close(); tx.submit().checkedGet(); - verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture()); + verify(mockedDataTreeListener, timeout(1000).times(3)).onDataTreeChanged(captorForChanges.capture(), + captorForSubtrees.capture()); final List> capturedValue = captorForChanges.getAllValues(); - assertTrue(capturedValue.size() == 2); + assertTrue(capturedValue.size() == 3); - final ContainerNode capturedChange = (ContainerNode) capturedValue.get(0).iterator().next().getRootNode().getDataAfter().get(); - final ContainerNode innerContainerVerify = (ContainerNode) crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(); + final ContainerNode capturedChange = + (ContainerNode) capturedValue.get(1).iterator().next().getRootNode().getDataAfter().get(); + final ContainerNode innerContainerVerify = (ContainerNode) crossShardContainer.getChild( + TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(); assertEquals(innerContainerVerify, capturedChange); verifyNoMoreInteractions(mockedDataTreeListener); } + @Test + // TODO extract common logic from testSingleSubshardWrite and + // testSingleShardWrite tests + public void testSingleSubshardWrite() throws Exception { + final DOMDataTreeListener mockedDataTreeListener = mock(DOMDataTreeListener.class); + doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap()); + + InMemoryDOMDataTreeShard testShard = InMemoryDOMDataTreeShard.create(TEST_ID, executor, 1); + testShard.onGlobalContextUpdated(schemaContext); + + final DOMDataTreeProducer regProducer = dataTreeService.createProducer(Collections.singleton(TEST_ID)); + dataTreeService.registerDataTreeShard(TEST_ID, testShard, regProducer); + regProducer.close(); + + dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(TEST_ID), + true, Collections.emptyList()); + + final DOMDataTreeProducer producer = dataTreeService.createProducer(Collections.singletonList(ROOT_ID)); + DOMDataTreeCursorAwareTransaction tx = producer.createTransaction(false); + DOMDataTreeWriteCursor cursor = tx.createCursor(ROOT_ID); + assertNotNull(cursor); + + cursor.write(TEST_ID.getRootIdentifier().getLastPathArgument(), crossShardContainer); + + cursor.close(); + tx.submit().checkedGet(); + + tx = producer.createTransaction(false); + cursor = tx.createCursor(TEST_ID); + assertNotNull(cursor); + + cursor.delete(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()); + cursor.close(); + tx.submit().checkedGet(); + + verify(mockedDataTreeListener, timeout(5000).times(3)).onDataTreeChanged(captorForChanges.capture(), + captorForSubtrees.capture()); + + final List> capturedValue = captorForChanges.getAllValues(); + final ContainerNode capturedChange = + (ContainerNode) capturedValue.get(1).iterator().next().getRootNode().getDataAfter().get(); + final ContainerNode innerContainerVerify = crossShardContainer; + assertEquals(innerContainerVerify, capturedChange); + } + + @Test + public void testMultipleWritesIntoSingleMapEntry() throws Exception { + + final YangInstanceIdentifier oid1 = TestModel.OUTER_LIST_PATH.node(new NodeIdentifierWithPredicates( + TestModel.OUTER_LIST_QNAME, QName.create(TestModel.OUTER_LIST_QNAME, "id"), 0)); + final DOMDataTreeIdentifier outerListPath = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, oid1); + + final DOMDataTreeProducer shardProducer = dataTreeService.createProducer( + Collections.singletonList(outerListPath)); + final InMemoryDOMDataTreeShard outerListShard = InMemoryDOMDataTreeShard.create(outerListPath, executor, 1000); + outerListShard.onGlobalContextUpdated(schemaContext); + + final ListenerRegistration oid1ShardRegistration = + dataTreeService.registerDataTreeShard(outerListPath, outerListShard, shardProducer); + + final DOMDataTreeCursorAwareTransaction tx = shardProducer.createTransaction(false); + final DOMDataTreeWriteCursor cursor = + tx.createCursor(new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, oid1)); + assertNotNull(cursor); + + MapNode innerList = ImmutableMapNodeBuilder + .create() + .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_LIST_QNAME)) + .build(); + + cursor.write(new NodeIdentifier(TestModel.INNER_LIST_QNAME), innerList); + cursor.close(); + tx.submit().checkedGet(); + + final ArrayList> futures = new ArrayList<>(); + for (int i = 0; i < 1000; i++) { + final Collection innerListMapEntries = createInnerListMapEntries(1000, "run-" + i); + for (final MapEntryNode innerListMapEntry : innerListMapEntries) { + final DOMDataTreeCursorAwareTransaction tx1 = shardProducer.createTransaction(false); + final DOMDataTreeWriteCursor cursor1 = tx1.createCursor( + new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, + oid1.node(new NodeIdentifier(TestModel.INNER_LIST_QNAME)))); + cursor1.write(innerListMapEntry.getIdentifier(), innerListMapEntry); + cursor1.close(); + futures.add(tx1.submit()); + } + } + + futures.get(futures.size() - 1).checkedGet(); + + } + + private static Collection createInnerListMapEntries(final int amount, final String valuePrefix) { + final Collection ret = new ArrayList<>(); + for (int i = 0; i < amount; i++) { + ret.add(ImmutableNodes.mapEntryBuilder() + .withNodeIdentifier(new NodeIdentifierWithPredicates(TestModel.INNER_LIST_QNAME, + QName.create(TestModel.OUTER_LIST_QNAME, "name"), Integer.toString(i))) + .withChild(ImmutableNodes + .leafNode(QName.create(TestModel.INNER_LIST_QNAME, "name"), Integer.toString(i))) + .withChild(ImmutableNodes + .leafNode(QName.create(TestModel.INNER_LIST_QNAME, "value"), valuePrefix + "-" + i)) + .build()); + } + + return ret; + } + @Test public void testMultipleProducerCursorCreation() throws Exception { @@ -164,7 +340,8 @@ public class ShardedDOMDataTreeTest { assertTrue(rootTx.cancel()); - final DOMDataTreeProducer innerContainerProducer = rootProducer.createProducer(Collections.singletonList(INNER_CONTAINER_ID)); + final DOMDataTreeProducer innerContainerProducer = rootProducer.createProducer( + Collections.singletonList(INNER_CONTAINER_ID)); rootTx = rootProducer.createTransaction(false); try { @@ -198,11 +375,13 @@ public class ShardedDOMDataTreeTest { assertNotNull(innerShardCursor); } - private ContainerNode createCrossShardContainer() { + private static ContainerNode createCrossShardContainer() { final LeafNode shardedValue1 = - ImmutableLeafNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build(); + ImmutableLeafNodeBuilder.create().withNodeIdentifier(new NodeIdentifier( + TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build(); final LeafNode shardedValue2 = - ImmutableLeafNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build(); + ImmutableLeafNodeBuilder.create().withNodeIdentifier(new NodeIdentifier( + TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build(); final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create() @@ -228,4 +407,79 @@ public class ShardedDOMDataTreeTest { return testContainer; } + + //test multiple vertical levels between the shards + @Test + public void testLargerSubshardSpace() throws Exception { + + final InMemoryDOMDataTreeShard outerListShard = InMemoryDOMDataTreeShard.create(OUTER_LIST_ID, executor, 1, 1); + outerListShard.onGlobalContextUpdated(schemaContext); + + try (DOMDataTreeProducer producer = + dataTreeService.createProducer(Collections.singletonList(OUTER_LIST_ID))) { + dataTreeService.registerDataTreeShard(OUTER_LIST_ID, outerListShard, producer); + } + + final DOMDataTreeProducer producer = dataTreeService.createProducer(Collections.singletonList(ROOT_ID)); + final DOMDataTreeCursorAwareTransaction tx = producer.createTransaction(false); + final DOMDataTreeWriteCursor cursor = tx.createCursor(ROOT_ID); + + assertNotNull(cursor); + cursor.write(TestModel.TEST_PATH.getLastPathArgument(), createCrossShardContainer2()); + cursor.close(); + + tx.submit().checkedGet(); + + final DOMDataTreeListener listener = mock(DOMDataTreeListener.class); + doNothing().when(listener).onDataTreeChanged(any(), any()); + dataTreeService.registerListener(listener, + Collections.singletonList( + new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.EMPTY)), + false, Collections.emptyList()); + + verify(listener, times(2)).onDataTreeChanged(any(), any()); + + + } + + private static ContainerNode createCrossShardContainer2() { + + final MapEntryNode + innerListEntry1 = ImmutableNodes + .mapEntryBuilder(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "name-1") + .withChild(ImmutableNodes.leafNode(TestModel.NAME_QNAME, "name-1")) + .withChild(ImmutableNodes.leafNode(TestModel.VALUE_QNAME, "value-1")) + .build(); + final MapEntryNode innerListEntry2 = ImmutableNodes + .mapEntryBuilder(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "name-2") + .withChild(ImmutableNodes.leafNode(TestModel.NAME_QNAME, "name-2")) + .withChild(ImmutableNodes.leafNode(TestModel.VALUE_QNAME, "value-2")) + .build(); + + final MapNode innerList1 = ImmutableNodes + .mapNodeBuilder(TestModel.INNER_LIST_QNAME).withChild(innerListEntry1).build(); + final MapNode innerList2 = ImmutableNodes + .mapNodeBuilder(TestModel.INNER_LIST_QNAME).withChild(innerListEntry2).build(); + + final MapEntryNode outerListEntry1 = ImmutableNodes + .mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1) + .withChild(innerList1) + .build(); + final MapEntryNode outerListEntry2 = ImmutableNodes + .mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2) + .withChild(innerList2) + .build(); + + final MapNode outerList = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME) + .withChild(outerListEntry1) + .withChild(outerListEntry2) + .build(); + + final ContainerNode testContainer = ImmutableContainerNodeBuilder.create() + .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME)) + .withChild(outerList) + .build(); + + return testContainer; + } }