961caa64c9f0c211ff8677af38fd727f61a3a4f9
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / test / java / org / opendaylight / controller / cluster / raft / SnapshotManagerTest.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.cluster.raft;
9
10 import static org.junit.Assert.assertArrayEquals;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertTrue;
14 import static org.mockito.ArgumentMatchers.any;
15 import static org.mockito.ArgumentMatchers.anyLong;
16 import static org.mockito.Mockito.doNothing;
17 import static org.mockito.Mockito.doReturn;
18 import static org.mockito.Mockito.doThrow;
19 import static org.mockito.Mockito.mock;
20 import static org.mockito.Mockito.never;
21 import static org.mockito.Mockito.reset;
22 import static org.mockito.Mockito.times;
23 import static org.mockito.Mockito.verify;
24
25 import akka.actor.ActorRef;
26 import akka.persistence.SnapshotSelectionCriteria;
27 import java.io.OutputStream;
28 import java.util.List;
29 import java.util.Optional;
30 import java.util.function.Consumer;
31 import org.junit.After;
32 import org.junit.Before;
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 import org.mockito.ArgumentCaptor;
36 import org.mockito.Mock;
37 import org.mockito.junit.MockitoJUnitRunner;
38 import org.opendaylight.controller.cluster.DataPersistenceProvider;
39 import org.opendaylight.controller.cluster.io.FileBackedOutputStreamFactory;
40 import org.opendaylight.controller.cluster.raft.SnapshotManager.LastAppliedTermInformationReader;
41 import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot;
42 import org.opendaylight.controller.cluster.raft.base.messages.SendInstallSnapshot;
43 import org.opendaylight.controller.cluster.raft.base.messages.SnapshotComplete;
44 import org.opendaylight.controller.cluster.raft.behaviors.RaftActorBehavior;
45 import org.opendaylight.controller.cluster.raft.persisted.ByteState;
46 import org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry;
47 import org.opendaylight.controller.cluster.raft.persisted.Snapshot;
48 import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
49 import org.slf4j.LoggerFactory;
50
51 @RunWith(MockitoJUnitRunner.StrictStubs.class)
52 public class SnapshotManagerTest extends AbstractActorTest {
53
54     @Mock
55     private RaftActorContext mockRaftActorContext;
56
57     @Mock
58     private ConfigParams mockConfigParams;
59
60     @Mock
61     private ReplicatedLog mockReplicatedLog;
62
63     @Mock
64     private DataPersistenceProvider mockDataPersistenceProvider;
65
66     @Mock
67     private RaftActorBehavior mockRaftActorBehavior;
68
69     @Mock
70     private Consumer<Optional<OutputStream>> mockProcedure;
71
72     @Mock
73     private ElectionTerm mockElectionTerm;
74
75     private SnapshotManager snapshotManager;
76
77     private TestActorFactory factory;
78
79     private ActorRef actorRef;
80
81     @Before
82     public void setUp() {
83         doReturn(false).when(mockRaftActorContext).hasFollowers();
84         doReturn(mockConfigParams).when(mockRaftActorContext).getConfigParams();
85         doReturn(10L).when(mockConfigParams).getSnapshotBatchCount();
86         doReturn(70).when(mockConfigParams).getSnapshotDataThresholdPercentage();
87         doReturn(mockReplicatedLog).when(mockRaftActorContext).getReplicatedLog();
88         doReturn("123").when(mockRaftActorContext).getId();
89         doReturn(mockDataPersistenceProvider).when(mockRaftActorContext).getPersistenceProvider();
90         doReturn(mockRaftActorBehavior).when(mockRaftActorContext).getCurrentBehavior();
91         doReturn("123").when(mockRaftActorBehavior).getLeaderId();
92
93         doReturn(mockElectionTerm).when(mockRaftActorContext).getTermInformation();
94         doReturn(5L).when(mockElectionTerm).getCurrentTerm();
95         doReturn("member5").when(mockElectionTerm).getVotedFor();
96
97         doReturn(new FileBackedOutputStreamFactory(10000000, "target"))
98                 .when(mockRaftActorContext).getFileBackedOutputStreamFactory();
99
100         snapshotManager = new SnapshotManager(mockRaftActorContext, LoggerFactory.getLogger(this.getClass()));
101         factory = new TestActorFactory(getSystem());
102
103         actorRef = factory.createActor(MessageCollectorActor.props(), factory.generateActorId("test-"));
104         doReturn(actorRef).when(mockRaftActorContext).getActor();
105
106         snapshotManager.setCreateSnapshotConsumer(mockProcedure);
107     }
108
109     @After
110     public void tearDown() {
111         factory.close();
112     }
113
114     @Test
115     public void testConstruction() {
116         assertFalse(snapshotManager.isCapturing());
117     }
118
119     @SuppressWarnings({ "unchecked", "rawtypes" })
120     @Test
121     public void testCaptureToInstall() {
122
123         // Force capturing toInstall = true
124         snapshotManager.captureToInstall(new SimpleReplicatedLogEntry(0, 1,
125                 new MockRaftActorContext.MockPayload()), 0, "follower-1");
126
127         assertTrue(snapshotManager.isCapturing());
128
129         ArgumentCaptor<Optional> outputStream = ArgumentCaptor.forClass(Optional.class);
130         verify(mockProcedure).accept(outputStream.capture());
131         assertEquals("isPresent", true, outputStream.getValue().isPresent());
132
133         CaptureSnapshot captureSnapshot = snapshotManager.getCaptureSnapshot();
134
135         // LastIndex and LastTerm are picked up from the lastLogEntry
136         assertEquals(0L, captureSnapshot.getLastIndex());
137         assertEquals(1L, captureSnapshot.getLastTerm());
138
139         // Since the actor does not have any followers (no peer addresses) lastApplied will be from lastLogEntry
140         assertEquals(0L, captureSnapshot.getLastAppliedIndex());
141         assertEquals(1L, captureSnapshot.getLastAppliedTerm());
142
143         //
144         assertEquals(-1L, captureSnapshot.getReplicatedToAllIndex());
145         assertEquals(-1L, captureSnapshot.getReplicatedToAllTerm());
146         MessageCollectorActor.clearMessages(actorRef);
147     }
148
149     @SuppressWarnings({ "rawtypes", "unchecked" })
150     @Test
151     public void testCapture() {
152         boolean capture = snapshotManager.capture(new SimpleReplicatedLogEntry(9, 1,
153                 new MockRaftActorContext.MockPayload()), 9);
154
155         assertTrue(capture);
156
157         assertTrue(snapshotManager.isCapturing());
158
159         ArgumentCaptor<Optional> outputStream = ArgumentCaptor.forClass(Optional.class);
160         verify(mockProcedure).accept(outputStream.capture());
161         assertEquals("isPresent", false, outputStream.getValue().isPresent());
162
163         CaptureSnapshot captureSnapshot = snapshotManager.getCaptureSnapshot();
164
165         // LastIndex and LastTerm are picked up from the lastLogEntry
166         assertEquals(9L, captureSnapshot.getLastIndex());
167         assertEquals(1L, captureSnapshot.getLastTerm());
168
169         // Since the actor does not have any followers (no peer addresses) lastApplied will be from lastLogEntry
170         assertEquals(9L, captureSnapshot.getLastAppliedIndex());
171         assertEquals(1L, captureSnapshot.getLastAppliedTerm());
172
173         //
174         assertEquals(-1L, captureSnapshot.getReplicatedToAllIndex());
175         assertEquals(-1L, captureSnapshot.getReplicatedToAllTerm());
176
177         MessageCollectorActor.clearMessages(actorRef);
178     }
179
180     @SuppressWarnings({ "unchecked", "rawtypes" })
181     @Test
182     public void testCaptureWithNullLastLogEntry() {
183         boolean capture = snapshotManager.capture(null, 1);
184
185         assertTrue(capture);
186
187         assertTrue(snapshotManager.isCapturing());
188
189         ArgumentCaptor<Optional> outputStream = ArgumentCaptor.forClass(Optional.class);
190         verify(mockProcedure).accept(outputStream.capture());
191         assertEquals("isPresent", false, outputStream.getValue().isPresent());
192
193         CaptureSnapshot captureSnapshot = snapshotManager.getCaptureSnapshot();
194
195         // LastIndex and LastTerm are picked up from the lastLogEntry
196         assertEquals(0, captureSnapshot.getLastIndex());
197         assertEquals(0, captureSnapshot.getLastTerm());
198
199         // Since the actor does not have any followers (no peer addresses) lastApplied will be from lastLogEntry
200         assertEquals(0, captureSnapshot.getLastAppliedIndex());
201         assertEquals(0, captureSnapshot.getLastAppliedTerm());
202
203         //
204         assertEquals(-1L, captureSnapshot.getReplicatedToAllIndex());
205         assertEquals(-1L, captureSnapshot.getReplicatedToAllTerm());
206         MessageCollectorActor.clearMessages(actorRef);
207     }
208
209     @Test
210     public void testCaptureWithCreateProcedureError() {
211         doThrow(new RuntimeException("mock")).when(mockProcedure).accept(any());
212
213         boolean capture = snapshotManager.capture(new SimpleReplicatedLogEntry(9, 1,
214                 new MockRaftActorContext.MockPayload()), 9);
215
216         assertFalse(capture);
217
218         assertFalse(snapshotManager.isCapturing());
219
220         verify(mockProcedure).accept(any());
221     }
222
223     @SuppressWarnings("unchecked")
224     @Test
225     public void testIllegalCapture() {
226         boolean capture = snapshotManager.capture(new SimpleReplicatedLogEntry(9, 1,
227                 new MockRaftActorContext.MockPayload()), 9);
228
229         assertTrue(capture);
230
231         verify(mockProcedure).accept(any());
232
233         reset(mockProcedure);
234
235         // This will not cause snapshot capture to start again
236         capture = snapshotManager.capture(new SimpleReplicatedLogEntry(9, 1,
237                 new MockRaftActorContext.MockPayload()), 9);
238
239         assertFalse(capture);
240
241         verify(mockProcedure, never()).accept(any());
242     }
243
244     @Test
245     public void testPersistWhenReplicatedToAllIndexMinusOne() {
246         doReturn(7L).when(mockReplicatedLog).getSnapshotIndex();
247         doReturn(1L).when(mockReplicatedLog).getSnapshotTerm();
248
249         doReturn(true).when(mockRaftActorContext).hasFollowers();
250
251         doReturn(8L).when(mockRaftActorContext).getLastApplied();
252
253         ReplicatedLogEntry lastLogEntry = new SimpleReplicatedLogEntry(9L, 3L, new MockRaftActorContext.MockPayload());
254
255         ReplicatedLogEntry lastAppliedEntry = new SimpleReplicatedLogEntry(
256                 8L, 2L, new MockRaftActorContext.MockPayload());
257
258         doReturn(lastAppliedEntry).when(mockReplicatedLog).get(8L);
259         doReturn(List.of(lastLogEntry)).when(mockReplicatedLog).getFrom(9L);
260
261         // when replicatedToAllIndex = -1
262         snapshotManager.capture(lastLogEntry, -1);
263
264         ByteState snapshotState = ByteState.of(new byte[] {1,2,3,4,5,6,7,8,9,10});
265         snapshotManager.persist(snapshotState, Optional.empty(), Runtime.getRuntime().totalMemory());
266
267         ArgumentCaptor<Snapshot> snapshotArgumentCaptor = ArgumentCaptor.forClass(Snapshot.class);
268         verify(mockDataPersistenceProvider).saveSnapshot(snapshotArgumentCaptor.capture());
269
270         Snapshot snapshot = snapshotArgumentCaptor.getValue();
271
272         assertEquals("getLastTerm", 3L, snapshot.getLastTerm());
273         assertEquals("getLastIndex", 9L, snapshot.getLastIndex());
274         assertEquals("getLastAppliedTerm", 2L, snapshot.getLastAppliedTerm());
275         assertEquals("getLastAppliedIndex", 8L, snapshot.getLastAppliedIndex());
276         assertEquals("getState", snapshotState, snapshot.getState());
277         assertEquals("getUnAppliedEntries", List.of(lastLogEntry), snapshot.getUnAppliedEntries());
278         assertEquals("electionTerm", mockElectionTerm.getCurrentTerm(), snapshot.getElectionTerm());
279         assertEquals("electionVotedFor", mockElectionTerm.getVotedFor(), snapshot.getElectionVotedFor());
280
281         verify(mockReplicatedLog).snapshotPreCommit(7L, 1L);
282     }
283
284     @Test
285     public void testPersistWhenReplicatedToAllIndexNotMinus() {
286         doReturn(45L).when(mockReplicatedLog).getSnapshotIndex();
287         doReturn(6L).when(mockReplicatedLog).getSnapshotTerm();
288         ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
289         doReturn(null).when(mockReplicatedLog).get(0);
290         doReturn(replicatedLogEntry).when(mockReplicatedLog).get(9);
291         doReturn(6L).when(replicatedLogEntry).getTerm();
292         doReturn(9L).when(replicatedLogEntry).getIndex();
293
294         // when replicatedToAllIndex != -1
295         snapshotManager.capture(new SimpleReplicatedLogEntry(9, 6, new MockRaftActorContext.MockPayload()), 9);
296
297         ByteState snapshotState = ByteState.of(new byte[] {1,2,3,4,5,6,7,8,9,10});
298         snapshotManager.persist(snapshotState, Optional.empty(), Runtime.getRuntime().totalMemory());
299
300         ArgumentCaptor<Snapshot> snapshotArgumentCaptor = ArgumentCaptor.forClass(Snapshot.class);
301         verify(mockDataPersistenceProvider).saveSnapshot(snapshotArgumentCaptor.capture());
302
303         Snapshot snapshot = snapshotArgumentCaptor.getValue();
304
305         assertEquals("getLastTerm", 6L, snapshot.getLastTerm());
306         assertEquals("getLastIndex", 9L, snapshot.getLastIndex());
307         assertEquals("getLastAppliedTerm", 6L, snapshot.getLastAppliedTerm());
308         assertEquals("getLastAppliedIndex", 9L, snapshot.getLastAppliedIndex());
309         assertEquals("getState", snapshotState, snapshot.getState());
310         assertEquals("getUnAppliedEntries size", 0, snapshot.getUnAppliedEntries().size());
311
312         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
313
314         verify(mockRaftActorBehavior).setReplicatedToAllIndex(9);
315     }
316
317     @Test
318     public void testPersistWhenReplicatedLogDataSizeGreaterThanThreshold() {
319         doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize();
320
321         // when replicatedToAllIndex = -1
322         snapshotManager.capture(new SimpleReplicatedLogEntry(9, 6, new MockRaftActorContext.MockPayload()), -1);
323
324         snapshotManager.persist(ByteState.empty(), Optional.empty(), Runtime.getRuntime().totalMemory());
325
326         verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
327
328         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
329
330         verify(mockRaftActorBehavior, never()).setReplicatedToAllIndex(anyLong());
331     }
332
333     @Test
334     public void testPersistWhenReplicatedLogSizeExceedsSnapshotBatchCount() {
335         doReturn(10L).when(mockReplicatedLog).size(); // matches snapshotBatchCount
336         doReturn(100).when(mockReplicatedLog).dataSize();
337
338         doReturn(5L).when(mockReplicatedLog).getSnapshotIndex();
339         doReturn(5L).when(mockReplicatedLog).getSnapshotTerm();
340
341         long replicatedToAllIndex = 1;
342         ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
343         doReturn(null).when(mockReplicatedLog).get(0);
344         doReturn(replicatedLogEntry).when(mockReplicatedLog).get(replicatedToAllIndex);
345         doReturn(6L).when(replicatedLogEntry).getTerm();
346         doReturn(replicatedToAllIndex).when(replicatedLogEntry).getIndex();
347
348         snapshotManager.capture(new SimpleReplicatedLogEntry(9, 6,
349                 new MockRaftActorContext.MockPayload()), replicatedToAllIndex);
350
351         snapshotManager.persist(ByteState.empty(), Optional.empty(), 2000000L);
352
353         verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
354
355         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
356
357         verify(mockRaftActorBehavior).setReplicatedToAllIndex(replicatedToAllIndex);
358     }
359
360     @SuppressWarnings({ "rawtypes", "unchecked" })
361     @Test
362     public void testPersistSendInstallSnapshot() throws Exception {
363         doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize();
364         doNothing().when(mockProcedure).accept(any());
365
366         // when replicatedToAllIndex = -1
367         boolean capture = snapshotManager.captureToInstall(new SimpleReplicatedLogEntry(9, 6,
368                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
369
370         assertTrue(capture);
371
372         ByteState snapshotState = ByteState.of(new byte[] {1,2,3,4,5,6,7,8,9,10});
373
374         ArgumentCaptor<Optional> installSnapshotStreamCapture = ArgumentCaptor.forClass(Optional.class);
375         verify(mockProcedure).accept(installSnapshotStreamCapture.capture());
376
377         Optional<OutputStream> installSnapshotStream = installSnapshotStreamCapture.getValue();
378         assertEquals("isPresent", true, installSnapshotStream.isPresent());
379
380         installSnapshotStream.get().write(snapshotState.getBytes());
381
382         snapshotManager.persist(snapshotState, installSnapshotStream, Runtime.getRuntime().totalMemory());
383
384         assertTrue(snapshotManager.isCapturing());
385
386         verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
387
388         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
389
390         ArgumentCaptor<SendInstallSnapshot> sendInstallSnapshotArgumentCaptor
391                 = ArgumentCaptor.forClass(SendInstallSnapshot.class);
392
393         verify(mockRaftActorBehavior).handleMessage(any(ActorRef.class), sendInstallSnapshotArgumentCaptor.capture());
394
395         SendInstallSnapshot sendInstallSnapshot = sendInstallSnapshotArgumentCaptor.getValue();
396
397         assertEquals("state", snapshotState, sendInstallSnapshot.getSnapshot().getState());
398         assertArrayEquals("state", snapshotState.getBytes(), sendInstallSnapshot.getSnapshotBytes().read());
399     }
400
401     @Test
402     public void testCallingPersistWithoutCaptureWillDoNothing() {
403         snapshotManager.persist(ByteState.empty(), Optional.empty(), Runtime.getRuntime().totalMemory());
404
405         verify(mockDataPersistenceProvider, never()).saveSnapshot(any(Snapshot.class));
406
407         verify(mockReplicatedLog, never()).snapshotPreCommit(9L, 6L);
408
409         verify(mockRaftActorBehavior, never()).handleMessage(any(ActorRef.class), any(SendInstallSnapshot.class));
410     }
411
412     @Test
413     public void testCallingPersistTwiceWillDoNoHarm() {
414         doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize();
415
416         // when replicatedToAllIndex = -1
417         snapshotManager.capture(new SimpleReplicatedLogEntry(9, 6, new MockRaftActorContext.MockPayload()), -1);
418
419         snapshotManager.persist(ByteState.empty(), Optional.empty(), Runtime.getRuntime().totalMemory());
420
421         snapshotManager.persist(ByteState.empty(), Optional.empty(), Runtime.getRuntime().totalMemory());
422
423         verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
424
425         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
426     }
427
428     @Test
429     public void testCommit() {
430         doReturn(50L).when(mockDataPersistenceProvider).getLastSequenceNumber();
431
432         // when replicatedToAllIndex = -1
433         snapshotManager.capture(new SimpleReplicatedLogEntry(9, 6, new MockRaftActorContext.MockPayload()), -1);
434
435         snapshotManager.persist(ByteState.empty(), Optional.empty(), Runtime.getRuntime().totalMemory());
436
437         assertTrue(snapshotManager.isCapturing());
438
439         snapshotManager.commit(100L, 1234L);
440
441         assertFalse(snapshotManager.isCapturing());
442
443         verify(mockReplicatedLog).snapshotCommit();
444
445         verify(mockDataPersistenceProvider).deleteMessages(50L);
446
447         ArgumentCaptor<SnapshotSelectionCriteria> criteriaCaptor =
448                 ArgumentCaptor.forClass(SnapshotSelectionCriteria.class);
449
450         verify(mockDataPersistenceProvider).deleteSnapshots(criteriaCaptor.capture());
451
452         assertEquals(Long.MAX_VALUE, criteriaCaptor.getValue().maxSequenceNr());
453         assertEquals(1233L, criteriaCaptor.getValue().maxTimestamp());
454
455         MessageCollectorActor.expectFirstMatching(actorRef, SnapshotComplete.class);
456     }
457
458     @Test
459     public void testCommitBeforePersist() {
460         // when replicatedToAllIndex = -1
461         snapshotManager.capture(new SimpleReplicatedLogEntry(9, 6, new MockRaftActorContext.MockPayload()), -1);
462
463         snapshotManager.commit(100L, 0);
464
465         verify(mockReplicatedLog, never()).snapshotCommit();
466
467         verify(mockDataPersistenceProvider, never()).deleteMessages(100L);
468
469         verify(mockDataPersistenceProvider, never()).deleteSnapshots(any(SnapshotSelectionCriteria.class));
470
471     }
472
473     @Test
474     public void testCommitBeforeCapture() {
475         snapshotManager.commit(100L, 0);
476
477         verify(mockReplicatedLog, never()).snapshotCommit();
478
479         verify(mockDataPersistenceProvider, never()).deleteMessages(anyLong());
480
481         verify(mockDataPersistenceProvider, never()).deleteSnapshots(any(SnapshotSelectionCriteria.class));
482
483     }
484
485     @Test
486     public void testCallingCommitMultipleTimesCausesNoHarm() {
487         doReturn(50L).when(mockDataPersistenceProvider).getLastSequenceNumber();
488
489         // when replicatedToAllIndex = -1
490         snapshotManager.capture(new SimpleReplicatedLogEntry(9, 6, new MockRaftActorContext.MockPayload()), -1);
491
492         snapshotManager.persist(ByteState.empty(), Optional.empty(), Runtime.getRuntime().totalMemory());
493
494         snapshotManager.commit(100L, 0);
495
496         snapshotManager.commit(100L, 0);
497
498         verify(mockReplicatedLog, times(1)).snapshotCommit();
499
500         verify(mockDataPersistenceProvider, times(1)).deleteMessages(50L);
501
502         verify(mockDataPersistenceProvider, times(1)).deleteSnapshots(any(SnapshotSelectionCriteria.class));
503     }
504
505     @Test
506     public void testRollback() {
507         // when replicatedToAllIndex = -1
508         snapshotManager.capture(new SimpleReplicatedLogEntry(9, 6, new MockRaftActorContext.MockPayload()), -1);
509
510         snapshotManager.persist(ByteState.empty(), Optional.empty(), Runtime.getRuntime().totalMemory());
511
512         snapshotManager.rollback();
513
514         verify(mockReplicatedLog).snapshotRollback();
515
516         MessageCollectorActor.expectFirstMatching(actorRef, SnapshotComplete.class);
517     }
518
519
520     @Test
521     public void testRollbackBeforePersist() {
522         // when replicatedToAllIndex = -1
523         snapshotManager.capture(new SimpleReplicatedLogEntry(9, 6, new MockRaftActorContext.MockPayload()), -1);
524
525         snapshotManager.rollback();
526
527         verify(mockReplicatedLog, never()).snapshotRollback();
528     }
529
530     @Test
531     public void testRollbackBeforeCapture() {
532         snapshotManager.rollback();
533
534         verify(mockReplicatedLog, never()).snapshotRollback();
535     }
536
537     @Test
538     public void testCallingRollbackMultipleTimesCausesNoHarm() {
539         // when replicatedToAllIndex = -1
540         snapshotManager.capture(new SimpleReplicatedLogEntry(9, 6, new MockRaftActorContext.MockPayload()), -1);
541
542         snapshotManager.persist(ByteState.empty(), Optional.empty(), Runtime.getRuntime().totalMemory());
543
544         snapshotManager.rollback();
545
546         snapshotManager.rollback();
547
548         verify(mockReplicatedLog, times(1)).snapshotRollback();
549     }
550
551     @Test
552     public void testTrimLogWhenTrimIndexLessThanLastApplied() {
553         doReturn(20L).when(mockRaftActorContext).getLastApplied();
554
555         ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
556         doReturn(true).when(mockReplicatedLog).isPresent(10);
557         doReturn(replicatedLogEntry).when(mockReplicatedLog).get(10);
558         doReturn(5L).when(replicatedLogEntry).getTerm();
559
560         long retIndex = snapshotManager.trimLog(10);
561         assertEquals("return index", 10L, retIndex);
562
563         verify(mockReplicatedLog).snapshotPreCommit(10, 5);
564         verify(mockReplicatedLog).snapshotCommit(false);
565
566         verify(mockRaftActorBehavior, never()).setReplicatedToAllIndex(anyLong());
567     }
568
569     @Test
570     public void testTrimLogWhenLastAppliedNotSet() {
571         doReturn(-1L).when(mockRaftActorContext).getLastApplied();
572
573         long retIndex = snapshotManager.trimLog(10);
574         assertEquals("return index", -1L, retIndex);
575
576         verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong());
577         verify(mockReplicatedLog, never()).snapshotCommit(false);
578
579         verify(mockRaftActorBehavior, never()).setReplicatedToAllIndex(anyLong());
580     }
581
582     @Test
583     public void testTrimLogWhenLastAppliedZero() {
584         doReturn(0L).when(mockRaftActorContext).getLastApplied();
585
586         long retIndex = snapshotManager.trimLog(10);
587         assertEquals("return index", -1L, retIndex);
588
589         verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong());
590         verify(mockReplicatedLog, never()).snapshotCommit(false);
591
592         verify(mockRaftActorBehavior, never()).setReplicatedToAllIndex(anyLong());
593     }
594
595     @Test
596     public void testTrimLogWhenTrimIndexNotPresent() {
597         doReturn(20L).when(mockRaftActorContext).getLastApplied();
598
599         doReturn(false).when(mockReplicatedLog).isPresent(10);
600
601         long retIndex = snapshotManager.trimLog(10);
602         assertEquals("return index", -1L, retIndex);
603
604         verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong());
605         verify(mockReplicatedLog, never()).snapshotCommit(false);
606
607         // Trim index is greater than replicatedToAllIndex so should update it.
608         verify(mockRaftActorBehavior).setReplicatedToAllIndex(10L);
609     }
610
611     @Test
612     public void testTrimLogAfterCapture() {
613         boolean capture = snapshotManager.capture(new SimpleReplicatedLogEntry(9, 1,
614                 new MockRaftActorContext.MockPayload()), 9);
615
616         assertTrue(capture);
617
618         assertTrue(snapshotManager.isCapturing());
619
620         snapshotManager.trimLog(10);
621
622         verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong());
623         verify(mockReplicatedLog, never()).snapshotCommit(false);
624     }
625
626     @Test
627     public void testTrimLogAfterCaptureToInstall() {
628         boolean capture = snapshotManager.capture(new SimpleReplicatedLogEntry(9, 1,
629                 new MockRaftActorContext.MockPayload()), 9);
630
631         assertTrue(capture);
632
633         assertTrue(snapshotManager.isCapturing());
634
635         snapshotManager.trimLog(10);
636
637         verify(mockReplicatedLog, never()).snapshotPreCommit(10, 5);
638         verify(mockReplicatedLog, never()).snapshotCommit();
639     }
640
641     @Test
642     public void testLastAppliedTermInformationReader() {
643
644         LastAppliedTermInformationReader reader = new LastAppliedTermInformationReader();
645
646         doReturn(4L).when(mockReplicatedLog).getSnapshotTerm();
647         doReturn(7L).when(mockReplicatedLog).getSnapshotIndex();
648
649         ReplicatedLogEntry lastLogEntry = new SimpleReplicatedLogEntry(9L, 6L,
650                 new MockRaftActorContext.MockPayload());
651
652         // No followers and valid lastLogEntry
653         reader.init(mockReplicatedLog, 1L, lastLogEntry, false);
654
655         assertEquals("getTerm", 6L, reader.getTerm());
656         assertEquals("getIndex", 9L, reader.getIndex());
657
658         // No followers and null lastLogEntry
659         reader.init(mockReplicatedLog, 1L, null, false);
660
661         assertEquals("getTerm", -1L, reader.getTerm());
662         assertEquals("getIndex", -1L, reader.getIndex());
663
664         // Followers and valid originalIndex entry
665         doReturn(new SimpleReplicatedLogEntry(8L, 5L,
666                 new MockRaftActorContext.MockPayload())).when(mockReplicatedLog).get(8L);
667         reader.init(mockReplicatedLog, 8L, lastLogEntry, true);
668
669         assertEquals("getTerm", 5L, reader.getTerm());
670         assertEquals("getIndex", 8L, reader.getIndex());
671
672         // Followers and null originalIndex entry and valid snapshot index
673         reader.init(mockReplicatedLog, 7L, lastLogEntry, true);
674
675         assertEquals("getTerm", 4L, reader.getTerm());
676         assertEquals("getIndex", 7L, reader.getIndex());
677
678         // Followers and null originalIndex entry and invalid snapshot index
679         doReturn(-1L).when(mockReplicatedLog).getSnapshotIndex();
680         reader.init(mockReplicatedLog, 7L, lastLogEntry, true);
681
682         assertEquals("getTerm", -1L, reader.getTerm());
683         assertEquals("getIndex", -1L, reader.getIndex());
684     }
685 }