1 package org.opendaylight.controller.cluster.raft;
3 import static junit.framework.TestCase.assertFalse;
4 import static junit.framework.TestCase.assertTrue;
5 import static org.junit.Assert.assertEquals;
6 import static org.mockito.Matchers.any;
7 import static org.mockito.Matchers.anyLong;
8 import static org.mockito.Mockito.doReturn;
9 import static org.mockito.Mockito.mock;
10 import static org.mockito.Mockito.never;
11 import static org.mockito.Mockito.reset;
12 import static org.mockito.Mockito.times;
13 import static org.mockito.Mockito.verify;
14 import akka.actor.ActorRef;
15 import akka.japi.Procedure;
16 import akka.persistence.SnapshotSelectionCriteria;
17 import akka.testkit.TestActorRef;
18 import java.util.Arrays;
19 import java.util.HashMap;
20 import java.util.List;
21 import org.junit.After;
22 import org.junit.Before;
23 import org.junit.Test;
24 import org.mockito.ArgumentCaptor;
25 import org.mockito.Mock;
26 import org.mockito.MockitoAnnotations;
27 import org.opendaylight.controller.cluster.DataPersistenceProvider;
28 import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot;
29 import org.opendaylight.controller.cluster.raft.base.messages.SendInstallSnapshot;
30 import org.opendaylight.controller.cluster.raft.behaviors.RaftActorBehavior;
31 import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
32 import org.slf4j.LoggerFactory;
34 public class SnapshotManagerTest extends AbstractActorTest {
37 private RaftActorContext mockRaftActorContext;
40 private ConfigParams mockConfigParams;
43 private ReplicatedLog mockReplicatedLog;
46 private DataPersistenceProvider mockDataPersistenceProvider;
49 private RaftActorBehavior mockRaftActorBehavior;
52 private Procedure<Void> mockProcedure;
54 private SnapshotManager snapshotManager;
56 private TestActorFactory factory;
58 private TestActorRef<MessageCollectorActor> actorRef;
62 MockitoAnnotations.initMocks(this);
64 doReturn(new HashMap<>()).when(mockRaftActorContext).getPeerAddresses();
65 doReturn(mockConfigParams).when(mockRaftActorContext).getConfigParams();
66 doReturn(10L).when(mockConfigParams).getSnapshotBatchCount();
67 doReturn(mockReplicatedLog).when(mockRaftActorContext).getReplicatedLog();
68 doReturn("123").when(mockRaftActorContext).getId();
69 doReturn("123").when(mockRaftActorBehavior).getLeaderId();
71 snapshotManager = new SnapshotManager(mockRaftActorContext, LoggerFactory.getLogger(this.getClass()));
72 factory = new TestActorFactory(getSystem());
74 actorRef = factory.createTestActor(MessageCollectorActor.props(), factory.generateActorId("test-"));
75 doReturn(actorRef).when(mockRaftActorContext).getActor();
80 public void tearDown(){
85 public void testConstruction(){
86 assertEquals(false, snapshotManager.isCapturing());
90 public void testCaptureToInstall(){
92 // Force capturing toInstall = true
93 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(1, 0,
94 new MockRaftActorContext.MockPayload()), 0, "follower-1");
96 assertEquals(true, snapshotManager.isCapturing());
98 CaptureSnapshot captureSnapshot = MessageCollectorActor.expectFirstMatching(actorRef, CaptureSnapshot.class);
100 // LastIndex and LastTerm are picked up from the lastLogEntry
101 assertEquals(0L, captureSnapshot.getLastIndex());
102 assertEquals(1L, captureSnapshot.getLastTerm());
104 // Since the actor does not have any followers (no peer addresses) lastApplied will be from lastLogEntry
105 assertEquals(0L, captureSnapshot.getLastAppliedIndex());
106 assertEquals(1L, captureSnapshot.getLastAppliedTerm());
109 assertEquals(-1L, captureSnapshot.getReplicatedToAllIndex());
110 assertEquals(-1L, captureSnapshot.getReplicatedToAllTerm());
111 actorRef.underlyingActor().clear();
115 public void testCapture(){
116 boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
117 new MockRaftActorContext.MockPayload()), 9);
121 assertEquals(true, snapshotManager.isCapturing());
123 CaptureSnapshot captureSnapshot = MessageCollectorActor.expectFirstMatching(actorRef, CaptureSnapshot.class);
124 // LastIndex and LastTerm are picked up from the lastLogEntry
125 assertEquals(9L, captureSnapshot.getLastIndex());
126 assertEquals(1L, captureSnapshot.getLastTerm());
128 // Since the actor does not have any followers (no peer addresses) lastApplied will be from lastLogEntry
129 assertEquals(9L, captureSnapshot.getLastAppliedIndex());
130 assertEquals(1L, captureSnapshot.getLastAppliedTerm());
133 assertEquals(-1L, captureSnapshot.getReplicatedToAllIndex());
134 assertEquals(-1L, captureSnapshot.getReplicatedToAllTerm());
136 actorRef.underlyingActor().clear();
141 public void testIllegalCapture() throws Exception {
142 boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
143 new MockRaftActorContext.MockPayload()), 9);
147 List<CaptureSnapshot> allMatching = MessageCollectorActor.getAllMatching(actorRef, CaptureSnapshot.class);
149 assertEquals(1, allMatching.size());
151 // This will not cause snapshot capture to start again
152 capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
153 new MockRaftActorContext.MockPayload()), 9);
155 assertFalse(capture);
157 allMatching = MessageCollectorActor.getAllMatching(actorRef, CaptureSnapshot.class);
159 assertEquals(1, allMatching.size());
163 public void testPersistWhenReplicatedToAllIndexMinusOne(){
164 doReturn("123").when(mockRaftActorContext).getId();
165 doReturn(45L).when(mockReplicatedLog).getSnapshotIndex();
166 doReturn(6L).when(mockReplicatedLog).getSnapshotTerm();
168 // when replicatedToAllIndex = -1
169 snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
170 new MockRaftActorContext.MockPayload()), -1);
172 snapshotManager.create(mockProcedure);
174 byte[] bytes = new byte[] {1,2,3,4,5,6,7,8,9,10};
175 snapshotManager.persist(mockDataPersistenceProvider, bytes, mockRaftActorBehavior
176 , Runtime.getRuntime().totalMemory());
178 ArgumentCaptor<Snapshot> snapshotArgumentCaptor = ArgumentCaptor.forClass(Snapshot.class);
179 verify(mockDataPersistenceProvider).saveSnapshot(snapshotArgumentCaptor.capture());
181 Snapshot snapshot = snapshotArgumentCaptor.getValue();
183 assertEquals(6, snapshot.getLastAppliedTerm());
184 assertEquals(9, snapshot.getLastAppliedIndex());
185 assertEquals(9, snapshot.getLastIndex());
186 assertEquals(6, snapshot.getLastTerm());
187 assertEquals(10, snapshot.getState().length);
188 assertTrue(Arrays.equals(bytes, snapshot.getState()));
189 assertEquals(0, snapshot.getUnAppliedEntries().size());
191 verify(mockReplicatedLog).snapshotPreCommit(45L, 6L);
196 public void testCreate() throws Exception {
197 // when replicatedToAllIndex = -1
198 snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
199 new MockRaftActorContext.MockPayload()), -1);
201 snapshotManager.create(mockProcedure);
203 verify(mockProcedure).apply(null);
207 public void testCallingCreateMultipleTimesCausesNoHarm() throws Exception {
208 // when replicatedToAllIndex = -1
209 snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
210 new MockRaftActorContext.MockPayload()), -1);
212 snapshotManager.create(mockProcedure);
214 snapshotManager.create(mockProcedure);
216 verify(mockProcedure, times(1)).apply(null);
220 public void testCallingCreateBeforeCapture() throws Exception {
221 snapshotManager.create(mockProcedure);
223 verify(mockProcedure, times(0)).apply(null);
227 public void testCallingCreateAfterPersist() throws Exception {
228 // when replicatedToAllIndex = -1
229 snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
230 new MockRaftActorContext.MockPayload()), -1);
232 snapshotManager.create(mockProcedure);
234 verify(mockProcedure, times(1)).apply(null);
236 snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
237 , Runtime.getRuntime().totalMemory());
239 reset(mockProcedure);
241 snapshotManager.create(mockProcedure);
243 verify(mockProcedure, never()).apply(null);
247 public void testPersistWhenReplicatedToAllIndexNotMinus(){
248 doReturn(45L).when(mockReplicatedLog).getSnapshotIndex();
249 doReturn(6L).when(mockReplicatedLog).getSnapshotTerm();
250 ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
251 doReturn(replicatedLogEntry).when(mockReplicatedLog).get(9);
252 doReturn(6L).when(replicatedLogEntry).getTerm();
253 doReturn(9L).when(replicatedLogEntry).getIndex();
255 // when replicatedToAllIndex != -1
256 snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
257 new MockRaftActorContext.MockPayload()), 9);
259 snapshotManager.create(mockProcedure);
261 snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
262 , Runtime.getRuntime().totalMemory());
264 verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
266 verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
268 verify(mockRaftActorBehavior).setReplicatedToAllIndex(9);
273 public void testPersistWhenReplicatedLogDataSizeGreaterThanThreshold(){
274 doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize();
276 // when replicatedToAllIndex = -1
277 snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
278 new MockRaftActorContext.MockPayload()), -1);
280 snapshotManager.create(mockProcedure);
282 snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
283 , Runtime.getRuntime().totalMemory());
285 verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
287 verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
291 public void testPersistSendInstallSnapshot(){
292 doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize();
294 // when replicatedToAllIndex = -1
295 boolean capture = snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
296 new MockRaftActorContext.MockPayload()), -1, "follower-1");
300 snapshotManager.create(mockProcedure);
302 byte[] bytes = new byte[] {1,2,3,4,5,6,7,8,9,10};
304 snapshotManager.persist(mockDataPersistenceProvider, bytes, mockRaftActorBehavior
305 , Runtime.getRuntime().totalMemory());
307 verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
309 verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
311 ArgumentCaptor<SendInstallSnapshot> sendInstallSnapshotArgumentCaptor
312 = ArgumentCaptor.forClass(SendInstallSnapshot.class);
314 verify(mockRaftActorBehavior).handleMessage(any(ActorRef.class), sendInstallSnapshotArgumentCaptor.capture());
316 SendInstallSnapshot sendInstallSnapshot = sendInstallSnapshotArgumentCaptor.getValue();
318 assertTrue(Arrays.equals(bytes, sendInstallSnapshot.getSnapshot().toByteArray()));
322 public void testCallingPersistWithoutCaptureWillDoNothing(){
323 snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
324 , Runtime.getRuntime().totalMemory());
326 verify(mockDataPersistenceProvider, never()).saveSnapshot(any(Snapshot.class));
328 verify(mockReplicatedLog, never()).snapshotPreCommit(9L, 6L);
330 verify(mockRaftActorBehavior, never()).handleMessage(any(ActorRef.class), any(SendInstallSnapshot.class));
333 public void testCallingPersistTwiceWillDoNoHarm(){
334 doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize();
336 // when replicatedToAllIndex = -1
337 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
338 new MockRaftActorContext.MockPayload()), -1, "follower-1");
340 snapshotManager.create(mockProcedure);
342 snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
343 , Runtime.getRuntime().totalMemory());
345 snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
346 , Runtime.getRuntime().totalMemory());
348 verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
350 verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
352 verify(mockRaftActorBehavior).handleMessage(any(ActorRef.class), any(SendInstallSnapshot.class));
356 public void testCommit(){
357 // when replicatedToAllIndex = -1
358 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
359 new MockRaftActorContext.MockPayload()), -1, "follower-1");
361 snapshotManager.create(mockProcedure);
363 snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
364 , Runtime.getRuntime().totalMemory());
366 snapshotManager.commit(mockDataPersistenceProvider, 100L);
368 verify(mockReplicatedLog).snapshotCommit();
370 verify(mockDataPersistenceProvider).deleteMessages(100L);
372 ArgumentCaptor<SnapshotSelectionCriteria> criteriaCaptor = ArgumentCaptor.forClass(SnapshotSelectionCriteria.class);
374 verify(mockDataPersistenceProvider).deleteSnapshots(criteriaCaptor.capture());
376 assertEquals(90, criteriaCaptor.getValue().maxSequenceNr()); // sequenceNumber = 100
377 // config snapShotBatchCount = 10
378 // therefore maxSequenceNumber = 90
382 public void testCommitBeforePersist(){
383 // when replicatedToAllIndex = -1
384 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
385 new MockRaftActorContext.MockPayload()), -1, "follower-1");
387 snapshotManager.commit(mockDataPersistenceProvider, 100L);
389 verify(mockReplicatedLog, never()).snapshotCommit();
391 verify(mockDataPersistenceProvider, never()).deleteMessages(100L);
393 verify(mockDataPersistenceProvider, never()).deleteSnapshots(any(SnapshotSelectionCriteria.class));
398 public void testCommitBeforeCapture(){
399 snapshotManager.commit(mockDataPersistenceProvider, 100L);
401 verify(mockReplicatedLog, never()).snapshotCommit();
403 verify(mockDataPersistenceProvider, never()).deleteMessages(anyLong());
405 verify(mockDataPersistenceProvider, never()).deleteSnapshots(any(SnapshotSelectionCriteria.class));
410 public void testCallingCommitMultipleTimesCausesNoHarm(){
411 // when replicatedToAllIndex = -1
412 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
413 new MockRaftActorContext.MockPayload()), -1, "follower-1");
415 snapshotManager.create(mockProcedure);
417 snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
418 , Runtime.getRuntime().totalMemory());
420 snapshotManager.commit(mockDataPersistenceProvider, 100L);
422 snapshotManager.commit(mockDataPersistenceProvider, 100L);
424 verify(mockReplicatedLog, times(1)).snapshotCommit();
426 verify(mockDataPersistenceProvider, times(1)).deleteMessages(100L);
428 verify(mockDataPersistenceProvider, times(1)).deleteSnapshots(any(SnapshotSelectionCriteria.class));
432 public void testRollback(){
433 // when replicatedToAllIndex = -1
434 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
435 new MockRaftActorContext.MockPayload()), -1, "follower-1");
437 snapshotManager.create(mockProcedure);
439 snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
440 , Runtime.getRuntime().totalMemory());
442 snapshotManager.rollback();
444 verify(mockReplicatedLog).snapshotRollback();
449 public void testRollbackBeforePersist(){
450 // when replicatedToAllIndex = -1
451 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
452 new MockRaftActorContext.MockPayload()), -1, "follower-1");
454 snapshotManager.rollback();
456 verify(mockReplicatedLog, never()).snapshotRollback();
460 public void testRollbackBeforeCapture(){
461 snapshotManager.rollback();
463 verify(mockReplicatedLog, never()).snapshotRollback();
467 public void testCallingRollbackMultipleTimesCausesNoHarm(){
468 // when replicatedToAllIndex = -1
469 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
470 new MockRaftActorContext.MockPayload()), -1, "follower-1");
472 snapshotManager.create(mockProcedure);
474 snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
475 , Runtime.getRuntime().totalMemory());
477 snapshotManager.rollback();
479 snapshotManager.rollback();
481 verify(mockReplicatedLog, times(1)).snapshotRollback();
485 public void testTrimLog(){
486 ElectionTerm mockElectionTerm = mock(ElectionTerm.class);
487 ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
488 doReturn(20L).when(mockRaftActorContext).getLastApplied();
489 doReturn(true).when(mockReplicatedLog).isPresent(10);
490 doReturn(mockElectionTerm).when(mockRaftActorContext).getTermInformation();
491 doReturn(5L).when(mockElectionTerm).getCurrentTerm();
492 doReturn(replicatedLogEntry).when((mockReplicatedLog)).get(10);
493 doReturn(5L).when(replicatedLogEntry).getTerm();
495 snapshotManager.trimLog(10, mockRaftActorBehavior);
497 verify(mockReplicatedLog).snapshotPreCommit(10, 5);
498 verify(mockReplicatedLog).snapshotCommit();
502 public void testTrimLogAfterCapture(){
503 boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
504 new MockRaftActorContext.MockPayload()), 9);
508 assertEquals(true, snapshotManager.isCapturing());
510 ElectionTerm mockElectionTerm = mock(ElectionTerm.class);
511 ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
512 doReturn(20L).when(mockRaftActorContext).getLastApplied();
513 doReturn(true).when(mockReplicatedLog).isPresent(10);
514 doReturn(mockElectionTerm).when(mockRaftActorContext).getTermInformation();
515 doReturn(5L).when(mockElectionTerm).getCurrentTerm();
516 doReturn(replicatedLogEntry).when((mockReplicatedLog)).get(10);
517 doReturn(5L).when(replicatedLogEntry).getTerm();
519 snapshotManager.trimLog(10, mockRaftActorBehavior);
521 verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong());
522 verify(mockReplicatedLog, never()).snapshotCommit();
527 public void testTrimLogAfterCaptureToInstall(){
528 boolean capture = snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
529 new MockRaftActorContext.MockPayload()), 9, "follower-1");
533 assertEquals(true, snapshotManager.isCapturing());
535 ElectionTerm mockElectionTerm = mock(ElectionTerm.class);
536 ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
537 doReturn(20L).when(mockRaftActorContext).getLastApplied();
538 doReturn(true).when(mockReplicatedLog).isPresent(10);
539 doReturn(mockElectionTerm).when(mockRaftActorContext).getTermInformation();
540 doReturn(5L).when(mockElectionTerm).getCurrentTerm();
541 doReturn(replicatedLogEntry).when((mockReplicatedLog)).get(10);
542 doReturn(5L).when(replicatedLogEntry).getTerm();
544 snapshotManager.trimLog(10, mockRaftActorBehavior);
546 verify(mockReplicatedLog, never()).snapshotPreCommit(10, 5);
547 verify(mockReplicatedLog, never()).snapshotCommit();