2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3 * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.cluster.raft;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertNull;
14 import static org.junit.Assert.assertTrue;
15 import akka.japi.Procedure;
16 import java.util.HashMap;
17 import java.util.List;
19 import org.junit.Assert;
20 import org.junit.Before;
21 import org.junit.Test;
22 import org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload;
23 import org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockReplicatedLogEntry;
28 public class AbstractReplicatedLogImplTest {
30 private MockAbstractReplicatedLogImpl replicatedLogImpl;
34 replicatedLogImpl = new MockAbstractReplicatedLogImpl();
35 // create a set of initial entries in the in-memory log
36 replicatedLogImpl.append(new MockReplicatedLogEntry(1, 0, new MockPayload("A")));
37 replicatedLogImpl.append(new MockReplicatedLogEntry(1, 1, new MockPayload("B")));
38 replicatedLogImpl.append(new MockReplicatedLogEntry(1, 2, new MockPayload("C")));
39 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 3, new MockPayload("D")));
44 public void testEmptyLog() {
45 replicatedLogImpl = new MockAbstractReplicatedLogImpl();
47 assertEquals("size", 0, replicatedLogImpl.size());
48 assertEquals("dataSize", 0, replicatedLogImpl.dataSize());
49 assertEquals("getSnapshotIndex", -1, replicatedLogImpl.getSnapshotIndex());
50 assertEquals("getSnapshotTerm", -1, replicatedLogImpl.getSnapshotTerm());
51 assertEquals("lastIndex", -1, replicatedLogImpl.lastIndex());
52 assertEquals("lastTerm", -1, replicatedLogImpl.lastTerm());
53 assertEquals("isPresent", false, replicatedLogImpl.isPresent(0));
54 assertEquals("isInSnapshot", false, replicatedLogImpl.isInSnapshot(0));
55 Assert.assertNull("get(0)", replicatedLogImpl.get(0));
56 Assert.assertNull("last", replicatedLogImpl.last());
58 List<ReplicatedLogEntry> list = replicatedLogImpl.getFrom(0, 1);
59 assertEquals("getFrom size", 0, list.size());
61 assertEquals("removeFrom", -1, replicatedLogImpl.removeFrom(1));
63 replicatedLogImpl.setSnapshotIndex(2);
64 replicatedLogImpl.setSnapshotTerm(1);
66 assertEquals("getSnapshotIndex", 2, replicatedLogImpl.getSnapshotIndex());
67 assertEquals("getSnapshotTerm", 1, replicatedLogImpl.getSnapshotTerm());
68 assertEquals("lastIndex", 2, replicatedLogImpl.lastIndex());
69 assertEquals("lastTerm", 1, replicatedLogImpl.lastTerm());
73 public void testIndexOperations() {
75 // check if the values returned are correct, with snapshotIndex = -1
76 assertEquals("B", replicatedLogImpl.get(1).getData().toString());
77 assertEquals("D", replicatedLogImpl.last().getData().toString());
78 assertEquals(3, replicatedLogImpl.lastIndex());
79 assertEquals(2, replicatedLogImpl.lastTerm());
80 assertEquals(2, replicatedLogImpl.getFrom(2).size());
81 assertEquals(4, replicatedLogImpl.size());
82 assertTrue(replicatedLogImpl.isPresent(2));
83 assertFalse(replicatedLogImpl.isPresent(4));
84 assertFalse(replicatedLogImpl.isInSnapshot(2));
86 // now create a snapshot of 3 entries, with 1 unapplied entry left in the log
87 // It removes the entries which have made it to snapshot
88 // and updates the snapshot index and term
91 // check the values after the snapshot.
92 // each index value passed in the test is the logical index (log entry index)
93 // which gets mapped to the list's physical index
94 assertEquals("D", replicatedLogImpl.get(3).getData().toString());
95 assertEquals("D", replicatedLogImpl.last().getData().toString());
96 assertNull(replicatedLogImpl.get(1));
97 assertEquals(3, replicatedLogImpl.lastIndex());
98 assertEquals(2, replicatedLogImpl.lastTerm());
99 assertEquals(0, replicatedLogImpl.getFrom(2).size());
100 assertEquals(1, replicatedLogImpl.size());
101 assertFalse(replicatedLogImpl.isPresent(2));
102 assertTrue(replicatedLogImpl.isPresent(3));
103 assertFalse(replicatedLogImpl.isPresent(4));
104 assertTrue(replicatedLogImpl.isInSnapshot(2));
106 // append few more entries
107 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("E")));
108 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("F")));
109 replicatedLogImpl.append(new MockReplicatedLogEntry(3, 6, new MockPayload("G")));
110 replicatedLogImpl.append(new MockReplicatedLogEntry(3, 7, new MockPayload("H")));
112 // check their values as well
113 assertEquals(5, replicatedLogImpl.size());
114 assertEquals("D", replicatedLogImpl.get(3).getData().toString());
115 assertEquals("E", replicatedLogImpl.get(4).getData().toString());
116 assertEquals("H", replicatedLogImpl.last().getData().toString());
117 assertEquals(3, replicatedLogImpl.lastTerm());
118 assertEquals(7, replicatedLogImpl.lastIndex());
119 assertTrue(replicatedLogImpl.isPresent(7));
120 assertFalse(replicatedLogImpl.isInSnapshot(7));
121 assertEquals(1, replicatedLogImpl.getFrom(7).size());
122 assertEquals(2, replicatedLogImpl.getFrom(6).size());
124 // take a second snapshot with 5 entries with 0 unapplied entries left in the log
127 assertEquals(0, replicatedLogImpl.size());
128 assertNull(replicatedLogImpl.last());
129 assertNull(replicatedLogImpl.get(7));
130 assertNull(replicatedLogImpl.get(1));
131 assertFalse(replicatedLogImpl.isPresent(7));
132 assertTrue(replicatedLogImpl.isInSnapshot(7));
133 assertEquals(0, replicatedLogImpl.getFrom(7).size());
134 assertEquals(0, replicatedLogImpl.getFrom(6).size());
139 public void testGetFromWithMax(){
140 List<ReplicatedLogEntry> from = replicatedLogImpl.getFrom(0, 1);
141 Assert.assertEquals(1, from.size());
142 Assert.assertEquals(1, from.get(0).getTerm());
144 from = replicatedLogImpl.getFrom(0, 20);
145 Assert.assertEquals(4, from.size());
146 Assert.assertEquals(2, from.get(3).getTerm());
148 from = replicatedLogImpl.getFrom(1, 2);
149 Assert.assertEquals(2, from.size());
150 Assert.assertEquals(1, from.get(1).getTerm());
155 public void testSnapshotPreCommit() {
157 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("E")));
158 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("F")));
159 replicatedLogImpl.append(new MockReplicatedLogEntry(3, 6, new MockPayload("G")));
160 replicatedLogImpl.append(new MockReplicatedLogEntry(3, 7, new MockPayload("H")));
162 //sending negative values should not cause any changes
163 replicatedLogImpl.snapshotPreCommit(-1, -1);
164 assertEquals(8, replicatedLogImpl.size());
165 assertEquals(-1, replicatedLogImpl.getSnapshotIndex());
166 assertEquals(-1, replicatedLogImpl.getSnapshotTerm());
168 replicatedLogImpl.snapshotPreCommit(4, 2);
169 assertEquals(3, replicatedLogImpl.size());
170 assertEquals(4, replicatedLogImpl.getSnapshotIndex());
171 assertEquals(2, replicatedLogImpl.getSnapshotTerm());
173 replicatedLogImpl.snapshotPreCommit(6, 3);
174 assertEquals(1, replicatedLogImpl.size());
175 assertEquals(6, replicatedLogImpl.getSnapshotIndex());
176 assertEquals(3, replicatedLogImpl.getSnapshotTerm());
178 replicatedLogImpl.snapshotPreCommit(7, 3);
179 assertEquals(0, replicatedLogImpl.size());
180 assertEquals(7, replicatedLogImpl.getSnapshotIndex());
181 assertEquals(3, replicatedLogImpl.getSnapshotTerm());
183 //running it again on an empty list should not throw exception
184 replicatedLogImpl.snapshotPreCommit(7, 3);
185 assertEquals(0, replicatedLogImpl.size());
186 assertEquals(7, replicatedLogImpl.getSnapshotIndex());
187 assertEquals(3, replicatedLogImpl.getSnapshotTerm());
191 public void testSnapshotCommit() {
193 replicatedLogImpl.snapshotPreCommit(1, 1);
195 replicatedLogImpl.snapshotCommit();
197 assertEquals("size", 2, replicatedLogImpl.size());
198 assertEquals("dataSize", 2, replicatedLogImpl.dataSize());
199 assertEquals("getSnapshotIndex", 1, replicatedLogImpl.getSnapshotIndex());
200 assertEquals("getSnapshotTerm", 1, replicatedLogImpl.getSnapshotTerm());
201 assertEquals("lastIndex", 3, replicatedLogImpl.lastIndex());
202 assertEquals("lastTerm", 2, replicatedLogImpl.lastTerm());
204 Assert.assertNull("get(0)", replicatedLogImpl.get(0));
205 Assert.assertNull("get(1)", replicatedLogImpl.get(1));
206 Assert.assertNotNull("get(2)", replicatedLogImpl.get(2));
207 Assert.assertNotNull("get(3)", replicatedLogImpl.get(3));
211 public void testSnapshotRollback() {
213 replicatedLogImpl.snapshotPreCommit(1, 1);
215 assertEquals("size", 2, replicatedLogImpl.size());
216 assertEquals("getSnapshotIndex", 1, replicatedLogImpl.getSnapshotIndex());
217 assertEquals("getSnapshotTerm", 1, replicatedLogImpl.getSnapshotTerm());
219 replicatedLogImpl.snapshotRollback();
221 assertEquals("size", 4, replicatedLogImpl.size());
222 assertEquals("dataSize", 4, replicatedLogImpl.dataSize());
223 assertEquals("getSnapshotIndex", -1, replicatedLogImpl.getSnapshotIndex());
224 assertEquals("getSnapshotTerm", -1, replicatedLogImpl.getSnapshotTerm());
225 Assert.assertNotNull("get(0)", replicatedLogImpl.get(0));
226 Assert.assertNotNull("get(3)", replicatedLogImpl.get(3));
230 public void testIsPresent() {
231 assertTrue(replicatedLogImpl.isPresent(0));
232 assertTrue(replicatedLogImpl.isPresent(1));
233 assertTrue(replicatedLogImpl.isPresent(2));
234 assertTrue(replicatedLogImpl.isPresent(3));
236 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("D")));
237 replicatedLogImpl.snapshotPreCommit(3, 2); //snapshot on 3
238 replicatedLogImpl.snapshotCommit();
240 assertFalse(replicatedLogImpl.isPresent(0));
241 assertFalse(replicatedLogImpl.isPresent(1));
242 assertFalse(replicatedLogImpl.isPresent(2));
243 assertFalse(replicatedLogImpl.isPresent(3));
244 assertTrue(replicatedLogImpl.isPresent(4));
246 replicatedLogImpl.snapshotPreCommit(4, 2); //snapshot on 4
247 replicatedLogImpl.snapshotCommit();
248 assertFalse(replicatedLogImpl.isPresent(4));
250 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("D")));
251 assertTrue(replicatedLogImpl.isPresent(5));
255 public void testRemoveFrom() {
257 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("E", 2)));
258 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("F", 3)));
260 assertEquals("dataSize", 9, replicatedLogImpl.dataSize());
262 long adjusted = replicatedLogImpl.removeFrom(4);
263 assertEquals("removeFrom - adjusted", 4, adjusted);
264 assertEquals("size", 4, replicatedLogImpl.size());
265 assertEquals("dataSize", 4, replicatedLogImpl.dataSize());
269 adjusted = replicatedLogImpl.removeFrom(2);
270 assertEquals("removeFrom - adjusted", 1, adjusted);
271 assertEquals("size", 1, replicatedLogImpl.size());
272 assertEquals("dataSize", 1, replicatedLogImpl.dataSize());
274 assertEquals("removeFrom - adjusted", -1, replicatedLogImpl.removeFrom(0));
275 assertEquals("removeFrom - adjusted", -1, replicatedLogImpl.removeFrom(100));
278 // create a snapshot for test
279 public Map<Long, String> takeSnapshot(final int numEntries) {
280 Map<Long, String> map = new HashMap<>(numEntries);
284 for(int i = 0; i < numEntries; i++) {
285 ReplicatedLogEntry entry = replicatedLogImpl.getAtPhysicalIndex(i);
286 map.put(entry.getIndex(), entry.getData().toString());
287 lastIndex = entry.getIndex();
288 lastTerm = entry.getTerm();
291 replicatedLogImpl.snapshotPreCommit(lastIndex, lastTerm);
292 replicatedLogImpl.snapshotCommit();
297 class MockAbstractReplicatedLogImpl extends AbstractReplicatedLogImpl {
299 public void appendAndPersist(final ReplicatedLogEntry replicatedLogEntry) {
303 public void removeFromAndPersist(final long index) {
307 public void appendAndPersist(ReplicatedLogEntry replicatedLogEntry, Procedure<ReplicatedLogEntry> callback) {