a37a1314708100f962e8554c5c4ba075bbfbd896
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / test / java / org / opendaylight / controller / cluster / raft / AbstractReplicatedLogImplTest.java
1 /*
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.
4  *
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
8  */
9 package org.opendaylight.controller.cluster.raft;
10
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
16 import akka.japi.Procedure;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20 import org.junit.Assert;
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;
25
26 /**
27 * Unit tests for AbstractReplicatedLogImplTest.
28 */
29 public class AbstractReplicatedLogImplTest {
30
31     private MockAbstractReplicatedLogImpl replicatedLogImpl;
32
33     @Before
34     public void setUp() {
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")));
41
42     }
43
44     @Test
45     public void testEmptyLog() {
46         replicatedLogImpl = new MockAbstractReplicatedLogImpl();
47
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         Assert.assertNull("get(0)", replicatedLogImpl.get(0));
57         Assert.assertNull("last", replicatedLogImpl.last());
58
59         List<ReplicatedLogEntry> list = replicatedLogImpl.getFrom(0, 1, ReplicatedLog.NO_MAX_SIZE);
60         assertEquals("getFrom size", 0, list.size());
61
62         assertEquals("removeFrom", -1, replicatedLogImpl.removeFrom(1));
63
64         replicatedLogImpl.setSnapshotIndex(2);
65         replicatedLogImpl.setSnapshotTerm(1);
66
67         assertEquals("getSnapshotIndex", 2, replicatedLogImpl.getSnapshotIndex());
68         assertEquals("getSnapshotTerm", 1, replicatedLogImpl.getSnapshotTerm());
69         assertEquals("lastIndex", 2, replicatedLogImpl.lastIndex());
70         assertEquals("lastTerm", 1, replicatedLogImpl.lastTerm());
71     }
72
73     @Test
74     public void testIndexOperations() {
75
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));
86
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
90         takeSnapshot(3);
91
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));
106
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")));
112
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());
124
125         // take a second snapshot with 5 entries with 0 unapplied entries left in the log
126         takeSnapshot(5);
127
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());
136
137     }
138
139     @Test
140     public void testGetFromWithMax() {
141         List<ReplicatedLogEntry> from = replicatedLogImpl.getFrom(0, 1, ReplicatedLog.NO_MAX_SIZE);
142         Assert.assertEquals(1, from.size());
143         Assert.assertEquals("A", from.get(0).getData().toString());
144
145         from = replicatedLogImpl.getFrom(0, 20, ReplicatedLog.NO_MAX_SIZE);
146         Assert.assertEquals(4, from.size());
147         Assert.assertEquals("A", from.get(0).getData().toString());
148         Assert.assertEquals("D", from.get(3).getData().toString());
149
150         from = replicatedLogImpl.getFrom(1, 2, ReplicatedLog.NO_MAX_SIZE);
151         Assert.assertEquals(2, from.size());
152         Assert.assertEquals("B", from.get(0).getData().toString());
153         Assert.assertEquals("C", from.get(1).getData().toString());
154
155         from = replicatedLogImpl.getFrom(1, 3, 2);
156         Assert.assertEquals(2, from.size());
157         Assert.assertEquals("B", from.get(0).getData().toString());
158         Assert.assertEquals("C", from.get(1).getData().toString());
159
160         from = replicatedLogImpl.getFrom(1, 3, 3);
161         Assert.assertEquals(3, from.size());
162         Assert.assertEquals("B", from.get(0).getData().toString());
163         Assert.assertEquals("C", from.get(1).getData().toString());
164         Assert.assertEquals("D", from.get(2).getData().toString());
165
166         from = replicatedLogImpl.getFrom(1, 2, 3);
167         Assert.assertEquals(2, from.size());
168         Assert.assertEquals("B", from.get(0).getData().toString());
169         Assert.assertEquals("C", from.get(1).getData().toString());
170
171         replicatedLogImpl.append(new SimpleReplicatedLogEntry(4, 2, new MockPayload("12345")));
172         from = replicatedLogImpl.getFrom(4, 2, 2);
173         Assert.assertEquals(1, from.size());
174         Assert.assertEquals("12345", from.get(0).getData().toString());
175     }
176
177     @Test
178     public void testSnapshotPreCommit() {
179         //add 4 more entries
180         replicatedLogImpl.append(new SimpleReplicatedLogEntry(4, 2, new MockPayload("E")));
181         replicatedLogImpl.append(new SimpleReplicatedLogEntry(5, 2, new MockPayload("F")));
182         replicatedLogImpl.append(new SimpleReplicatedLogEntry(6, 3, new MockPayload("G")));
183         replicatedLogImpl.append(new SimpleReplicatedLogEntry(7, 3, new MockPayload("H")));
184
185         //sending negative values should not cause any changes
186         replicatedLogImpl.snapshotPreCommit(-1, -1);
187         assertEquals(8, replicatedLogImpl.size());
188         assertEquals(-1, replicatedLogImpl.getSnapshotIndex());
189         assertEquals(-1, replicatedLogImpl.getSnapshotTerm());
190
191         replicatedLogImpl.snapshotPreCommit(4, 2);
192         assertEquals(3, replicatedLogImpl.size());
193         assertEquals(4, replicatedLogImpl.getSnapshotIndex());
194         assertEquals(2, replicatedLogImpl.getSnapshotTerm());
195
196         replicatedLogImpl.snapshotPreCommit(6, 3);
197         assertEquals(1, replicatedLogImpl.size());
198         assertEquals(6, replicatedLogImpl.getSnapshotIndex());
199         assertEquals(3, replicatedLogImpl.getSnapshotTerm());
200
201         replicatedLogImpl.snapshotPreCommit(7, 3);
202         assertEquals(0, replicatedLogImpl.size());
203         assertEquals(7, replicatedLogImpl.getSnapshotIndex());
204         assertEquals(3, replicatedLogImpl.getSnapshotTerm());
205
206         //running it again on an empty list should not throw exception
207         replicatedLogImpl.snapshotPreCommit(7, 3);
208         assertEquals(0, replicatedLogImpl.size());
209         assertEquals(7, replicatedLogImpl.getSnapshotIndex());
210         assertEquals(3, replicatedLogImpl.getSnapshotTerm());
211     }
212
213     @Test
214     public void testSnapshotCommit() {
215
216         replicatedLogImpl.snapshotPreCommit(1, 1);
217
218         replicatedLogImpl.snapshotCommit();
219
220         assertEquals("size", 2, replicatedLogImpl.size());
221         assertEquals("dataSize", 2, replicatedLogImpl.dataSize());
222         assertEquals("getSnapshotIndex", 1, replicatedLogImpl.getSnapshotIndex());
223         assertEquals("getSnapshotTerm", 1, replicatedLogImpl.getSnapshotTerm());
224         assertEquals("lastIndex", 3, replicatedLogImpl.lastIndex());
225         assertEquals("lastTerm", 2, replicatedLogImpl.lastTerm());
226
227         Assert.assertNull("get(0)", replicatedLogImpl.get(0));
228         Assert.assertNull("get(1)", replicatedLogImpl.get(1));
229         Assert.assertNotNull("get(2)", replicatedLogImpl.get(2));
230         Assert.assertNotNull("get(3)", replicatedLogImpl.get(3));
231     }
232
233     @Test
234     public void testSnapshotRollback() {
235
236         replicatedLogImpl.snapshotPreCommit(1, 1);
237
238         assertEquals("size", 2, replicatedLogImpl.size());
239         assertEquals("getSnapshotIndex", 1, replicatedLogImpl.getSnapshotIndex());
240         assertEquals("getSnapshotTerm", 1, replicatedLogImpl.getSnapshotTerm());
241
242         replicatedLogImpl.snapshotRollback();
243
244         assertEquals("size", 4, replicatedLogImpl.size());
245         assertEquals("dataSize", 4, replicatedLogImpl.dataSize());
246         assertEquals("getSnapshotIndex", -1, replicatedLogImpl.getSnapshotIndex());
247         assertEquals("getSnapshotTerm", -1, replicatedLogImpl.getSnapshotTerm());
248         Assert.assertNotNull("get(0)", replicatedLogImpl.get(0));
249         Assert.assertNotNull("get(3)", replicatedLogImpl.get(3));
250     }
251
252     @Test
253     public void testIsPresent() {
254         assertTrue(replicatedLogImpl.isPresent(0));
255         assertTrue(replicatedLogImpl.isPresent(1));
256         assertTrue(replicatedLogImpl.isPresent(2));
257         assertTrue(replicatedLogImpl.isPresent(3));
258
259         replicatedLogImpl.append(new SimpleReplicatedLogEntry(4, 2, new MockPayload("D")));
260         replicatedLogImpl.snapshotPreCommit(3, 2); //snapshot on 3
261         replicatedLogImpl.snapshotCommit();
262
263         assertFalse(replicatedLogImpl.isPresent(0));
264         assertFalse(replicatedLogImpl.isPresent(1));
265         assertFalse(replicatedLogImpl.isPresent(2));
266         assertFalse(replicatedLogImpl.isPresent(3));
267         assertTrue(replicatedLogImpl.isPresent(4));
268
269         replicatedLogImpl.snapshotPreCommit(4, 2); //snapshot on 4
270         replicatedLogImpl.snapshotCommit();
271         assertFalse(replicatedLogImpl.isPresent(4));
272
273         replicatedLogImpl.append(new SimpleReplicatedLogEntry(5, 2, new MockPayload("D")));
274         assertTrue(replicatedLogImpl.isPresent(5));
275     }
276
277     @Test
278     public void testRemoveFrom() {
279
280         replicatedLogImpl.append(new SimpleReplicatedLogEntry(4, 2, new MockPayload("E", 2)));
281         replicatedLogImpl.append(new SimpleReplicatedLogEntry(5, 2, new MockPayload("F", 3)));
282
283         assertEquals("dataSize", 9, replicatedLogImpl.dataSize());
284
285         long adjusted = replicatedLogImpl.removeFrom(4);
286         assertEquals("removeFrom - adjusted", 4, adjusted);
287         assertEquals("size", 4, replicatedLogImpl.size());
288         assertEquals("dataSize", 4, replicatedLogImpl.dataSize());
289
290         takeSnapshot(1);
291
292         adjusted = replicatedLogImpl.removeFrom(2);
293         assertEquals("removeFrom - adjusted", 1, adjusted);
294         assertEquals("size", 1, replicatedLogImpl.size());
295         assertEquals("dataSize", 1, replicatedLogImpl.dataSize());
296
297         assertEquals("removeFrom - adjusted", -1, replicatedLogImpl.removeFrom(0));
298         assertEquals("removeFrom - adjusted", -1, replicatedLogImpl.removeFrom(100));
299     }
300
301     // create a snapshot for test
302     public Map<Long, String> takeSnapshot(final int numEntries) {
303         Map<Long, String> map = new HashMap<>(numEntries);
304
305         long lastIndex = 0;
306         long lastTerm = 0;
307         for (int i = 0; i < numEntries; i++) {
308             ReplicatedLogEntry entry = replicatedLogImpl.getAtPhysicalIndex(i);
309             map.put(entry.getIndex(), entry.getData().toString());
310             lastIndex = entry.getIndex();
311             lastTerm = entry.getTerm();
312         }
313
314         replicatedLogImpl.snapshotPreCommit(lastIndex, lastTerm);
315         replicatedLogImpl.snapshotCommit();
316
317         return map;
318
319     }
320
321     class MockAbstractReplicatedLogImpl extends AbstractReplicatedLogImpl {
322         @Override
323         public boolean removeFromAndPersist(final long index) {
324             return true;
325         }
326
327         @Override
328         public boolean appendAndPersist(ReplicatedLogEntry replicatedLogEntry, Procedure<ReplicatedLogEntry> callback,
329                 boolean doAsync) {
330             return true;
331         }
332
333         @Override
334         public void captureSnapshotIfReady(ReplicatedLogEntry replicatedLogEntry) {
335         }
336     }
337 }