2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.cluster.datastore;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.junit.Assert.assertTrue;
13 import static org.junit.Assert.fail;
14 import static org.mockito.ArgumentMatchers.any;
15 import static org.mockito.ArgumentMatchers.anyBoolean;
16 import static org.mockito.ArgumentMatchers.eq;
17 import static org.mockito.Mockito.atLeastOnce;
18 import static org.mockito.Mockito.doNothing;
19 import static org.mockito.Mockito.doReturn;
20 import static org.mockito.Mockito.inOrder;
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.never;
23 import static org.mockito.Mockito.reset;
24 import static org.mockito.Mockito.verify;
25 import static org.mockito.Mockito.verifyNoMoreInteractions;
26 import static org.opendaylight.controller.cluster.datastore.ShardDataTreeMocking.coordinatedCanCommit;
27 import static org.opendaylight.controller.cluster.datastore.ShardDataTreeMocking.coordinatedCommit;
28 import static org.opendaylight.controller.cluster.datastore.ShardDataTreeMocking.coordinatedPreCommit;
29 import static org.opendaylight.controller.cluster.datastore.ShardDataTreeMocking.immediate3PhaseCommit;
30 import static org.opendaylight.controller.cluster.datastore.ShardDataTreeMocking.immediateCanCommit;
31 import static org.opendaylight.controller.cluster.datastore.ShardDataTreeMocking.immediateCommit;
32 import static org.opendaylight.controller.cluster.datastore.ShardDataTreeMocking.immediatePayloadReplication;
33 import static org.opendaylight.controller.cluster.datastore.ShardDataTreeMocking.immediatePreCommit;
35 import com.google.common.base.Ticker;
36 import com.google.common.primitives.UnsignedLong;
37 import com.google.common.util.concurrent.FutureCallback;
38 import java.io.IOException;
39 import java.math.BigInteger;
40 import java.util.ArrayList;
41 import java.util.Collection;
42 import java.util.HashMap;
43 import java.util.List;
45 import java.util.Optional;
46 import java.util.function.Consumer;
47 import org.junit.Before;
48 import org.junit.Test;
49 import org.mockito.ArgumentCaptor;
50 import org.mockito.InOrder;
51 import org.mockito.Mockito;
52 import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats;
53 import org.opendaylight.controller.cluster.datastore.persisted.CommitTransactionPayload;
54 import org.opendaylight.controller.cluster.datastore.persisted.MetadataShardDataTreeSnapshot;
55 import org.opendaylight.controller.cluster.datastore.persisted.PayloadVersion;
56 import org.opendaylight.controller.cluster.datastore.persisted.ShardSnapshotState;
57 import org.opendaylight.controller.md.cluster.datastore.model.CarsModel;
58 import org.opendaylight.controller.md.cluster.datastore.model.PeopleModel;
59 import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelper;
60 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
61 import org.opendaylight.yangtools.yang.common.Uint64;
62 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
63 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
64 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
65 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
66 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
67 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
68 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
69 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
70 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
71 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
72 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
73 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
74 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
75 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
76 import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
77 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
78 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
79 import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
80 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
81 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
83 public class ShardDataTreeTest extends AbstractTest {
84 private static final DatastoreContext DATASTORE_CONTEXT = DatastoreContext.newBuilder().build();
86 private final Shard mockShard = Mockito.mock(Shard.class);
87 private ShardDataTree shardDataTree;
88 private EffectiveModelContext fullSchema;
92 doReturn(Ticker.systemTicker()).when(mockShard).ticker();
93 doReturn(mock(ShardStats.class)).when(mockShard).getShardMBean();
94 doReturn(DATASTORE_CONTEXT).when(mockShard).getDatastoreContext();
96 fullSchema = SchemaContextHelper.full();
98 shardDataTree = new ShardDataTree(mockShard, fullSchema, TreeType.OPERATIONAL);
102 public void testWrite() {
103 modify(false, true, true);
107 public void testMerge() {
108 modify(true, true, true);
111 private void modify(final boolean merge, final boolean expectedCarsPresent, final boolean expectedPeoplePresent) {
112 immediatePayloadReplication(shardDataTree, mockShard);
114 assertEquals(fullSchema, shardDataTree.getSchemaContext());
116 final ReadWriteShardDataTreeTransaction transaction =
117 shardDataTree.newReadWriteTransaction(nextTransactionId());
119 final DataTreeModification snapshot = transaction.getSnapshot();
121 assertNotNull(snapshot);
124 snapshot.merge(CarsModel.BASE_PATH, CarsModel.create());
125 snapshot.merge(PeopleModel.BASE_PATH, PeopleModel.create());
127 snapshot.write(CarsModel.BASE_PATH, CarsModel.create());
128 snapshot.write(PeopleModel.BASE_PATH, PeopleModel.create());
131 final ShardDataTreeCohort cohort = shardDataTree.finishTransaction(transaction, Optional.empty());
133 immediateCanCommit(cohort);
134 immediatePreCommit(cohort);
135 immediateCommit(cohort);
137 final ReadOnlyShardDataTreeTransaction readOnlyShardDataTreeTransaction =
138 shardDataTree.newReadOnlyTransaction(nextTransactionId());
140 final DataTreeSnapshot snapshot1 = readOnlyShardDataTreeTransaction.getSnapshot();
142 final Optional<NormalizedNode<?, ?>> optional = snapshot1.readNode(CarsModel.BASE_PATH);
144 assertEquals(expectedCarsPresent, optional.isPresent());
146 final Optional<NormalizedNode<?, ?>> optional1 = snapshot1.readNode(PeopleModel.BASE_PATH);
148 assertEquals(expectedPeoplePresent, optional1.isPresent());
152 public void bug4359AddRemoveCarOnce() {
153 immediatePayloadReplication(shardDataTree, mockShard);
155 final List<DataTreeCandidate> candidates = new ArrayList<>();
156 candidates.add(addCar(shardDataTree));
157 candidates.add(removeCar(shardDataTree));
159 final NormalizedNode<?, ?> expected = getCars(shardDataTree);
161 applyCandidates(shardDataTree, candidates);
163 final NormalizedNode<?, ?> actual = getCars(shardDataTree);
165 assertEquals(expected, actual);
169 public void bug4359AddRemoveCarTwice() {
170 immediatePayloadReplication(shardDataTree, mockShard);
172 final List<DataTreeCandidate> candidates = new ArrayList<>();
173 candidates.add(addCar(shardDataTree));
174 candidates.add(removeCar(shardDataTree));
175 candidates.add(addCar(shardDataTree));
176 candidates.add(removeCar(shardDataTree));
178 final NormalizedNode<?, ?> expected = getCars(shardDataTree);
180 applyCandidates(shardDataTree, candidates);
182 final NormalizedNode<?, ?> actual = getCars(shardDataTree);
184 assertEquals(expected, actual);
188 public void testListenerNotifiedOnApplySnapshot() throws Exception {
189 immediatePayloadReplication(shardDataTree, mockShard);
191 DOMDataTreeChangeListener listener = mock(DOMDataTreeChangeListener.class);
192 shardDataTree.registerTreeChangeListener(CarsModel.CAR_LIST_PATH.node(CarsModel.CAR_QNAME), listener,
193 Optional.empty(), noop -> { });
195 addCar(shardDataTree, "optima");
197 verifyOnDataTreeChanged(listener, dtc -> {
198 assertEquals("getModificationType", ModificationType.WRITE, dtc.getRootNode().getModificationType());
199 assertEquals("getRootPath", CarsModel.newCarPath("optima"), dtc.getRootPath());
202 addCar(shardDataTree, "sportage");
204 verifyOnDataTreeChanged(listener, dtc -> {
205 assertEquals("getModificationType", ModificationType.WRITE, dtc.getRootNode().getModificationType());
206 assertEquals("getRootPath", CarsModel.newCarPath("sportage"), dtc.getRootPath());
209 ShardDataTree newDataTree = new ShardDataTree(mockShard, fullSchema, TreeType.OPERATIONAL);
210 immediatePayloadReplication(newDataTree, mockShard);
211 addCar(newDataTree, "optima");
212 addCar(newDataTree, "murano");
214 shardDataTree.applySnapshot(newDataTree.takeStateSnapshot());
216 Map<YangInstanceIdentifier, ModificationType> expChanges = new HashMap<>();
217 expChanges.put(CarsModel.newCarPath("optima"), ModificationType.WRITE);
218 expChanges.put(CarsModel.newCarPath("murano"), ModificationType.WRITE);
219 expChanges.put(CarsModel.newCarPath("sportage"), ModificationType.DELETE);
220 verifyOnDataTreeChanged(listener, dtc -> {
221 ModificationType expType = expChanges.remove(dtc.getRootPath());
222 assertNotNull("Got unexpected change for " + dtc.getRootPath(), expType);
223 assertEquals("getModificationType", expType, dtc.getRootNode().getModificationType());
226 if (!expChanges.isEmpty()) {
227 fail("Missing change notifications: " + expChanges);
232 public void testPipelinedTransactionsWithCoordinatedCommits() throws Exception {
233 final ShardDataTreeCohort cohort1 = newShardDataTreeCohort(snapshot ->
234 snapshot.write(CarsModel.BASE_PATH, CarsModel.emptyContainer()));
236 final ShardDataTreeCohort cohort2 = newShardDataTreeCohort(snapshot ->
237 snapshot.write(CarsModel.CAR_LIST_PATH, CarsModel.newCarMapNode()));
239 NormalizedNode<?, ?> peopleNode = PeopleModel.create();
240 final ShardDataTreeCohort cohort3 = newShardDataTreeCohort(snapshot ->
241 snapshot.write(PeopleModel.BASE_PATH, peopleNode));
243 YangInstanceIdentifier carPath = CarsModel.newCarPath("optima");
244 MapEntryNode carNode = CarsModel.newCarEntry("optima", Uint64.valueOf(100));
245 final ShardDataTreeCohort cohort4 = newShardDataTreeCohort(snapshot -> snapshot.write(carPath, carNode));
247 immediateCanCommit(cohort1);
248 final FutureCallback<Void> canCommitCallback2 = coordinatedCanCommit(cohort2);
249 final FutureCallback<Void> canCommitCallback3 = coordinatedCanCommit(cohort3);
250 final FutureCallback<Void> canCommitCallback4 = coordinatedCanCommit(cohort4);
252 final FutureCallback<DataTreeCandidate> preCommitCallback1 = coordinatedPreCommit(cohort1);
253 verify(preCommitCallback1).onSuccess(cohort1.getCandidate());
254 verify(canCommitCallback2).onSuccess(null);
256 final FutureCallback<DataTreeCandidate> preCommitCallback2 = coordinatedPreCommit(cohort2);
257 verify(preCommitCallback2).onSuccess(cohort2.getCandidate());
258 verify(canCommitCallback3).onSuccess(null);
260 final FutureCallback<DataTreeCandidate> preCommitCallback3 = coordinatedPreCommit(cohort3);
261 verify(preCommitCallback3).onSuccess(cohort3.getCandidate());
262 verify(canCommitCallback4).onSuccess(null);
264 final FutureCallback<DataTreeCandidate> preCommitCallback4 = coordinatedPreCommit(cohort4);
265 verify(preCommitCallback4).onSuccess(cohort4.getCandidate());
267 final FutureCallback<UnsignedLong> commitCallback2 = coordinatedCommit(cohort2);
268 verify(mockShard, never()).persistPayload(eq(cohort1.getIdentifier()), any(CommitTransactionPayload.class),
270 verifyNoMoreInteractions(commitCallback2);
272 final FutureCallback<UnsignedLong> commitCallback4 = coordinatedCommit(cohort4);
273 verify(mockShard, never()).persistPayload(eq(cohort4.getIdentifier()), any(CommitTransactionPayload.class),
275 verifyNoMoreInteractions(commitCallback4);
277 final FutureCallback<UnsignedLong> commitCallback1 = coordinatedCommit(cohort1);
278 InOrder inOrder = inOrder(mockShard);
279 inOrder.verify(mockShard).persistPayload(eq(cohort1.getIdentifier()), any(CommitTransactionPayload.class),
281 inOrder.verify(mockShard).persistPayload(eq(cohort2.getIdentifier()), any(CommitTransactionPayload.class),
283 verifyNoMoreInteractions(commitCallback1);
284 verifyNoMoreInteractions(commitCallback2);
286 final FutureCallback<UnsignedLong> commitCallback3 = coordinatedCommit(cohort3);
287 inOrder = inOrder(mockShard);
288 inOrder.verify(mockShard).persistPayload(eq(cohort3.getIdentifier()), any(CommitTransactionPayload.class),
290 inOrder.verify(mockShard).persistPayload(eq(cohort4.getIdentifier()), any(CommitTransactionPayload.class),
292 verifyNoMoreInteractions(commitCallback3);
293 verifyNoMoreInteractions(commitCallback4);
295 final ShardDataTreeCohort cohort5 = newShardDataTreeCohort(snapshot ->
296 snapshot.merge(CarsModel.BASE_PATH, CarsModel.emptyContainer()));
297 final FutureCallback<Void> canCommitCallback5 = coordinatedCanCommit(cohort5);
299 // The payload instance doesn't matter - it just needs to be of type CommitTransactionPayload.
300 CommitTransactionPayload mockPayload = CommitTransactionPayload.create(nextTransactionId(),
301 cohort1.getCandidate());
302 shardDataTree.applyReplicatedPayload(cohort1.getIdentifier(), mockPayload);
303 shardDataTree.applyReplicatedPayload(cohort2.getIdentifier(), mockPayload);
304 shardDataTree.applyReplicatedPayload(cohort3.getIdentifier(), mockPayload);
305 shardDataTree.applyReplicatedPayload(cohort4.getIdentifier(), mockPayload);
307 inOrder = inOrder(commitCallback1, commitCallback2, commitCallback3, commitCallback4);
308 inOrder.verify(commitCallback1).onSuccess(any(UnsignedLong.class));
309 inOrder.verify(commitCallback2).onSuccess(any(UnsignedLong.class));
310 inOrder.verify(commitCallback3).onSuccess(any(UnsignedLong.class));
311 inOrder.verify(commitCallback4).onSuccess(any(UnsignedLong.class));
313 verify(canCommitCallback5).onSuccess(null);
315 final DataTreeSnapshot snapshot =
316 shardDataTree.newReadOnlyTransaction(nextTransactionId()).getSnapshot();
317 Optional<NormalizedNode<?, ?>> optional = snapshot.readNode(carPath);
318 assertTrue("Car node present", optional.isPresent());
319 assertEquals("Car node", carNode, optional.get());
321 optional = snapshot.readNode(PeopleModel.BASE_PATH);
322 assertTrue("People node present", optional.isPresent());
323 assertEquals("People node", peopleNode, optional.get());
327 public void testPipelinedTransactionsWithImmediateCommits() throws Exception {
328 final ShardDataTreeCohort cohort1 = newShardDataTreeCohort(snapshot ->
329 snapshot.write(CarsModel.BASE_PATH, CarsModel.emptyContainer()));
331 final ShardDataTreeCohort cohort2 = newShardDataTreeCohort(snapshot ->
332 snapshot.write(CarsModel.CAR_LIST_PATH, CarsModel.newCarMapNode()));
334 YangInstanceIdentifier carPath = CarsModel.newCarPath("optima");
335 MapEntryNode carNode = CarsModel.newCarEntry("optima", Uint64.valueOf(100));
336 final ShardDataTreeCohort cohort3 = newShardDataTreeCohort(snapshot -> snapshot.write(carPath, carNode));
338 final FutureCallback<UnsignedLong> commitCallback2 = immediate3PhaseCommit(cohort2);
339 final FutureCallback<UnsignedLong> commitCallback3 = immediate3PhaseCommit(cohort3);
340 final FutureCallback<UnsignedLong> commitCallback1 = immediate3PhaseCommit(cohort1);
342 InOrder inOrder = inOrder(mockShard);
343 inOrder.verify(mockShard).persistPayload(eq(cohort1.getIdentifier()), any(CommitTransactionPayload.class),
345 inOrder.verify(mockShard).persistPayload(eq(cohort2.getIdentifier()), any(CommitTransactionPayload.class),
347 inOrder.verify(mockShard).persistPayload(eq(cohort3.getIdentifier()), any(CommitTransactionPayload.class),
350 // The payload instance doesn't matter - it just needs to be of type CommitTransactionPayload.
351 CommitTransactionPayload mockPayload = CommitTransactionPayload.create(nextTransactionId(),
352 cohort1.getCandidate());
353 shardDataTree.applyReplicatedPayload(cohort1.getIdentifier(), mockPayload);
354 shardDataTree.applyReplicatedPayload(cohort2.getIdentifier(), mockPayload);
355 shardDataTree.applyReplicatedPayload(cohort3.getIdentifier(), mockPayload);
357 inOrder = inOrder(commitCallback1, commitCallback2, commitCallback3);
358 inOrder.verify(commitCallback1).onSuccess(any(UnsignedLong.class));
359 inOrder.verify(commitCallback2).onSuccess(any(UnsignedLong.class));
360 inOrder.verify(commitCallback3).onSuccess(any(UnsignedLong.class));
362 final DataTreeSnapshot snapshot =
363 shardDataTree.newReadOnlyTransaction(nextTransactionId()).getSnapshot();
364 Optional<NormalizedNode<?, ?>> optional = snapshot.readNode(carPath);
365 assertTrue("Car node present", optional.isPresent());
366 assertEquals("Car node", carNode, optional.get());
370 public void testPipelinedTransactionsWithImmediateReplication() {
371 immediatePayloadReplication(shardDataTree, mockShard);
373 final ShardDataTreeCohort cohort1 = newShardDataTreeCohort(snapshot ->
374 snapshot.write(CarsModel.BASE_PATH, CarsModel.emptyContainer()));
376 final ShardDataTreeCohort cohort2 = newShardDataTreeCohort(snapshot ->
377 snapshot.write(CarsModel.CAR_LIST_PATH, CarsModel.newCarMapNode()));
379 YangInstanceIdentifier carPath = CarsModel.newCarPath("optima");
380 MapEntryNode carNode = CarsModel.newCarEntry("optima", Uint64.valueOf(100));
381 final ShardDataTreeCohort cohort3 = newShardDataTreeCohort(snapshot -> snapshot.write(carPath, carNode));
383 final FutureCallback<UnsignedLong> commitCallback1 = immediate3PhaseCommit(cohort1);
384 final FutureCallback<UnsignedLong> commitCallback2 = immediate3PhaseCommit(cohort2);
385 final FutureCallback<UnsignedLong> commitCallback3 = immediate3PhaseCommit(cohort3);
387 InOrder inOrder = inOrder(commitCallback1, commitCallback2, commitCallback3);
388 inOrder.verify(commitCallback1).onSuccess(any(UnsignedLong.class));
389 inOrder.verify(commitCallback2).onSuccess(any(UnsignedLong.class));
390 inOrder.verify(commitCallback3).onSuccess(any(UnsignedLong.class));
392 final DataTreeSnapshot snapshot = shardDataTree.newReadOnlyTransaction(nextTransactionId()).getSnapshot();
393 Optional<NormalizedNode<?, ?>> optional = snapshot.readNode(CarsModel.BASE_PATH);
394 assertTrue("Car node present", optional.isPresent());
397 @SuppressWarnings("unchecked")
399 public void testAbortWithPendingCommits() throws Exception {
400 final ShardDataTreeCohort cohort1 = newShardDataTreeCohort(snapshot ->
401 snapshot.write(CarsModel.BASE_PATH, CarsModel.emptyContainer()));
403 final ShardDataTreeCohort cohort2 = newShardDataTreeCohort(snapshot ->
404 snapshot.write(PeopleModel.BASE_PATH, PeopleModel.create()));
406 final ShardDataTreeCohort cohort3 = newShardDataTreeCohort(snapshot ->
407 snapshot.write(CarsModel.CAR_LIST_PATH, CarsModel.newCarMapNode()));
409 YangInstanceIdentifier carPath = CarsModel.newCarPath("optima");
410 MapEntryNode carNode = CarsModel.newCarEntry("optima", Uint64.valueOf(100));
411 final ShardDataTreeCohort cohort4 = newShardDataTreeCohort(snapshot -> snapshot.write(carPath, carNode));
413 coordinatedCanCommit(cohort2);
414 immediateCanCommit(cohort1);
415 coordinatedCanCommit(cohort3);
416 coordinatedCanCommit(cohort4);
418 coordinatedPreCommit(cohort1);
419 coordinatedPreCommit(cohort2);
420 coordinatedPreCommit(cohort3);
422 FutureCallback<Void> mockAbortCallback = mock(FutureCallback.class);
423 doNothing().when(mockAbortCallback).onSuccess(null);
424 cohort2.abort(mockAbortCallback);
425 verify(mockAbortCallback).onSuccess(null);
427 coordinatedPreCommit(cohort4);
428 coordinatedCommit(cohort1);
429 coordinatedCommit(cohort3);
430 coordinatedCommit(cohort4);
432 InOrder inOrder = inOrder(mockShard);
433 inOrder.verify(mockShard).persistPayload(eq(cohort1.getIdentifier()), any(CommitTransactionPayload.class),
435 inOrder.verify(mockShard).persistPayload(eq(cohort3.getIdentifier()), any(CommitTransactionPayload.class),
437 inOrder.verify(mockShard).persistPayload(eq(cohort4.getIdentifier()), any(CommitTransactionPayload.class),
440 // The payload instance doesn't matter - it just needs to be of type CommitTransactionPayload.
441 CommitTransactionPayload mockPayload = CommitTransactionPayload.create(nextTransactionId(),
442 cohort1.getCandidate());
443 shardDataTree.applyReplicatedPayload(cohort1.getIdentifier(), mockPayload);
444 shardDataTree.applyReplicatedPayload(cohort3.getIdentifier(), mockPayload);
445 shardDataTree.applyReplicatedPayload(cohort4.getIdentifier(), mockPayload);
447 final DataTreeSnapshot snapshot =
448 shardDataTree.newReadOnlyTransaction(nextTransactionId()).getSnapshot();
449 Optional<NormalizedNode<?, ?>> optional = snapshot.readNode(carPath);
450 assertTrue("Car node present", optional.isPresent());
451 assertEquals("Car node", carNode, optional.get());
454 @SuppressWarnings("unchecked")
456 public void testAbortWithFailedRebase() {
457 immediatePayloadReplication(shardDataTree, mockShard);
459 final ShardDataTreeCohort cohort1 = newShardDataTreeCohort(snapshot ->
460 snapshot.write(CarsModel.BASE_PATH, CarsModel.emptyContainer()));
462 final ShardDataTreeCohort cohort2 = newShardDataTreeCohort(snapshot ->
463 snapshot.write(CarsModel.CAR_LIST_PATH, CarsModel.newCarMapNode()));
465 NormalizedNode<?, ?> peopleNode = PeopleModel.create();
466 final ShardDataTreeCohort cohort3 = newShardDataTreeCohort(snapshot ->
467 snapshot.write(PeopleModel.BASE_PATH, peopleNode));
469 immediateCanCommit(cohort1);
470 FutureCallback<Void> canCommitCallback2 = coordinatedCanCommit(cohort2);
472 coordinatedPreCommit(cohort1);
473 verify(canCommitCallback2).onSuccess(null);
475 FutureCallback<Void> mockAbortCallback = mock(FutureCallback.class);
476 doNothing().when(mockAbortCallback).onSuccess(null);
477 cohort1.abort(mockAbortCallback);
478 verify(mockAbortCallback).onSuccess(null);
480 FutureCallback<DataTreeCandidate> preCommitCallback2 = coordinatedPreCommit(cohort2);
481 verify(preCommitCallback2).onFailure(any(Throwable.class));
483 immediateCanCommit(cohort3);
484 immediatePreCommit(cohort3);
485 immediateCommit(cohort3);
487 final DataTreeSnapshot snapshot =
488 shardDataTree.newReadOnlyTransaction(nextTransactionId()).getSnapshot();
489 Optional<NormalizedNode<?, ?>> optional = snapshot.readNode(PeopleModel.BASE_PATH);
490 assertTrue("People node present", optional.isPresent());
491 assertEquals("People node", peopleNode, optional.get());
495 public void testUintCommitPayload() throws IOException {
496 shardDataTree.applyRecoveryPayload(CommitTransactionPayload.create(nextTransactionId(),
497 DataTreeCandidates.fromNormalizedNode(YangInstanceIdentifier.empty(), bigIntegerRoot()),
498 PayloadVersion.SODIUM_SR1));
504 public void testUintSnapshot() throws IOException, DataValidationFailedException {
505 shardDataTree.applyRecoverySnapshot(new ShardSnapshotState(new MetadataShardDataTreeSnapshot(bigIntegerRoot()),
512 public void testUintReplay() throws DataValidationFailedException, IOException {
513 // Commit two writes and one merge, saving the data tree candidate for each.
517 final DataTree dataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
519 DataTreeModification mod = dataTree.takeSnapshot().newModification();
520 mod.write(CarsModel.BASE_PATH, Builders.containerBuilder()
521 .withNodeIdentifier(new NodeIdentifier(CarsModel.BASE_QNAME))
522 .withChild(Builders.mapBuilder()
523 .withNodeIdentifier(new NodeIdentifier(CarsModel.CAR_QNAME))
524 .withChild(createCar("one", BigInteger.ONE))
528 dataTree.validate(mod);
529 final DataTreeCandidate first = dataTree.prepare(mod);
530 dataTree.commit(first);
532 mod = dataTree.takeSnapshot().newModification();
533 mod.write(CarsModel.newCarPath("two"), createCar("two", BigInteger.TWO));
535 dataTree.validate(mod);
536 final DataTreeCandidate second = dataTree.prepare(mod);
537 dataTree.commit(second);
539 mod = dataTree.takeSnapshot().newModification();
540 mod.merge(CarsModel.CAR_LIST_PATH, Builders.mapBuilder()
541 .withNodeIdentifier(new NodeIdentifier(CarsModel.CAR_QNAME))
542 .withChild(createCar("three", BigInteger.TEN))
545 dataTree.validate(mod);
546 final DataTreeCandidate third = dataTree.prepare(mod);
547 dataTree.commit(third);
549 // Apply first candidate as a snapshot
550 shardDataTree.applyRecoverySnapshot(
551 new ShardSnapshotState(new MetadataShardDataTreeSnapshot(first.getRootNode().getDataAfter().get()), true));
552 // Apply the other two snapshots as transactions
553 shardDataTree.applyRecoveryPayload(CommitTransactionPayload.create(nextTransactionId(), second,
554 PayloadVersion.SODIUM_SR1));
555 shardDataTree.applyRecoveryPayload(CommitTransactionPayload.create(nextTransactionId(), third,
556 PayloadVersion.SODIUM_SR1));
558 // Verify uint translation
559 final DataTreeSnapshot snapshot = shardDataTree.newReadOnlyTransaction(nextTransactionId()).getSnapshot();
560 final NormalizedNode<?, ?> cars = snapshot.readNode(CarsModel.CAR_LIST_PATH).get();
562 assertEquals(Builders.mapBuilder()
563 .withNodeIdentifier(new NodeIdentifier(CarsModel.CAR_QNAME))
565 .withChild(createCar("one", Uint64.ONE))
566 .withChild(createCar("two", Uint64.TWO))
567 .withChild(createCar("three", Uint64.TEN))
571 private void assertCarsUint64() {
572 final DataTreeSnapshot snapshot = shardDataTree.newReadOnlyTransaction(nextTransactionId()).getSnapshot();
573 final NormalizedNode<?, ?> cars = snapshot.readNode(CarsModel.CAR_LIST_PATH).get();
575 assertEquals(Builders.mapBuilder()
576 .withNodeIdentifier(new NodeIdentifier(CarsModel.CAR_QNAME))
578 .withChild(createCar("foo", Uint64.ONE))
582 private static ContainerNode bigIntegerRoot() {
583 return Builders.containerBuilder()
584 .withNodeIdentifier(new NodeIdentifier(SchemaContext.NAME))
585 .withChild(Builders.containerBuilder()
586 .withNodeIdentifier(new NodeIdentifier(CarsModel.CARS_QNAME))
587 .withChild(Builders.mapBuilder()
588 .withNodeIdentifier(new NodeIdentifier(CarsModel.CAR_QNAME))
590 .withChild(createCar("foo", BigInteger.ONE))
596 private static MapEntryNode createCar(final String name, final Object value) {
597 return Builders.mapEntryBuilder()
598 .withNodeIdentifier(NodeIdentifierWithPredicates.of(CarsModel.CAR_QNAME,CarsModel.CAR_NAME_QNAME, name))
599 .withChild(ImmutableNodes.leafNode(CarsModel.CAR_NAME_QNAME, name))
600 // Note: old BigInteger
601 .withChild(ImmutableNodes.leafNode(CarsModel.CAR_PRICE_QNAME, value))
605 private ShardDataTreeCohort newShardDataTreeCohort(final DataTreeOperation operation) {
606 final ReadWriteShardDataTreeTransaction transaction =
607 shardDataTree.newReadWriteTransaction(nextTransactionId());
608 final DataTreeModification snapshot = transaction.getSnapshot();
609 operation.execute(snapshot);
610 return shardDataTree.finishTransaction(transaction, Optional.empty());
613 @SuppressWarnings({ "rawtypes", "unchecked" })
614 private static void verifyOnDataTreeChanged(final DOMDataTreeChangeListener listener,
615 final Consumer<DataTreeCandidate> callback) {
616 ArgumentCaptor<Collection> changes = ArgumentCaptor.forClass(Collection.class);
617 verify(listener, atLeastOnce()).onDataTreeChanged(changes.capture());
618 for (Collection list : changes.getAllValues()) {
619 for (Object dtc : list) {
620 callback.accept((DataTreeCandidate)dtc);
627 private static NormalizedNode<?, ?> getCars(final ShardDataTree shardDataTree) {
628 final ReadOnlyShardDataTreeTransaction readOnlyShardDataTreeTransaction =
629 shardDataTree.newReadOnlyTransaction(nextTransactionId());
630 final DataTreeSnapshot snapshot1 = readOnlyShardDataTreeTransaction.getSnapshot();
632 final Optional<NormalizedNode<?, ?>> optional = snapshot1.readNode(CarsModel.BASE_PATH);
634 assertTrue(optional.isPresent());
636 return optional.get();
639 private static DataTreeCandidate addCar(final ShardDataTree shardDataTree) {
640 return addCar(shardDataTree, "altima");
643 private static DataTreeCandidate addCar(final ShardDataTree shardDataTree, final String name) {
644 return doTransaction(shardDataTree, snapshot -> {
645 snapshot.merge(CarsModel.BASE_PATH, CarsModel.emptyContainer());
646 snapshot.merge(CarsModel.CAR_LIST_PATH, CarsModel.newCarMapNode());
647 snapshot.write(CarsModel.newCarPath(name), CarsModel.newCarEntry(name, Uint64.valueOf(100)));
651 private static DataTreeCandidate removeCar(final ShardDataTree shardDataTree) {
652 return doTransaction(shardDataTree, snapshot -> snapshot.delete(CarsModel.newCarPath("altima")));
656 private interface DataTreeOperation {
657 void execute(DataTreeModification snapshot);
660 private static DataTreeCandidate doTransaction(final ShardDataTree shardDataTree,
661 final DataTreeOperation operation) {
662 final ReadWriteShardDataTreeTransaction transaction =
663 shardDataTree.newReadWriteTransaction(nextTransactionId());
664 final DataTreeModification snapshot = transaction.getSnapshot();
665 operation.execute(snapshot);
666 final ShardDataTreeCohort cohort = shardDataTree.finishTransaction(transaction, Optional.empty());
668 immediateCanCommit(cohort);
669 immediatePreCommit(cohort);
670 final DataTreeCandidate candidate = cohort.getCandidate();
671 immediateCommit(cohort);
676 private static DataTreeCandidate applyCandidates(final ShardDataTree shardDataTree,
677 final List<DataTreeCandidate> candidates) {
678 final ReadWriteShardDataTreeTransaction transaction =
679 shardDataTree.newReadWriteTransaction(nextTransactionId());
680 final DataTreeModification snapshot = transaction.getSnapshot();
681 for (final DataTreeCandidate candidateTip : candidates) {
682 DataTreeCandidates.applyToModification(snapshot, candidateTip);
684 final ShardDataTreeCohort cohort = shardDataTree.finishTransaction(transaction, Optional.empty());
686 immediateCanCommit(cohort);
687 immediatePreCommit(cohort);
688 final DataTreeCandidate candidate = cohort.getCandidate();
689 immediateCommit(cohort);