X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-akka-raft%2Fsrc%2Ftest%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fcluster%2Fraft%2FSnapshotManagerTest.java;h=27596dff74f6a0b83ebab2775cf507d7515900fb;hb=24a5bafd22b83c4d838b7c3fc5225934fe969561;hp=90272fec980f46ed99f85d23d2cd24b859eddd66;hpb=9fe7a995204bcfed3ee6b644922b8fe440fe5f5c;p=controller.git diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/SnapshotManagerTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/SnapshotManagerTest.java index 90272fec98..27596dff74 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/SnapshotManagerTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/SnapshotManagerTest.java @@ -1,12 +1,24 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + package org.opendaylight.controller.cluster.raft; -import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyLong; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import akka.actor.ActorRef; @@ -14,18 +26,17 @@ import akka.japi.Procedure; import akka.persistence.SnapshotSelectionCriteria; import akka.testkit.TestActorRef; import java.util.Arrays; -import java.util.HashMap; -import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.opendaylight.controller.cluster.DataPersistenceProvider; +import org.opendaylight.controller.cluster.raft.SnapshotManager.LastAppliedTermInformationReader; import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot; import org.opendaylight.controller.cluster.raft.base.messages.SendInstallSnapshot; +import org.opendaylight.controller.cluster.raft.base.messages.SnapshotComplete; import org.opendaylight.controller.cluster.raft.behaviors.RaftActorBehavior; import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor; import org.slf4j.LoggerFactory; @@ -50,6 +61,9 @@ public class SnapshotManagerTest extends AbstractActorTest { @Mock private Procedure mockProcedure; + @Mock + private ElectionTerm mockElectionTerm; + private SnapshotManager snapshotManager; private TestActorFactory factory; @@ -60,19 +74,26 @@ public class SnapshotManagerTest extends AbstractActorTest { public void setUp(){ MockitoAnnotations.initMocks(this); - doReturn(new HashMap<>()).when(mockRaftActorContext).getPeerAddresses(); + doReturn(false).when(mockRaftActorContext).hasFollowers(); doReturn(mockConfigParams).when(mockRaftActorContext).getConfigParams(); doReturn(10L).when(mockConfigParams).getSnapshotBatchCount(); + doReturn(70).when(mockConfigParams).getSnapshotDataThresholdPercentage(); doReturn(mockReplicatedLog).when(mockRaftActorContext).getReplicatedLog(); doReturn("123").when(mockRaftActorContext).getId(); + doReturn(mockDataPersistenceProvider).when(mockRaftActorContext).getPersistenceProvider(); doReturn("123").when(mockRaftActorBehavior).getLeaderId(); + doReturn(mockElectionTerm).when(mockRaftActorContext).getTermInformation(); + doReturn(5L).when(mockElectionTerm).getCurrentTerm(); + doReturn("member5").when(mockElectionTerm).getVotedFor(); + snapshotManager = new SnapshotManager(mockRaftActorContext, LoggerFactory.getLogger(this.getClass())); factory = new TestActorFactory(getSystem()); actorRef = factory.createTestActor(MessageCollectorActor.props(), factory.generateActorId("test-")); doReturn(actorRef).when(mockRaftActorContext).getActor(); + snapshotManager.setCreateSnapshotCallable(mockProcedure); } @After @@ -86,15 +107,17 @@ public class SnapshotManagerTest extends AbstractActorTest { } @Test - public void testCaptureToInstall(){ + public void testCaptureToInstall() throws Exception { // Force capturing toInstall = true snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(1, 0, - new MockRaftActorContext.MockPayload()), 0); + new MockRaftActorContext.MockPayload()), 0, "follower-1"); assertEquals(true, snapshotManager.isCapturing()); - CaptureSnapshot captureSnapshot = MessageCollectorActor.expectFirstMatching(actorRef, CaptureSnapshot.class); + verify(mockProcedure).apply(null); + + CaptureSnapshot captureSnapshot = snapshotManager.getCaptureSnapshot(); // LastIndex and LastTerm are picked up from the lastLogEntry assertEquals(0L, captureSnapshot.getLastIndex()); @@ -111,13 +134,18 @@ public class SnapshotManagerTest extends AbstractActorTest { } @Test - public void testCapture(){ - snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9, + public void testCapture() throws Exception { + boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9, new MockRaftActorContext.MockPayload()), 9); + assertTrue(capture); + assertEquals(true, snapshotManager.isCapturing()); - CaptureSnapshot captureSnapshot = MessageCollectorActor.expectFirstMatching(actorRef, CaptureSnapshot.class); + verify(mockProcedure).apply(null); + + CaptureSnapshot captureSnapshot = snapshotManager.getCaptureSnapshot(); + // LastIndex and LastTerm are picked up from the lastLogEntry assertEquals(9L, captureSnapshot.getLastIndex()); assertEquals(1L, captureSnapshot.getLastTerm()); @@ -135,103 +163,107 @@ public class SnapshotManagerTest extends AbstractActorTest { } @Test - public void testIllegalCapture() throws Exception { - snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9, - new MockRaftActorContext.MockPayload()), 9); + public void testCaptureWithNullLastLogEntry() throws Exception { + boolean capture = snapshotManager.capture(null, 1); - List allMatching = MessageCollectorActor.getAllMatching(actorRef, CaptureSnapshot.class); + assertTrue(capture); - assertEquals(1, allMatching.size()); + assertEquals(true, snapshotManager.isCapturing()); - // This will not cause snapshot capture to start again - snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9, - new MockRaftActorContext.MockPayload()), 9); + verify(mockProcedure).apply(null); - allMatching = MessageCollectorActor.getAllMatching(actorRef, CaptureSnapshot.class); + CaptureSnapshot captureSnapshot = snapshotManager.getCaptureSnapshot(); - assertEquals(1, allMatching.size()); - } + System.out.println(captureSnapshot); - @Test - public void testPersistWhenReplicatedToAllIndexMinusOne(){ - doReturn("123").when(mockRaftActorContext).getId(); - doReturn(45L).when(mockReplicatedLog).getSnapshotIndex(); - doReturn(6L).when(mockReplicatedLog).getSnapshotTerm(); + // LastIndex and LastTerm are picked up from the lastLogEntry + assertEquals(-1L, captureSnapshot.getLastIndex()); + assertEquals(-1L, captureSnapshot.getLastTerm()); - // when replicatedToAllIndex = -1 - snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9, - new MockRaftActorContext.MockPayload()), -1); + // Since the actor does not have any followers (no peer addresses) lastApplied will be from lastLogEntry + assertEquals(-1L, captureSnapshot.getLastAppliedIndex()); + assertEquals(-1L, captureSnapshot.getLastAppliedTerm()); - snapshotManager.create(mockProcedure); + // + assertEquals(-1L, captureSnapshot.getReplicatedToAllIndex()); + assertEquals(-1L, captureSnapshot.getReplicatedToAllTerm()); + actorRef.underlyingActor().clear(); - byte[] bytes = new byte[] {1,2,3,4,5,6,7,8,9,10}; - snapshotManager.persist(mockDataPersistenceProvider, bytes, mockRaftActorBehavior); + } - ArgumentCaptor snapshotArgumentCaptor = ArgumentCaptor.forClass(Snapshot.class); - verify(mockDataPersistenceProvider).saveSnapshot(snapshotArgumentCaptor.capture()); + @Test + public void testCaptureWithCreateProcedureError () throws Exception { + doThrow(new Exception("mock")).when(mockProcedure).apply(null); - Snapshot snapshot = snapshotArgumentCaptor.getValue(); + boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9, + new MockRaftActorContext.MockPayload()), 9); - assertEquals(6, snapshot.getLastAppliedTerm()); - assertEquals(9, snapshot.getLastAppliedIndex()); - assertEquals(9, snapshot.getLastIndex()); - assertEquals(6, snapshot.getLastTerm()); - assertEquals(10, snapshot.getState().length); - assertTrue(Arrays.equals(bytes, snapshot.getState())); - assertEquals(0, snapshot.getUnAppliedEntries().size()); + assertFalse(capture); - verify(mockReplicatedLog).snapshotPreCommit(45L, 6L); - } + assertEquals(false, snapshotManager.isCapturing()); + verify(mockProcedure).apply(null); + } @Test - public void testCreate() throws Exception { - // when replicatedToAllIndex = -1 - snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9, - new MockRaftActorContext.MockPayload()), -1); + public void testIllegalCapture() throws Exception { + boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9, + new MockRaftActorContext.MockPayload()), 9); - snapshotManager.create(mockProcedure); + assertTrue(capture); verify(mockProcedure).apply(null); - } - @Test - public void testCallingCreateMultipleTimesCausesNoHarm() throws Exception { - // when replicatedToAllIndex = -1 - snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9, - new MockRaftActorContext.MockPayload()), -1); + reset(mockProcedure); - snapshotManager.create(mockProcedure); + // This will not cause snapshot capture to start again + capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9, + new MockRaftActorContext.MockPayload()), 9); - snapshotManager.create(mockProcedure); + assertFalse(capture); - verify(mockProcedure, times(1)).apply(null); + verify(mockProcedure, never()).apply(null); } @Test - public void testCallingCreateBeforeCapture() throws Exception { - snapshotManager.create(mockProcedure); + public void testPersistWhenReplicatedToAllIndexMinusOne(){ + doReturn(7L).when(mockReplicatedLog).getSnapshotIndex(); + doReturn(1L).when(mockReplicatedLog).getSnapshotTerm(); - verify(mockProcedure, times(0)).apply(null); - } + doReturn(true).when(mockRaftActorContext).hasFollowers(); - @Test - public void testCallingCreateAfterPersist() throws Exception { - // when replicatedToAllIndex = -1 - snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9, - new MockRaftActorContext.MockPayload()), -1); + doReturn(8L).when(mockRaftActorContext).getLastApplied(); - snapshotManager.create(mockProcedure); + MockRaftActorContext.MockReplicatedLogEntry lastLogEntry = new MockRaftActorContext.MockReplicatedLogEntry( + 3L, 9L, new MockRaftActorContext.MockPayload()); - verify(mockProcedure, times(1)).apply(null); + MockRaftActorContext.MockReplicatedLogEntry lastAppliedEntry = new MockRaftActorContext.MockReplicatedLogEntry( + 2L, 8L, new MockRaftActorContext.MockPayload()); - snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior); + doReturn(lastAppliedEntry).when(mockReplicatedLog).get(8L); + doReturn(Arrays.asList(lastLogEntry)).when(mockReplicatedLog).getFrom(9L); - Mockito.reset(mockProcedure); + // when replicatedToAllIndex = -1 + snapshotManager.capture(lastLogEntry, -1); - snapshotManager.create(mockProcedure); + byte[] bytes = new byte[] {1,2,3,4,5,6,7,8,9,10}; + snapshotManager.persist(bytes, mockRaftActorBehavior, Runtime.getRuntime().totalMemory()); - verify(mockProcedure, never()).apply(null); + ArgumentCaptor snapshotArgumentCaptor = ArgumentCaptor.forClass(Snapshot.class); + verify(mockDataPersistenceProvider).saveSnapshot(snapshotArgumentCaptor.capture()); + + Snapshot snapshot = snapshotArgumentCaptor.getValue(); + + assertEquals("getLastTerm", 3L, snapshot.getLastTerm()); + assertEquals("getLastIndex", 9L, snapshot.getLastIndex()); + assertEquals("getLastAppliedTerm", 2L, snapshot.getLastAppliedTerm()); + assertEquals("getLastAppliedIndex", 8L, snapshot.getLastAppliedIndex()); + assertArrayEquals("getState", bytes, snapshot.getState()); + assertEquals("getUnAppliedEntries", Arrays.asList(lastLogEntry), snapshot.getUnAppliedEntries()); + assertEquals("electionTerm", mockElectionTerm.getCurrentTerm(), snapshot.getElectionTerm()); + assertEquals("electionVotedFor", mockElectionTerm.getVotedFor(), snapshot.getElectionVotedFor()); + + verify(mockReplicatedLog).snapshotPreCommit(7L, 1L); } @Test @@ -247,18 +279,26 @@ public class SnapshotManagerTest extends AbstractActorTest { snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9, new MockRaftActorContext.MockPayload()), 9); - snapshotManager.create(mockProcedure); + byte[] bytes = new byte[] {1,2,3,4,5,6,7,8,9,10}; + snapshotManager.persist(bytes, mockRaftActorBehavior, Runtime.getRuntime().totalMemory()); - snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior); + ArgumentCaptor snapshotArgumentCaptor = ArgumentCaptor.forClass(Snapshot.class); + verify(mockDataPersistenceProvider).saveSnapshot(snapshotArgumentCaptor.capture()); - verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class)); + Snapshot snapshot = snapshotArgumentCaptor.getValue(); + + assertEquals("getLastTerm", 6L, snapshot.getLastTerm()); + assertEquals("getLastIndex", 9L, snapshot.getLastIndex()); + assertEquals("getLastAppliedTerm", 6L, snapshot.getLastAppliedTerm()); + assertEquals("getLastAppliedIndex", 9L, snapshot.getLastAppliedIndex()); + assertArrayEquals("getState", bytes, snapshot.getState()); + assertEquals("getUnAppliedEntries size", 0, snapshot.getUnAppliedEntries().size()); verify(mockReplicatedLog).snapshotPreCommit(9L, 6L); verify(mockRaftActorBehavior).setReplicatedToAllIndex(9); } - @Test public void testPersistWhenReplicatedLogDataSizeGreaterThanThreshold(){ doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize(); @@ -267,15 +307,39 @@ public class SnapshotManagerTest extends AbstractActorTest { snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9, new MockRaftActorContext.MockPayload()), -1); - snapshotManager.create(mockProcedure); + snapshotManager.persist(new byte[]{}, mockRaftActorBehavior, Runtime.getRuntime().totalMemory()); + + verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class)); + + verify(mockReplicatedLog).snapshotPreCommit(9L, 6L); + + verify(mockRaftActorBehavior, never()).setReplicatedToAllIndex(anyLong()); + } + + @Test + public void testPersistWhenReplicatedLogSizeExceedsSnapshotBatchCount() { + doReturn(10L).when(mockReplicatedLog).size(); // matches snapshotBatchCount + doReturn(100).when(mockReplicatedLog).dataSize(); + + doReturn(5L).when(mockReplicatedLog).getSnapshotIndex(); + doReturn(5L).when(mockReplicatedLog).getSnapshotTerm(); + + long replicatedToAllIndex = 1; + ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class); + doReturn(replicatedLogEntry).when(mockReplicatedLog).get(replicatedToAllIndex); + doReturn(6L).when(replicatedLogEntry).getTerm(); + doReturn(replicatedToAllIndex).when(replicatedLogEntry).getIndex(); + + snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6, 9, + new MockRaftActorContext.MockPayload()), replicatedToAllIndex); - snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior); + snapshotManager.persist(new byte[]{}, mockRaftActorBehavior, 2000000L); verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class)); verify(mockReplicatedLog).snapshotPreCommit(9L, 6L); - verify(mockRaftActorBehavior).setReplicatedToAllIndex(-1); + verify(mockRaftActorBehavior).setReplicatedToAllIndex(replicatedToAllIndex); } @Test @@ -283,14 +347,16 @@ public class SnapshotManagerTest extends AbstractActorTest { doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize(); // when replicatedToAllIndex = -1 - snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9, - new MockRaftActorContext.MockPayload()), -1); + boolean capture = snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9, + new MockRaftActorContext.MockPayload()), -1, "follower-1"); - snapshotManager.create(mockProcedure); + assertTrue(capture); byte[] bytes = new byte[] {1,2,3,4,5,6,7,8,9,10}; - snapshotManager.persist(mockDataPersistenceProvider, bytes, mockRaftActorBehavior); + snapshotManager.persist(bytes, mockRaftActorBehavior, Runtime.getRuntime().totalMemory()); + + assertEquals(true, snapshotManager.isCapturing()); verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class)); @@ -303,12 +369,12 @@ public class SnapshotManagerTest extends AbstractActorTest { SendInstallSnapshot sendInstallSnapshot = sendInstallSnapshotArgumentCaptor.getValue(); - assertTrue(Arrays.equals(bytes, sendInstallSnapshot.getSnapshot().toByteArray())); + assertTrue(Arrays.equals(bytes, sendInstallSnapshot.getSnapshot().getState())); } @Test public void testCallingPersistWithoutCaptureWillDoNothing(){ - snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior); + snapshotManager.persist(new byte[]{}, mockRaftActorBehavior, Runtime.getRuntime().totalMemory()); verify(mockDataPersistenceProvider, never()).saveSnapshot(any(Snapshot.class)); @@ -322,13 +388,11 @@ public class SnapshotManagerTest extends AbstractActorTest { // when replicatedToAllIndex = -1 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9, - new MockRaftActorContext.MockPayload()), -1); - - snapshotManager.create(mockProcedure); + new MockRaftActorContext.MockPayload()), -1, "follower-1"); - snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior); + snapshotManager.persist(new byte[]{}, mockRaftActorBehavior, Runtime.getRuntime().totalMemory()); - snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior); + snapshotManager.persist(new byte[]{}, mockRaftActorBehavior, Runtime.getRuntime().totalMemory()); verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class)); @@ -339,19 +403,23 @@ public class SnapshotManagerTest extends AbstractActorTest { @Test public void testCommit(){ + doReturn(50L).when(mockDataPersistenceProvider).getLastSequenceNumber(); + // when replicatedToAllIndex = -1 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9, - new MockRaftActorContext.MockPayload()), -1); + new MockRaftActorContext.MockPayload()), -1, "follower-1"); - snapshotManager.create(mockProcedure); + snapshotManager.persist(new byte[]{}, mockRaftActorBehavior, Runtime.getRuntime().totalMemory()); - snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior); + assertEquals(true, snapshotManager.isCapturing()); - snapshotManager.commit(mockDataPersistenceProvider, 100L); + snapshotManager.commit(100L, mockRaftActorBehavior); + + assertEquals(false, snapshotManager.isCapturing()); verify(mockReplicatedLog).snapshotCommit(); - verify(mockDataPersistenceProvider).deleteMessages(100L); + verify(mockDataPersistenceProvider).deleteMessages(50L); ArgumentCaptor criteriaCaptor = ArgumentCaptor.forClass(SnapshotSelectionCriteria.class); @@ -360,15 +428,17 @@ public class SnapshotManagerTest extends AbstractActorTest { assertEquals(90, criteriaCaptor.getValue().maxSequenceNr()); // sequenceNumber = 100 // config snapShotBatchCount = 10 // therefore maxSequenceNumber = 90 + + MessageCollectorActor.expectFirstMatching(actorRef, SnapshotComplete.class); } @Test public void testCommitBeforePersist(){ // when replicatedToAllIndex = -1 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9, - new MockRaftActorContext.MockPayload()), -1); + new MockRaftActorContext.MockPayload()), -1, "follower-1"); - snapshotManager.commit(mockDataPersistenceProvider, 100L); + snapshotManager.commit(100L, mockRaftActorBehavior); verify(mockReplicatedLog, never()).snapshotCommit(); @@ -380,7 +450,7 @@ public class SnapshotManagerTest extends AbstractActorTest { @Test public void testCommitBeforeCapture(){ - snapshotManager.commit(mockDataPersistenceProvider, 100L); + snapshotManager.commit(100L, mockRaftActorBehavior); verify(mockReplicatedLog, never()).snapshotCommit(); @@ -392,21 +462,21 @@ public class SnapshotManagerTest extends AbstractActorTest { @Test public void testCallingCommitMultipleTimesCausesNoHarm(){ + doReturn(50L).when(mockDataPersistenceProvider).getLastSequenceNumber(); + // when replicatedToAllIndex = -1 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9, - new MockRaftActorContext.MockPayload()), -1); + new MockRaftActorContext.MockPayload()), -1, "follower-1"); - snapshotManager.create(mockProcedure); + snapshotManager.persist(new byte[]{}, mockRaftActorBehavior, Runtime.getRuntime().totalMemory()); - snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior); + snapshotManager.commit(100L, mockRaftActorBehavior); - snapshotManager.commit(mockDataPersistenceProvider, 100L); - - snapshotManager.commit(mockDataPersistenceProvider, 100L); + snapshotManager.commit(100L, mockRaftActorBehavior); verify(mockReplicatedLog, times(1)).snapshotCommit(); - verify(mockDataPersistenceProvider, times(1)).deleteMessages(100L); + verify(mockDataPersistenceProvider, times(1)).deleteMessages(50L); verify(mockDataPersistenceProvider, times(1)).deleteSnapshots(any(SnapshotSelectionCriteria.class)); } @@ -415,15 +485,15 @@ public class SnapshotManagerTest extends AbstractActorTest { public void testRollback(){ // when replicatedToAllIndex = -1 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9, - new MockRaftActorContext.MockPayload()), -1); - - snapshotManager.create(mockProcedure); + new MockRaftActorContext.MockPayload()), -1, "follower-1"); - snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior); + snapshotManager.persist(new byte[]{}, mockRaftActorBehavior, Runtime.getRuntime().totalMemory()); snapshotManager.rollback(); verify(mockReplicatedLog).snapshotRollback(); + + MessageCollectorActor.expectFirstMatching(actorRef, SnapshotComplete.class); } @@ -431,7 +501,7 @@ public class SnapshotManagerTest extends AbstractActorTest { public void testRollbackBeforePersist(){ // when replicatedToAllIndex = -1 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9, - new MockRaftActorContext.MockPayload()), -1); + new MockRaftActorContext.MockPayload()), -1, "follower-1"); snapshotManager.rollback(); @@ -449,11 +519,9 @@ public class SnapshotManagerTest extends AbstractActorTest { public void testCallingRollbackMultipleTimesCausesNoHarm(){ // when replicatedToAllIndex = -1 snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9, - new MockRaftActorContext.MockPayload()), -1); - - snapshotManager.create(mockProcedure); + new MockRaftActorContext.MockPayload()), -1, "follower-1"); - snapshotManager.persist(mockDataPersistenceProvider, new byte[]{}, mockRaftActorBehavior); + snapshotManager.persist(new byte[]{}, mockRaftActorBehavior, Runtime.getRuntime().totalMemory()); snapshotManager.rollback(); @@ -463,39 +531,91 @@ public class SnapshotManagerTest extends AbstractActorTest { } @Test - public void testTrimLog(){ - ElectionTerm mockElectionTerm = mock(ElectionTerm.class); - ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class); + public void testTrimLogWhenTrimIndexLessThanLastApplied() { doReturn(20L).when(mockRaftActorContext).getLastApplied(); + + ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class); doReturn(true).when(mockReplicatedLog).isPresent(10); - doReturn(mockElectionTerm).when(mockRaftActorContext).getTermInformation(); - doReturn(5L).when(mockElectionTerm).getCurrentTerm(); doReturn(replicatedLogEntry).when((mockReplicatedLog)).get(10); doReturn(5L).when(replicatedLogEntry).getTerm(); - snapshotManager.trimLog(10); + long retIndex = snapshotManager.trimLog(10, mockRaftActorBehavior); + assertEquals("return index", 10L, retIndex); verify(mockReplicatedLog).snapshotPreCommit(10, 5); verify(mockReplicatedLog).snapshotCommit(); + + verify(mockRaftActorBehavior, never()).setReplicatedToAllIndex(anyLong()); + } + + @Test + public void testTrimLogWhenLastAppliedNotSet() { + doReturn(-1L).when(mockRaftActorContext).getLastApplied(); + + ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class); + doReturn(true).when(mockReplicatedLog).isPresent(10); + doReturn(replicatedLogEntry).when((mockReplicatedLog)).get(10); + doReturn(5L).when(replicatedLogEntry).getTerm(); + + long retIndex = snapshotManager.trimLog(10, mockRaftActorBehavior); + assertEquals("return index", -1L, retIndex); + + verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong()); + verify(mockReplicatedLog, never()).snapshotCommit(); + + verify(mockRaftActorBehavior, never()).setReplicatedToAllIndex(anyLong()); + } + + @Test + public void testTrimLogWhenLastAppliedZero() { + doReturn(0L).when(mockRaftActorContext).getLastApplied(); + + ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class); + doReturn(true).when(mockReplicatedLog).isPresent(10); + doReturn(replicatedLogEntry).when((mockReplicatedLog)).get(10); + doReturn(5L).when(replicatedLogEntry).getTerm(); + + long retIndex = snapshotManager.trimLog(10, mockRaftActorBehavior); + assertEquals("return index", -1L, retIndex); + + verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong()); + verify(mockReplicatedLog, never()).snapshotCommit(); + + verify(mockRaftActorBehavior, never()).setReplicatedToAllIndex(anyLong()); + } + + @Test + public void testTrimLogWhenTrimIndexNotPresent() { + doReturn(20L).when(mockRaftActorContext).getLastApplied(); + + doReturn(false).when(mockReplicatedLog).isPresent(10); + + long retIndex = snapshotManager.trimLog(10, mockRaftActorBehavior); + assertEquals("return index", -1L, retIndex); + + verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong()); + verify(mockReplicatedLog, never()).snapshotCommit(); + + // Trim index is greater than replicatedToAllIndex so should update it. + verify(mockRaftActorBehavior).setReplicatedToAllIndex(10L); } @Test public void testTrimLogAfterCapture(){ - snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9, + boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9, new MockRaftActorContext.MockPayload()), 9); + assertTrue(capture); + assertEquals(true, snapshotManager.isCapturing()); - ElectionTerm mockElectionTerm = mock(ElectionTerm.class); ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class); doReturn(20L).when(mockRaftActorContext).getLastApplied(); doReturn(true).when(mockReplicatedLog).isPresent(10); - doReturn(mockElectionTerm).when(mockRaftActorContext).getTermInformation(); - doReturn(5L).when(mockElectionTerm).getCurrentTerm(); doReturn(replicatedLogEntry).when((mockReplicatedLog)).get(10); doReturn(5L).when(replicatedLogEntry).getTerm(); - snapshotManager.trimLog(10); + snapshotManager.trimLog(10, mockRaftActorBehavior); verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong()); verify(mockReplicatedLog, never()).snapshotCommit(); @@ -504,25 +624,68 @@ public class SnapshotManagerTest extends AbstractActorTest { @Test public void testTrimLogAfterCaptureToInstall(){ - snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(1,9, - new MockRaftActorContext.MockPayload()), 9); + boolean capture = snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(1,9, + new MockRaftActorContext.MockPayload()), 9, "follower-1"); + + assertTrue(capture); assertEquals(true, snapshotManager.isCapturing()); - ElectionTerm mockElectionTerm = mock(ElectionTerm.class); ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class); doReturn(20L).when(mockRaftActorContext).getLastApplied(); doReturn(true).when(mockReplicatedLog).isPresent(10); - doReturn(mockElectionTerm).when(mockRaftActorContext).getTermInformation(); - doReturn(5L).when(mockElectionTerm).getCurrentTerm(); doReturn(replicatedLogEntry).when((mockReplicatedLog)).get(10); doReturn(5L).when(replicatedLogEntry).getTerm(); - snapshotManager.trimLog(10); + snapshotManager.trimLog(10, mockRaftActorBehavior); verify(mockReplicatedLog, never()).snapshotPreCommit(10, 5); verify(mockReplicatedLog, never()).snapshotCommit(); } -} \ No newline at end of file + @Test + public void testLastAppliedTermInformationReader() { + + LastAppliedTermInformationReader reader = new LastAppliedTermInformationReader(); + + doReturn(4L).when(mockReplicatedLog).getSnapshotTerm(); + doReturn(7L).when(mockReplicatedLog).getSnapshotIndex(); + + ReplicatedLogEntry lastLogEntry = new MockRaftActorContext.MockReplicatedLogEntry(6L, 9L, + new MockRaftActorContext.MockPayload()); + + // No followers and valid lastLogEntry + reader.init(mockReplicatedLog, 1L, lastLogEntry, false); + + assertEquals("getTerm", 6L, reader.getTerm()); + assertEquals("getIndex", 9L, reader.getIndex()); + + // No followers and null lastLogEntry + reader.init(mockReplicatedLog, 1L, null, false); + + assertEquals("getTerm", -1L, reader.getTerm()); + assertEquals("getIndex", -1L, reader.getIndex()); + + // Followers and valid originalIndex entry + doReturn(new MockRaftActorContext.MockReplicatedLogEntry(5L, 8L, + new MockRaftActorContext.MockPayload())).when(mockReplicatedLog).get(8L); + reader.init(mockReplicatedLog, 8L, lastLogEntry, true); + + assertEquals("getTerm", 5L, reader.getTerm()); + assertEquals("getIndex", 8L, reader.getIndex()); + + // Followers and null originalIndex entry and valid snapshot index + reader.init(mockReplicatedLog, 7L, lastLogEntry, true); + + assertEquals("getTerm", 4L, reader.getTerm()); + assertEquals("getIndex", 7L, reader.getIndex()); + + // Followers and null originalIndex entry and invalid snapshot index + doReturn(-1L).when(mockReplicatedLog).getSnapshotIndex(); + reader.init(mockReplicatedLog, 7L, lastLogEntry, true); + + assertEquals("getTerm", -1L, reader.getTerm()); + assertEquals("getIndex", -1L, reader.getIndex()); + } +}