*/
package org.opendaylight.controller.cluster.datastore;
+import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.Uninterruptibles;
-import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import org.opendaylight.controller.cluster.datastore.persisted.ShardSnapshotState;
import org.opendaylight.controller.cluster.datastore.utils.MockDataTreeChangeListener;
import org.opendaylight.controller.cluster.raft.persisted.Snapshot;
+import org.opendaylight.controller.cluster.raft.utils.InMemorySnapshotStore;
import org.opendaylight.controller.md.cluster.datastore.model.CarsModel;
import org.opendaylight.controller.md.cluster.datastore.model.PeopleModel;
import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelper;
import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionChain;
import org.opendaylight.mdsal.dom.spi.store.DOMStoreWriteTransaction;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.Uint64;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
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.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
public abstract class AbstractDistributedDataStoreIntegrationTest {
writeTx = dataStore.newWriteOnlyTransaction();
- final MapEntryNode car = CarsModel.newCarEntry("optima", BigInteger.valueOf(20000));
+ final MapEntryNode car = CarsModel.newCarEntry("optima", Uint64.valueOf(20000));
final YangInstanceIdentifier carPath = CarsModel.newCarPath("optima");
writeTx.write(carPath, car);
readWriteTx = dataStore.newReadWriteTransaction();
- final MapEntryNode car = CarsModel.newCarEntry("optima", BigInteger.valueOf(20000));
+ final MapEntryNode car = CarsModel.newCarEntry("optima", Uint64.valueOf(20000));
final YangInstanceIdentifier carPath = CarsModel.newCarPath("optima");
readWriteTx.write(carPath, car);
for (int i = 0; i < numCars; i++) {
writeTx = txChain.newWriteOnlyTransaction();
writeTx.write(CarsModel.newCarPath("car" + i),
- CarsModel.newCarEntry("car" + i, BigInteger.valueOf(20000)));
+ CarsModel.newCarEntry("car" + i, Uint64.valueOf(20000)));
testKit.doCommit(writeTx.ready());
final DOMStoreReadWriteTransaction readWriteTx = txChain.newReadWriteTransaction();
- final MapEntryNode car = CarsModel.newCarEntry("optima", BigInteger.valueOf(20000));
+ final MapEntryNode car = CarsModel.newCarEntry("optima", Uint64.valueOf(20000));
final YangInstanceIdentifier carPath = CarsModel.newCarPath("optima");
readWriteTx.write(carPath, car);
final DOMDataTreeReadWriteTransaction rwTx = txChain.newReadWriteTransaction();
rwTx.merge(LogicalDatastoreType.CONFIGURATION, CarsModel.newCarPath("car" + i),
- CarsModel.newCarEntry("car" + i, BigInteger.valueOf(20000)));
+ CarsModel.newCarEntry("car" + i, Uint64.valueOf(20000)));
futures.add(rwTx.commit());
}
final String name = "transactionIntegrationTest";
final ContainerNode carsNode = CarsModel.newCarsNode(
- CarsModel.newCarsMapNode(CarsModel.newCarEntry("optima", BigInteger.valueOf(20000L)),
- CarsModel.newCarEntry("sportage", BigInteger.valueOf(30000L))));
+ CarsModel.newCarsMapNode(CarsModel.newCarEntry("optima", Uint64.valueOf(20000)),
+ CarsModel.newCarEntry("sportage", Uint64.valueOf(30000))));
DataTree dataTree = new InMemoryDataTreeFactory().create(
DataTreeConfiguration.DEFAULT_OPERATIONAL, SchemaContextHelper.full());
AbstractShardTest.writeToStore(dataTree, CarsModel.BASE_PATH, carsNode);
- NormalizedNode<?, ?> root = AbstractShardTest.readStore(dataTree, YangInstanceIdentifier.EMPTY);
+ NormalizedNode<?, ?> root = AbstractShardTest.readStore(dataTree, YangInstanceIdentifier.empty());
final Snapshot carsSnapshot = Snapshot.create(
new ShardSnapshotState(new MetadataShardDataTreeSnapshot(root)),
final NormalizedNode<?, ?> peopleNode = PeopleModel.create();
AbstractShardTest.writeToStore(dataTree, PeopleModel.BASE_PATH, peopleNode);
- root = AbstractShardTest.readStore(dataTree, YangInstanceIdentifier.EMPTY);
+ root = AbstractShardTest.readStore(dataTree, YangInstanceIdentifier.empty());
final Snapshot peopleSnapshot = Snapshot.create(
new ShardSnapshotState(new MetadataShardDataTreeSnapshot(root)),
assertEquals("Data node", peopleNode, optional.get());
}
}
+
+ @Test
+ public void testSnapshotOnRootOverwrite() throws Exception {
+ if (!DistributedDataStore.class.isAssignableFrom(testParameter)) {
+ // FIXME: ClientBackedDatastore does not have stable indexes/term, the snapshot index seems to fluctuate
+ return;
+ }
+
+ final IntegrationTestKit testKit = new IntegrationTestKit(getSystem(),
+ datastoreContextBuilder.snapshotOnRootOverwrite(true));
+ try (AbstractDataStore dataStore = testKit.setupAbstractDataStore(
+ testParameter, "testRootOverwrite", "module-shards-default-cars-member1.conf",
+ true, "cars", "default")) {
+
+ ContainerNode rootNode = ImmutableContainerNodeBuilder.create()
+ .withNodeIdentifier(YangInstanceIdentifier.NodeIdentifier.create(SchemaContext.NAME))
+ .withChild((ContainerNode) CarsModel.create())
+ .build();
+
+ testKit.testWriteTransaction(dataStore, YangInstanceIdentifier.empty(), rootNode);
+ IntegrationTestKit.verifyShardState(dataStore, "cars",
+ state -> assertEquals(1, state.getSnapshotIndex()));
+
+ // root has been written expect snapshot at index 0
+ verifySnapshot("member-1-shard-cars-testRootOverwrite", 1, 1);
+
+ for (int i = 0; i < 10; i++) {
+ testKit.testWriteTransaction(dataStore, CarsModel.newCarPath("car " + i),
+ CarsModel.newCarEntry("car " + i, Uint64.ONE));
+ }
+
+ // fake snapshot causes the snapshotIndex to move
+ IntegrationTestKit.verifyShardState(dataStore, "cars",
+ state -> assertEquals(10, state.getSnapshotIndex()));
+
+ // however the real snapshot still has not changed and was taken at index 0
+ verifySnapshot("member-1-shard-cars-testRootOverwrite", 1, 1);
+
+ // root overwrite so expect a snapshot
+ testKit.testWriteTransaction(dataStore, YangInstanceIdentifier.empty(), rootNode);
+
+ // this was a real snapshot so everything should be in it(1 + 10 + 1)
+ IntegrationTestKit.verifyShardState(dataStore, "cars",
+ state -> assertEquals(12, state.getSnapshotIndex()));
+
+ verifySnapshot("member-1-shard-cars-testRootOverwrite", 12, 1);
+ }
+ }
+
+ private static void verifySnapshot(final String persistenceId, final long lastAppliedIndex,
+ final long lastAppliedTerm) {
+ await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> {
+ List<Snapshot> snap = InMemorySnapshotStore.getSnapshots(persistenceId, Snapshot.class);
+ assertEquals(1, snap.size());
+ assertEquals(lastAppliedIndex, snap.get(0).getLastAppliedIndex());
+ assertEquals(lastAppliedTerm, snap.get(0).getLastAppliedTerm());
+ }
+ );
+ }
}