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.assertNotNull;
14 import static org.junit.Assert.assertNull;
15 import static org.junit.Assert.assertTrue;
17 import java.util.HashMap;
18 import java.util.List;
20 import java.util.function.Consumer;
21 import org.junit.Before;
22 import org.junit.Test;
23 import org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload;
24 import org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry;
27 * Unit tests for AbstractReplicatedLogImplTest.
29 public class AbstractReplicatedLogImplTest {
31 private MockAbstractReplicatedLogImpl replicatedLogImpl;
35 replicatedLogImpl = new MockAbstractReplicatedLogImpl();
36 // create a set of initial entries in the in-memory log
37 replicatedLogImpl.append(new SimpleReplicatedLogEntry(0, 1, new MockPayload("A")));
38 replicatedLogImpl.append(new SimpleReplicatedLogEntry(1, 1, new MockPayload("B")));
39 replicatedLogImpl.append(new SimpleReplicatedLogEntry(2, 1, new MockPayload("C")));
40 replicatedLogImpl.append(new SimpleReplicatedLogEntry(3, 2, new MockPayload("D")));
45 public void testEmptyLog() {
46 replicatedLogImpl = new MockAbstractReplicatedLogImpl();
48 assertEquals("size", 0, replicatedLogImpl.size());
49 assertEquals("dataSize", 0, replicatedLogImpl.dataSize());
50 assertEquals("getSnapshotIndex", -1, replicatedLogImpl.getSnapshotIndex());
51 assertEquals("getSnapshotTerm", -1, replicatedLogImpl.getSnapshotTerm());
52 assertEquals("lastIndex", -1, replicatedLogImpl.lastIndex());
53 assertEquals("lastTerm", -1, replicatedLogImpl.lastTerm());
54 assertEquals("isPresent", false, replicatedLogImpl.isPresent(0));
55 assertEquals("isInSnapshot", false, replicatedLogImpl.isInSnapshot(0));
56 assertNull("get(0)", replicatedLogImpl.get(0));
57 assertNull("last", replicatedLogImpl.last());
59 List<ReplicatedLogEntry> list = replicatedLogImpl.getFrom(0, 1, ReplicatedLog.NO_MAX_SIZE);
60 assertEquals("getFrom size", 0, list.size());
62 assertEquals("removeFrom", -1, replicatedLogImpl.removeFrom(1));
64 replicatedLogImpl.setSnapshotIndex(2);
65 replicatedLogImpl.setSnapshotTerm(1);
67 assertEquals("getSnapshotIndex", 2, replicatedLogImpl.getSnapshotIndex());
68 assertEquals("getSnapshotTerm", 1, replicatedLogImpl.getSnapshotTerm());
69 assertEquals("lastIndex", 2, replicatedLogImpl.lastIndex());
70 assertEquals("lastTerm", 1, replicatedLogImpl.lastTerm());
74 public void testIndexOperations() {
76 // check if the values returned are correct, with snapshotIndex = -1
77 assertEquals("B", replicatedLogImpl.get(1).getData().toString());
78 assertEquals("D", replicatedLogImpl.last().getData().toString());
79 assertEquals(3, replicatedLogImpl.lastIndex());
80 assertEquals(2, replicatedLogImpl.lastTerm());
81 assertEquals(2, replicatedLogImpl.getFrom(2).size());
82 assertEquals(4, replicatedLogImpl.size());
83 assertTrue(replicatedLogImpl.isPresent(2));
84 assertFalse(replicatedLogImpl.isPresent(4));
85 assertFalse(replicatedLogImpl.isInSnapshot(2));
87 // now create a snapshot of 3 entries, with 1 unapplied entry left in the log
88 // It removes the entries which have made it to snapshot
89 // and updates the snapshot index and term
92 // check the values after the snapshot.
93 // each index value passed in the test is the logical index (log entry index)
94 // which gets mapped to the list's physical index
95 assertEquals("D", replicatedLogImpl.get(3).getData().toString());
96 assertEquals("D", replicatedLogImpl.last().getData().toString());
97 assertNull(replicatedLogImpl.get(1));
98 assertEquals(3, replicatedLogImpl.lastIndex());
99 assertEquals(2, replicatedLogImpl.lastTerm());
100 assertEquals(0, replicatedLogImpl.getFrom(2).size());
101 assertEquals(1, replicatedLogImpl.size());
102 assertFalse(replicatedLogImpl.isPresent(2));
103 assertTrue(replicatedLogImpl.isPresent(3));
104 assertFalse(replicatedLogImpl.isPresent(4));
105 assertTrue(replicatedLogImpl.isInSnapshot(2));
107 // append few more entries
108 replicatedLogImpl.append(new SimpleReplicatedLogEntry(4, 2, new MockPayload("E")));
109 replicatedLogImpl.append(new SimpleReplicatedLogEntry(5, 2, new MockPayload("F")));
110 replicatedLogImpl.append(new SimpleReplicatedLogEntry(6, 3, new MockPayload("G")));
111 replicatedLogImpl.append(new SimpleReplicatedLogEntry(7, 3, new MockPayload("H")));
113 // check their values as well
114 assertEquals(5, replicatedLogImpl.size());
115 assertEquals("D", replicatedLogImpl.get(3).getData().toString());
116 assertEquals("E", replicatedLogImpl.get(4).getData().toString());
117 assertEquals("H", replicatedLogImpl.last().getData().toString());
118 assertEquals(3, replicatedLogImpl.lastTerm());
119 assertEquals(7, replicatedLogImpl.lastIndex());
120 assertTrue(replicatedLogImpl.isPresent(7));
121 assertFalse(replicatedLogImpl.isInSnapshot(7));
122 assertEquals(1, replicatedLogImpl.getFrom(7).size());
123 assertEquals(2, replicatedLogImpl.getFrom(6).size());
125 // take a second snapshot with 5 entries with 0 unapplied entries left in the log
128 assertEquals(0, replicatedLogImpl.size());
129 assertNull(replicatedLogImpl.last());
130 assertNull(replicatedLogImpl.get(7));
131 assertNull(replicatedLogImpl.get(1));
132 assertFalse(replicatedLogImpl.isPresent(7));
133 assertTrue(replicatedLogImpl.isInSnapshot(7));
134 assertEquals(0, replicatedLogImpl.getFrom(7).size());
135 assertEquals(0, replicatedLogImpl.getFrom(6).size());
140 public void testGetFromWithMax() {
141 List<ReplicatedLogEntry> from = replicatedLogImpl.getFrom(0, 1, ReplicatedLog.NO_MAX_SIZE);
142 assertEquals(1, from.size());
143 assertEquals("A", from.get(0).getData().toString());
145 from = replicatedLogImpl.getFrom(0, 20, ReplicatedLog.NO_MAX_SIZE);
146 assertEquals(4, from.size());
147 assertEquals("A", from.get(0).getData().toString());
148 assertEquals("B", from.get(1).getData().toString());
149 assertEquals("C", from.get(2).getData().toString());
150 assertEquals("D", from.get(3).getData().toString());
152 // Pre-calculate sizing information for use with capping
153 final int sizeB = from.get(1).serializedSize();
154 final int sizeC = from.get(2).serializedSize();
155 final int sizeD = from.get(3).serializedSize();
157 from = replicatedLogImpl.getFrom(1, 2, ReplicatedLog.NO_MAX_SIZE);
158 assertEquals(2, from.size());
159 assertEquals("B", from.get(0).getData().toString());
160 assertEquals("C", from.get(1).getData().toString());
162 from = replicatedLogImpl.getFrom(1, 3, sizeB + sizeC);
163 assertEquals(2, from.size());
164 assertEquals("B", from.get(0).getData().toString());
165 assertEquals("C", from.get(1).getData().toString());
167 from = replicatedLogImpl.getFrom(1, 3, sizeB + sizeC + sizeD);
168 assertEquals(3, from.size());
169 assertEquals("B", from.get(0).getData().toString());
170 assertEquals("C", from.get(1).getData().toString());
171 assertEquals("D", from.get(2).getData().toString());
173 from = replicatedLogImpl.getFrom(1, 2, sizeB + sizeC + sizeD);
174 assertEquals(2, from.size());
175 assertEquals("B", from.get(0).getData().toString());
176 assertEquals("C", from.get(1).getData().toString());
178 replicatedLogImpl.append(new SimpleReplicatedLogEntry(4, 2, new MockPayload("12345")));
179 from = replicatedLogImpl.getFrom(4, 2, 2);
180 assertEquals(1, from.size());
181 assertEquals("12345", from.get(0).getData().toString());
185 public void testSnapshotPreCommit() {
187 replicatedLogImpl.append(new SimpleReplicatedLogEntry(4, 2, new MockPayload("E")));
188 replicatedLogImpl.append(new SimpleReplicatedLogEntry(5, 2, new MockPayload("F")));
189 replicatedLogImpl.append(new SimpleReplicatedLogEntry(6, 3, new MockPayload("G")));
190 replicatedLogImpl.append(new SimpleReplicatedLogEntry(7, 3, new MockPayload("H")));
192 //sending negative values should not cause any changes
193 replicatedLogImpl.snapshotPreCommit(-1, -1);
194 assertEquals(8, replicatedLogImpl.size());
195 assertEquals(-1, replicatedLogImpl.getSnapshotIndex());
196 assertEquals(-1, replicatedLogImpl.getSnapshotTerm());
198 replicatedLogImpl.snapshotPreCommit(4, 2);
199 assertEquals(3, replicatedLogImpl.size());
200 assertEquals(4, replicatedLogImpl.getSnapshotIndex());
201 assertEquals(2, replicatedLogImpl.getSnapshotTerm());
203 replicatedLogImpl.snapshotPreCommit(6, 3);
204 assertEquals(1, replicatedLogImpl.size());
205 assertEquals(6, replicatedLogImpl.getSnapshotIndex());
206 assertEquals(3, replicatedLogImpl.getSnapshotTerm());
208 replicatedLogImpl.snapshotPreCommit(7, 3);
209 assertEquals(0, replicatedLogImpl.size());
210 assertEquals(7, replicatedLogImpl.getSnapshotIndex());
211 assertEquals(3, replicatedLogImpl.getSnapshotTerm());
213 //running it again on an empty list should not throw exception
214 replicatedLogImpl.snapshotPreCommit(7, 3);
215 assertEquals(0, replicatedLogImpl.size());
216 assertEquals(7, replicatedLogImpl.getSnapshotIndex());
217 assertEquals(3, replicatedLogImpl.getSnapshotTerm());
221 public void testSnapshotCommit() {
223 replicatedLogImpl.snapshotPreCommit(1, 1);
225 replicatedLogImpl.snapshotCommit();
227 assertEquals("size", 2, replicatedLogImpl.size());
228 assertEquals("dataSize", 2, replicatedLogImpl.dataSize());
229 assertEquals("getSnapshotIndex", 1, replicatedLogImpl.getSnapshotIndex());
230 assertEquals("getSnapshotTerm", 1, replicatedLogImpl.getSnapshotTerm());
231 assertEquals("lastIndex", 3, replicatedLogImpl.lastIndex());
232 assertEquals("lastTerm", 2, replicatedLogImpl.lastTerm());
234 assertNull("get(0)", replicatedLogImpl.get(0));
235 assertNull("get(1)", replicatedLogImpl.get(1));
236 assertNotNull("get(2)", replicatedLogImpl.get(2));
237 assertNotNull("get(3)", replicatedLogImpl.get(3));
241 public void testSnapshotRollback() {
243 replicatedLogImpl.snapshotPreCommit(1, 1);
245 assertEquals("size", 2, replicatedLogImpl.size());
246 assertEquals("getSnapshotIndex", 1, replicatedLogImpl.getSnapshotIndex());
247 assertEquals("getSnapshotTerm", 1, replicatedLogImpl.getSnapshotTerm());
249 replicatedLogImpl.snapshotRollback();
251 assertEquals("size", 4, replicatedLogImpl.size());
252 assertEquals("dataSize", 4, replicatedLogImpl.dataSize());
253 assertEquals("getSnapshotIndex", -1, replicatedLogImpl.getSnapshotIndex());
254 assertEquals("getSnapshotTerm", -1, replicatedLogImpl.getSnapshotTerm());
255 assertNotNull("get(0)", replicatedLogImpl.get(0));
256 assertNotNull("get(3)", replicatedLogImpl.get(3));
260 public void testIsPresent() {
261 assertTrue(replicatedLogImpl.isPresent(0));
262 assertTrue(replicatedLogImpl.isPresent(1));
263 assertTrue(replicatedLogImpl.isPresent(2));
264 assertTrue(replicatedLogImpl.isPresent(3));
266 replicatedLogImpl.append(new SimpleReplicatedLogEntry(4, 2, new MockPayload("D")));
267 replicatedLogImpl.snapshotPreCommit(3, 2); //snapshot on 3
268 replicatedLogImpl.snapshotCommit();
270 assertFalse(replicatedLogImpl.isPresent(0));
271 assertFalse(replicatedLogImpl.isPresent(1));
272 assertFalse(replicatedLogImpl.isPresent(2));
273 assertFalse(replicatedLogImpl.isPresent(3));
274 assertTrue(replicatedLogImpl.isPresent(4));
276 replicatedLogImpl.snapshotPreCommit(4, 2); //snapshot on 4
277 replicatedLogImpl.snapshotCommit();
278 assertFalse(replicatedLogImpl.isPresent(4));
280 replicatedLogImpl.append(new SimpleReplicatedLogEntry(5, 2, new MockPayload("D")));
281 assertTrue(replicatedLogImpl.isPresent(5));
285 public void testRemoveFrom() {
287 replicatedLogImpl.append(new SimpleReplicatedLogEntry(4, 2, new MockPayload("E", 2)));
288 replicatedLogImpl.append(new SimpleReplicatedLogEntry(5, 2, new MockPayload("F", 3)));
290 assertEquals("dataSize", 9, replicatedLogImpl.dataSize());
292 long adjusted = replicatedLogImpl.removeFrom(4);
293 assertEquals("removeFrom - adjusted", 4, adjusted);
294 assertEquals("size", 4, replicatedLogImpl.size());
295 assertEquals("dataSize", 4, replicatedLogImpl.dataSize());
299 adjusted = replicatedLogImpl.removeFrom(2);
300 assertEquals("removeFrom - adjusted", 1, adjusted);
301 assertEquals("size", 1, replicatedLogImpl.size());
302 assertEquals("dataSize", 1, replicatedLogImpl.dataSize());
304 assertEquals("removeFrom - adjusted", -1, replicatedLogImpl.removeFrom(0));
305 assertEquals("removeFrom - adjusted", -1, replicatedLogImpl.removeFrom(100));
308 // create a snapshot for test
309 public Map<Long, String> takeSnapshot(final int numEntries) {
310 Map<Long, String> map = new HashMap<>(numEntries);
314 for (int i = 0; i < numEntries; i++) {
315 ReplicatedLogEntry entry = replicatedLogImpl.getAtPhysicalIndex(i);
316 map.put(entry.getIndex(), entry.getData().toString());
317 lastIndex = entry.getIndex();
318 lastTerm = entry.getTerm();
321 replicatedLogImpl.snapshotPreCommit(lastIndex, lastTerm);
322 replicatedLogImpl.snapshotCommit();
328 static class MockAbstractReplicatedLogImpl extends AbstractReplicatedLogImpl {
330 public boolean removeFromAndPersist(final long index) {
335 public boolean appendAndPersist(final ReplicatedLogEntry replicatedLogEntry,
336 final Consumer<ReplicatedLogEntry> callback, final boolean doAsync) {
337 if (callback != null) {
338 callback.accept(replicatedLogEntry);
344 public void captureSnapshotIfReady(final ReplicatedLogEntry replicatedLogEntry) {
349 public boolean shouldCaptureSnapshot(final long logIndex) {