Improve segmented journal actor metrics
[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.assertNotNull;
14 import static org.junit.Assert.assertNull;
15 import static org.junit.Assert.assertTrue;
16
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
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;
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         assertNull("get(0)", replicatedLogImpl.get(0));
57         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         assertEquals(1, from.size());
143         assertEquals("A", from.get(0).getData().toString());
144
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());
151
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();
156
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());
161
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());
166
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());
172
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());
177
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());
182     }
183
184     @Test
185     public void testSnapshotPreCommit() {
186         //add 4 more entries
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")));
191
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());
197
198         replicatedLogImpl.snapshotPreCommit(4, 2);
199         assertEquals(3, replicatedLogImpl.size());
200         assertEquals(4, replicatedLogImpl.getSnapshotIndex());
201         assertEquals(2, replicatedLogImpl.getSnapshotTerm());
202
203         replicatedLogImpl.snapshotPreCommit(6, 3);
204         assertEquals(1, replicatedLogImpl.size());
205         assertEquals(6, replicatedLogImpl.getSnapshotIndex());
206         assertEquals(3, replicatedLogImpl.getSnapshotTerm());
207
208         replicatedLogImpl.snapshotPreCommit(7, 3);
209         assertEquals(0, replicatedLogImpl.size());
210         assertEquals(7, replicatedLogImpl.getSnapshotIndex());
211         assertEquals(3, replicatedLogImpl.getSnapshotTerm());
212
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());
218     }
219
220     @Test
221     public void testSnapshotCommit() {
222
223         replicatedLogImpl.snapshotPreCommit(1, 1);
224
225         replicatedLogImpl.snapshotCommit();
226
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());
233
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));
238     }
239
240     @Test
241     public void testSnapshotRollback() {
242
243         replicatedLogImpl.snapshotPreCommit(1, 1);
244
245         assertEquals("size", 2, replicatedLogImpl.size());
246         assertEquals("getSnapshotIndex", 1, replicatedLogImpl.getSnapshotIndex());
247         assertEquals("getSnapshotTerm", 1, replicatedLogImpl.getSnapshotTerm());
248
249         replicatedLogImpl.snapshotRollback();
250
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));
257     }
258
259     @Test
260     public void testIsPresent() {
261         assertTrue(replicatedLogImpl.isPresent(0));
262         assertTrue(replicatedLogImpl.isPresent(1));
263         assertTrue(replicatedLogImpl.isPresent(2));
264         assertTrue(replicatedLogImpl.isPresent(3));
265
266         replicatedLogImpl.append(new SimpleReplicatedLogEntry(4, 2, new MockPayload("D")));
267         replicatedLogImpl.snapshotPreCommit(3, 2); //snapshot on 3
268         replicatedLogImpl.snapshotCommit();
269
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));
275
276         replicatedLogImpl.snapshotPreCommit(4, 2); //snapshot on 4
277         replicatedLogImpl.snapshotCommit();
278         assertFalse(replicatedLogImpl.isPresent(4));
279
280         replicatedLogImpl.append(new SimpleReplicatedLogEntry(5, 2, new MockPayload("D")));
281         assertTrue(replicatedLogImpl.isPresent(5));
282     }
283
284     @Test
285     public void testRemoveFrom() {
286
287         replicatedLogImpl.append(new SimpleReplicatedLogEntry(4, 2, new MockPayload("E", 2)));
288         replicatedLogImpl.append(new SimpleReplicatedLogEntry(5, 2, new MockPayload("F", 3)));
289
290         assertEquals("dataSize", 9, replicatedLogImpl.dataSize());
291
292         long adjusted = replicatedLogImpl.removeFrom(4);
293         assertEquals("removeFrom - adjusted", 4, adjusted);
294         assertEquals("size", 4, replicatedLogImpl.size());
295         assertEquals("dataSize", 4, replicatedLogImpl.dataSize());
296
297         takeSnapshot(1);
298
299         adjusted = replicatedLogImpl.removeFrom(2);
300         assertEquals("removeFrom - adjusted", 1, adjusted);
301         assertEquals("size", 1, replicatedLogImpl.size());
302         assertEquals("dataSize", 1, replicatedLogImpl.dataSize());
303
304         assertEquals("removeFrom - adjusted", -1, replicatedLogImpl.removeFrom(0));
305         assertEquals("removeFrom - adjusted", -1, replicatedLogImpl.removeFrom(100));
306     }
307
308     // create a snapshot for test
309     public Map<Long, String> takeSnapshot(final int numEntries) {
310         Map<Long, String> map = new HashMap<>(numEntries);
311
312         long lastIndex = 0;
313         long lastTerm = 0;
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();
319         }
320
321         replicatedLogImpl.snapshotPreCommit(lastIndex, lastTerm);
322         replicatedLogImpl.snapshotCommit();
323
324         return map;
325
326     }
327
328     static class MockAbstractReplicatedLogImpl extends AbstractReplicatedLogImpl {
329         @Override
330         public boolean removeFromAndPersist(final long index) {
331             return true;
332         }
333
334         @Override
335         public boolean appendAndPersist(final ReplicatedLogEntry replicatedLogEntry,
336                 final Consumer<ReplicatedLogEntry> callback, final boolean doAsync) {
337             if (callback != null) {
338                 callback.accept(replicatedLogEntry);
339             }
340             return true;
341         }
342
343         @Override
344         public void captureSnapshotIfReady(final ReplicatedLogEntry replicatedLogEntry) {
345             // No-op
346         }
347
348         @Override
349         public boolean shouldCaptureSnapshot(final long logIndex) {
350             return false;
351         }
352     }
353 }