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, ReplicatedLog.NO_MAX_SIZE);
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, ReplicatedLog.NO_MAX_SIZE);
141 Assert.assertEquals(1, from.size());
142 Assert.assertEquals("A", from.get(0).getData().toString());
144 from = replicatedLogImpl.getFrom(0, 20, ReplicatedLog.NO_MAX_SIZE);
145 Assert.assertEquals(4, from.size());
146 Assert.assertEquals("A", from.get(0).getData().toString());
147 Assert.assertEquals("D", from.get(3).getData().toString());
149 from = replicatedLogImpl.getFrom(1, 2, ReplicatedLog.NO_MAX_SIZE);
150 Assert.assertEquals(2, from.size());
151 Assert.assertEquals("B", from.get(0).getData().toString());
152 Assert.assertEquals("C", from.get(1).getData().toString());
154 from = replicatedLogImpl.getFrom(1, 3, 2);
155 Assert.assertEquals(2, from.size());
156 Assert.assertEquals("B", from.get(0).getData().toString());
157 Assert.assertEquals("C", from.get(1).getData().toString());
159 from = replicatedLogImpl.getFrom(1, 3, 3);
160 Assert.assertEquals(3, from.size());
161 Assert.assertEquals("B", from.get(0).getData().toString());
162 Assert.assertEquals("C", from.get(1).getData().toString());
163 Assert.assertEquals("D", from.get(2).getData().toString());
165 from = replicatedLogImpl.getFrom(1, 2, 3);
166 Assert.assertEquals(2, from.size());
167 Assert.assertEquals("B", from.get(0).getData().toString());
168 Assert.assertEquals("C", from.get(1).getData().toString());
170 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("12345")));
171 from = replicatedLogImpl.getFrom(4, 2, 2);
172 Assert.assertEquals(1, from.size());
173 Assert.assertEquals("12345", from.get(0).getData().toString());
177 public void testSnapshotPreCommit() {
179 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("E")));
180 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("F")));
181 replicatedLogImpl.append(new MockReplicatedLogEntry(3, 6, new MockPayload("G")));
182 replicatedLogImpl.append(new MockReplicatedLogEntry(3, 7, new MockPayload("H")));
184 //sending negative values should not cause any changes
185 replicatedLogImpl.snapshotPreCommit(-1, -1);
186 assertEquals(8, replicatedLogImpl.size());
187 assertEquals(-1, replicatedLogImpl.getSnapshotIndex());
188 assertEquals(-1, replicatedLogImpl.getSnapshotTerm());
190 replicatedLogImpl.snapshotPreCommit(4, 2);
191 assertEquals(3, replicatedLogImpl.size());
192 assertEquals(4, replicatedLogImpl.getSnapshotIndex());
193 assertEquals(2, replicatedLogImpl.getSnapshotTerm());
195 replicatedLogImpl.snapshotPreCommit(6, 3);
196 assertEquals(1, replicatedLogImpl.size());
197 assertEquals(6, replicatedLogImpl.getSnapshotIndex());
198 assertEquals(3, replicatedLogImpl.getSnapshotTerm());
200 replicatedLogImpl.snapshotPreCommit(7, 3);
201 assertEquals(0, replicatedLogImpl.size());
202 assertEquals(7, replicatedLogImpl.getSnapshotIndex());
203 assertEquals(3, replicatedLogImpl.getSnapshotTerm());
205 //running it again on an empty list should not throw exception
206 replicatedLogImpl.snapshotPreCommit(7, 3);
207 assertEquals(0, replicatedLogImpl.size());
208 assertEquals(7, replicatedLogImpl.getSnapshotIndex());
209 assertEquals(3, replicatedLogImpl.getSnapshotTerm());
213 public void testSnapshotCommit() {
215 replicatedLogImpl.snapshotPreCommit(1, 1);
217 replicatedLogImpl.snapshotCommit();
219 assertEquals("size", 2, replicatedLogImpl.size());
220 assertEquals("dataSize", 2, replicatedLogImpl.dataSize());
221 assertEquals("getSnapshotIndex", 1, replicatedLogImpl.getSnapshotIndex());
222 assertEquals("getSnapshotTerm", 1, replicatedLogImpl.getSnapshotTerm());
223 assertEquals("lastIndex", 3, replicatedLogImpl.lastIndex());
224 assertEquals("lastTerm", 2, replicatedLogImpl.lastTerm());
226 Assert.assertNull("get(0)", replicatedLogImpl.get(0));
227 Assert.assertNull("get(1)", replicatedLogImpl.get(1));
228 Assert.assertNotNull("get(2)", replicatedLogImpl.get(2));
229 Assert.assertNotNull("get(3)", replicatedLogImpl.get(3));
233 public void testSnapshotRollback() {
235 replicatedLogImpl.snapshotPreCommit(1, 1);
237 assertEquals("size", 2, replicatedLogImpl.size());
238 assertEquals("getSnapshotIndex", 1, replicatedLogImpl.getSnapshotIndex());
239 assertEquals("getSnapshotTerm", 1, replicatedLogImpl.getSnapshotTerm());
241 replicatedLogImpl.snapshotRollback();
243 assertEquals("size", 4, replicatedLogImpl.size());
244 assertEquals("dataSize", 4, replicatedLogImpl.dataSize());
245 assertEquals("getSnapshotIndex", -1, replicatedLogImpl.getSnapshotIndex());
246 assertEquals("getSnapshotTerm", -1, replicatedLogImpl.getSnapshotTerm());
247 Assert.assertNotNull("get(0)", replicatedLogImpl.get(0));
248 Assert.assertNotNull("get(3)", replicatedLogImpl.get(3));
252 public void testIsPresent() {
253 assertTrue(replicatedLogImpl.isPresent(0));
254 assertTrue(replicatedLogImpl.isPresent(1));
255 assertTrue(replicatedLogImpl.isPresent(2));
256 assertTrue(replicatedLogImpl.isPresent(3));
258 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("D")));
259 replicatedLogImpl.snapshotPreCommit(3, 2); //snapshot on 3
260 replicatedLogImpl.snapshotCommit();
262 assertFalse(replicatedLogImpl.isPresent(0));
263 assertFalse(replicatedLogImpl.isPresent(1));
264 assertFalse(replicatedLogImpl.isPresent(2));
265 assertFalse(replicatedLogImpl.isPresent(3));
266 assertTrue(replicatedLogImpl.isPresent(4));
268 replicatedLogImpl.snapshotPreCommit(4, 2); //snapshot on 4
269 replicatedLogImpl.snapshotCommit();
270 assertFalse(replicatedLogImpl.isPresent(4));
272 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("D")));
273 assertTrue(replicatedLogImpl.isPresent(5));
277 public void testRemoveFrom() {
279 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("E", 2)));
280 replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("F", 3)));
282 assertEquals("dataSize", 9, replicatedLogImpl.dataSize());
284 long adjusted = replicatedLogImpl.removeFrom(4);
285 assertEquals("removeFrom - adjusted", 4, adjusted);
286 assertEquals("size", 4, replicatedLogImpl.size());
287 assertEquals("dataSize", 4, replicatedLogImpl.dataSize());
291 adjusted = replicatedLogImpl.removeFrom(2);
292 assertEquals("removeFrom - adjusted", 1, adjusted);
293 assertEquals("size", 1, replicatedLogImpl.size());
294 assertEquals("dataSize", 1, replicatedLogImpl.dataSize());
296 assertEquals("removeFrom - adjusted", -1, replicatedLogImpl.removeFrom(0));
297 assertEquals("removeFrom - adjusted", -1, replicatedLogImpl.removeFrom(100));
300 // create a snapshot for test
301 public Map<Long, String> takeSnapshot(final int numEntries) {
302 Map<Long, String> map = new HashMap<>(numEntries);
306 for(int i = 0; i < numEntries; i++) {
307 ReplicatedLogEntry entry = replicatedLogImpl.getAtPhysicalIndex(i);
308 map.put(entry.getIndex(), entry.getData().toString());
309 lastIndex = entry.getIndex();
310 lastTerm = entry.getTerm();
313 replicatedLogImpl.snapshotPreCommit(lastIndex, lastTerm);
314 replicatedLogImpl.snapshotCommit();
319 class MockAbstractReplicatedLogImpl extends AbstractReplicatedLogImpl {
321 public void appendAndPersist(final ReplicatedLogEntry replicatedLogEntry) {
325 public void removeFromAndPersist(final long index) {
329 public void appendAndPersist(ReplicatedLogEntry replicatedLogEntry, Procedure<ReplicatedLogEntry> callback) {
333 public void captureSnapshotIfReady(ReplicatedLogEntry replicatedLogEntry) {