Merge "Avoid IllegalArgument on missing source"
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / test / java / org / opendaylight / controller / cluster / raft / SnapshotManagerTest.java
1 package org.opendaylight.controller.cluster.raft;
2
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;
33
34 public class SnapshotManagerTest extends AbstractActorTest {
35
36     @Mock
37     private RaftActorContext mockRaftActorContext;
38
39     @Mock
40     private ConfigParams mockConfigParams;
41
42     @Mock
43     private ReplicatedLog mockReplicatedLog;
44
45     @Mock
46     private DataPersistenceProvider mockDataPersistenceProvider;
47
48     @Mock
49     private RaftActorBehavior mockRaftActorBehavior;
50
51     @Mock
52     private Procedure<Void> mockProcedure;
53
54     private SnapshotManager snapshotManager;
55
56     private TestActorFactory factory;
57
58     private TestActorRef<MessageCollectorActor> actorRef;
59
60     @Before
61     public void setUp(){
62         MockitoAnnotations.initMocks(this);
63
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();
70
71         snapshotManager = new SnapshotManager(mockRaftActorContext, LoggerFactory.getLogger(this.getClass()));
72         factory = new TestActorFactory(getSystem());
73
74         actorRef = factory.createTestActor(MessageCollectorActor.props(), factory.generateActorId("test-"));
75         doReturn(actorRef).when(mockRaftActorContext).getActor();
76
77     }
78
79     @After
80     public void tearDown(){
81         factory.close();
82     }
83
84     @Test
85     public void testConstruction(){
86         assertEquals(false, snapshotManager.isCapturing());
87     }
88
89     @Test
90     public void testCaptureToInstall(){
91
92         // Force capturing toInstall = true
93         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(1, 0,
94                 new MockRaftActorContext.MockPayload()), 0, "follower-1");
95
96         assertEquals(true, snapshotManager.isCapturing());
97
98         CaptureSnapshot captureSnapshot = MessageCollectorActor.expectFirstMatching(actorRef, CaptureSnapshot.class);
99
100         // LastIndex and LastTerm are picked up from the lastLogEntry
101         assertEquals(0L, captureSnapshot.getLastIndex());
102         assertEquals(1L, captureSnapshot.getLastTerm());
103
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());
107
108         //
109         assertEquals(-1L, captureSnapshot.getReplicatedToAllIndex());
110         assertEquals(-1L, captureSnapshot.getReplicatedToAllTerm());
111         actorRef.underlyingActor().clear();
112     }
113
114     @Test
115     public void testCapture(){
116         boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
117                 new MockRaftActorContext.MockPayload()), 9);
118
119         assertTrue(capture);
120
121         assertEquals(true, snapshotManager.isCapturing());
122
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());
127
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());
131
132         //
133         assertEquals(-1L, captureSnapshot.getReplicatedToAllIndex());
134         assertEquals(-1L, captureSnapshot.getReplicatedToAllTerm());
135
136         actorRef.underlyingActor().clear();
137
138     }
139
140     @Test
141     public void testIllegalCapture() throws Exception {
142         boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
143                 new MockRaftActorContext.MockPayload()), 9);
144
145         assertTrue(capture);
146
147         List<CaptureSnapshot> allMatching = MessageCollectorActor.getAllMatching(actorRef, CaptureSnapshot.class);
148
149         assertEquals(1, allMatching.size());
150
151         // This will not cause snapshot capture to start again
152         capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
153                 new MockRaftActorContext.MockPayload()), 9);
154
155         assertFalse(capture);
156
157         allMatching = MessageCollectorActor.getAllMatching(actorRef, CaptureSnapshot.class);
158
159         assertEquals(1, allMatching.size());
160     }
161
162     @Test
163     public void testPersistWhenReplicatedToAllIndexMinusOne(){
164         doReturn("123").when(mockRaftActorContext).getId();
165         doReturn(45L).when(mockReplicatedLog).getSnapshotIndex();
166         doReturn(6L).when(mockReplicatedLog).getSnapshotTerm();
167
168         // when replicatedToAllIndex = -1
169         snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
170                 new MockRaftActorContext.MockPayload()), -1);
171
172         snapshotManager.create(mockProcedure);
173
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());
177
178         ArgumentCaptor<Snapshot> snapshotArgumentCaptor = ArgumentCaptor.forClass(Snapshot.class);
179         verify(mockDataPersistenceProvider).saveSnapshot(snapshotArgumentCaptor.capture());
180
181         Snapshot snapshot = snapshotArgumentCaptor.getValue();
182
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());
190
191         verify(mockReplicatedLog).snapshotPreCommit(45L, 6L);
192     }
193
194
195     @Test
196     public void testCreate() throws Exception {
197         // when replicatedToAllIndex = -1
198         snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
199                 new MockRaftActorContext.MockPayload()), -1);
200
201         snapshotManager.create(mockProcedure);
202
203         verify(mockProcedure).apply(null);
204     }
205
206     @Test
207     public void testCallingCreateMultipleTimesCausesNoHarm() throws Exception {
208         // when replicatedToAllIndex = -1
209         snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
210                 new MockRaftActorContext.MockPayload()), -1);
211
212         snapshotManager.create(mockProcedure);
213
214         snapshotManager.create(mockProcedure);
215
216         verify(mockProcedure, times(1)).apply(null);
217     }
218
219     @Test
220     public void testCallingCreateBeforeCapture() throws Exception {
221         snapshotManager.create(mockProcedure);
222
223         verify(mockProcedure, times(0)).apply(null);
224     }
225
226     @Test
227     public void testCallingCreateAfterPersist() throws Exception {
228         // when replicatedToAllIndex = -1
229         snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
230                 new MockRaftActorContext.MockPayload()), -1);
231
232         snapshotManager.create(mockProcedure);
233
234         verify(mockProcedure, times(1)).apply(null);
235
236         snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
237                 , Runtime.getRuntime().totalMemory());
238
239         reset(mockProcedure);
240
241         snapshotManager.create(mockProcedure);
242
243         verify(mockProcedure, never()).apply(null);
244     }
245
246     @Test
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();
254
255         // when replicatedToAllIndex != -1
256         snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
257                 new MockRaftActorContext.MockPayload()), 9);
258
259         snapshotManager.create(mockProcedure);
260
261         snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
262                 , Runtime.getRuntime().totalMemory());
263
264         verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
265
266         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
267
268         verify(mockRaftActorBehavior).setReplicatedToAllIndex(9);
269     }
270
271
272     @Test
273     public void testPersistWhenReplicatedLogDataSizeGreaterThanThreshold(){
274         doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize();
275
276         // when replicatedToAllIndex = -1
277         snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
278                 new MockRaftActorContext.MockPayload()), -1);
279
280         snapshotManager.create(mockProcedure);
281
282         snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
283                 , Runtime.getRuntime().totalMemory());
284
285         verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
286
287         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
288     }
289
290     @Test
291     public void testPersistSendInstallSnapshot(){
292         doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize();
293
294         // when replicatedToAllIndex = -1
295         boolean capture = snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
296                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
297
298         assertTrue(capture);
299
300         snapshotManager.create(mockProcedure);
301
302         byte[] bytes = new byte[] {1,2,3,4,5,6,7,8,9,10};
303
304         snapshotManager.persist(mockDataPersistenceProvider, bytes, mockRaftActorBehavior
305                 , Runtime.getRuntime().totalMemory());
306
307         verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
308
309         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
310
311         ArgumentCaptor<SendInstallSnapshot> sendInstallSnapshotArgumentCaptor
312                 = ArgumentCaptor.forClass(SendInstallSnapshot.class);
313
314         verify(mockRaftActorBehavior).handleMessage(any(ActorRef.class), sendInstallSnapshotArgumentCaptor.capture());
315
316         SendInstallSnapshot sendInstallSnapshot = sendInstallSnapshotArgumentCaptor.getValue();
317
318         assertTrue(Arrays.equals(bytes, sendInstallSnapshot.getSnapshot().toByteArray()));
319     }
320
321     @Test
322     public void testCallingPersistWithoutCaptureWillDoNothing(){
323         snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
324                 , Runtime.getRuntime().totalMemory());
325
326         verify(mockDataPersistenceProvider, never()).saveSnapshot(any(Snapshot.class));
327
328         verify(mockReplicatedLog, never()).snapshotPreCommit(9L, 6L);
329
330         verify(mockRaftActorBehavior, never()).handleMessage(any(ActorRef.class), any(SendInstallSnapshot.class));
331     }
332     @Test
333     public void testCallingPersistTwiceWillDoNoHarm(){
334         doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize();
335
336         // when replicatedToAllIndex = -1
337         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
338                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
339
340         snapshotManager.create(mockProcedure);
341
342         snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
343                 , Runtime.getRuntime().totalMemory());
344
345         snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
346                 , Runtime.getRuntime().totalMemory());
347
348         verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
349
350         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
351
352         verify(mockRaftActorBehavior).handleMessage(any(ActorRef.class), any(SendInstallSnapshot.class));
353     }
354
355     @Test
356     public void testCommit(){
357         // when replicatedToAllIndex = -1
358         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
359                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
360
361         snapshotManager.create(mockProcedure);
362
363         snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
364                 , Runtime.getRuntime().totalMemory());
365
366         snapshotManager.commit(mockDataPersistenceProvider, 100L);
367
368         verify(mockReplicatedLog).snapshotCommit();
369
370         verify(mockDataPersistenceProvider).deleteMessages(100L);
371
372         ArgumentCaptor<SnapshotSelectionCriteria> criteriaCaptor = ArgumentCaptor.forClass(SnapshotSelectionCriteria.class);
373
374         verify(mockDataPersistenceProvider).deleteSnapshots(criteriaCaptor.capture());
375
376         assertEquals(90, criteriaCaptor.getValue().maxSequenceNr()); // sequenceNumber = 100
377                                                                      // config snapShotBatchCount = 10
378                                                                      // therefore maxSequenceNumber = 90
379     }
380
381     @Test
382     public void testCommitBeforePersist(){
383         // when replicatedToAllIndex = -1
384         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
385                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
386
387         snapshotManager.commit(mockDataPersistenceProvider, 100L);
388
389         verify(mockReplicatedLog, never()).snapshotCommit();
390
391         verify(mockDataPersistenceProvider, never()).deleteMessages(100L);
392
393         verify(mockDataPersistenceProvider, never()).deleteSnapshots(any(SnapshotSelectionCriteria.class));
394
395     }
396
397     @Test
398     public void testCommitBeforeCapture(){
399         snapshotManager.commit(mockDataPersistenceProvider, 100L);
400
401         verify(mockReplicatedLog, never()).snapshotCommit();
402
403         verify(mockDataPersistenceProvider, never()).deleteMessages(anyLong());
404
405         verify(mockDataPersistenceProvider, never()).deleteSnapshots(any(SnapshotSelectionCriteria.class));
406
407     }
408
409     @Test
410     public void testCallingCommitMultipleTimesCausesNoHarm(){
411         // when replicatedToAllIndex = -1
412         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
413                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
414
415         snapshotManager.create(mockProcedure);
416
417         snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
418                 , Runtime.getRuntime().totalMemory());
419
420         snapshotManager.commit(mockDataPersistenceProvider, 100L);
421
422         snapshotManager.commit(mockDataPersistenceProvider, 100L);
423
424         verify(mockReplicatedLog, times(1)).snapshotCommit();
425
426         verify(mockDataPersistenceProvider, times(1)).deleteMessages(100L);
427
428         verify(mockDataPersistenceProvider, times(1)).deleteSnapshots(any(SnapshotSelectionCriteria.class));
429     }
430
431     @Test
432     public void testRollback(){
433         // when replicatedToAllIndex = -1
434         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
435                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
436
437         snapshotManager.create(mockProcedure);
438
439         snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
440                 , Runtime.getRuntime().totalMemory());
441
442         snapshotManager.rollback();
443
444         verify(mockReplicatedLog).snapshotRollback();
445     }
446
447
448     @Test
449     public void testRollbackBeforePersist(){
450         // when replicatedToAllIndex = -1
451         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
452                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
453
454         snapshotManager.rollback();
455
456         verify(mockReplicatedLog, never()).snapshotRollback();
457     }
458
459     @Test
460     public void testRollbackBeforeCapture(){
461         snapshotManager.rollback();
462
463         verify(mockReplicatedLog, never()).snapshotRollback();
464     }
465
466     @Test
467     public void testCallingRollbackMultipleTimesCausesNoHarm(){
468         // when replicatedToAllIndex = -1
469         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
470                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
471
472         snapshotManager.create(mockProcedure);
473
474         snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior
475                 , Runtime.getRuntime().totalMemory());
476
477         snapshotManager.rollback();
478
479         snapshotManager.rollback();
480
481         verify(mockReplicatedLog, times(1)).snapshotRollback();
482     }
483
484     @Test
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();
494
495         snapshotManager.trimLog(10, mockRaftActorBehavior);
496
497         verify(mockReplicatedLog).snapshotPreCommit(10, 5);
498         verify(mockReplicatedLog).snapshotCommit();
499     }
500
501     @Test
502     public void testTrimLogAfterCapture(){
503         boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
504                 new MockRaftActorContext.MockPayload()), 9);
505
506         assertTrue(capture);
507
508         assertEquals(true, snapshotManager.isCapturing());
509
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();
518
519         snapshotManager.trimLog(10, mockRaftActorBehavior);
520
521         verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong());
522         verify(mockReplicatedLog, never()).snapshotCommit();
523
524     }
525
526     @Test
527     public void testTrimLogAfterCaptureToInstall(){
528         boolean capture = snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
529                 new MockRaftActorContext.MockPayload()), 9, "follower-1");
530
531         assertTrue(capture);
532
533         assertEquals(true, snapshotManager.isCapturing());
534
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();
543
544         snapshotManager.trimLog(10, mockRaftActorBehavior);
545
546         verify(mockReplicatedLog, never()).snapshotPreCommit(10, 5);
547         verify(mockReplicatedLog, never()).snapshotCommit();
548
549     }
550
551 }