Merge "Fixes Bug 2935"
[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  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.cluster.raft;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNull;
13 import static org.junit.Assert.assertTrue;
14 import akka.japi.Procedure;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18 import org.junit.Assert;
19 import org.junit.Before;
20 import org.junit.Test;
21 import org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload;
22 import org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockReplicatedLogEntry;
23
24 /**
25 *
26 */
27 public class AbstractReplicatedLogImplTest {
28
29     private MockAbstractReplicatedLogImpl replicatedLogImpl;
30
31     @Before
32     public void setUp() {
33         replicatedLogImpl = new MockAbstractReplicatedLogImpl();
34         // create a set of initial entries in the in-memory log
35         replicatedLogImpl.append(new MockReplicatedLogEntry(1, 0, new MockPayload("A")));
36         replicatedLogImpl.append(new MockReplicatedLogEntry(1, 1, new MockPayload("B")));
37         replicatedLogImpl.append(new MockReplicatedLogEntry(1, 2, new MockPayload("C")));
38         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 3, new MockPayload("D")));
39
40     }
41
42     @Test
43     public void testIndexOperations() {
44
45         // check if the values returned are correct, with snapshotIndex = -1
46         assertEquals("B", replicatedLogImpl.get(1).getData().toString());
47         assertEquals("D", replicatedLogImpl.last().getData().toString());
48         assertEquals(3, replicatedLogImpl.lastIndex());
49         assertEquals(2, replicatedLogImpl.lastTerm());
50         assertEquals(2, replicatedLogImpl.getFrom(2).size());
51         assertEquals(4, replicatedLogImpl.size());
52         assertTrue(replicatedLogImpl.isPresent(2));
53         assertFalse(replicatedLogImpl.isPresent(4));
54         assertFalse(replicatedLogImpl.isInSnapshot(2));
55
56         // now create a snapshot of 3 entries, with 1 unapplied entry left in the log
57         // It removes the entries which have made it to snapshot
58         // and updates the snapshot index and term
59         takeSnapshot(3);
60
61         // check the values after the snapshot.
62         // each index value passed in the test is the logical index (log entry index)
63         // which gets mapped to the list's physical index
64         assertEquals("D", replicatedLogImpl.get(3).getData().toString());
65         assertEquals("D", replicatedLogImpl.last().getData().toString());
66         assertNull(replicatedLogImpl.get(1));
67         assertEquals(3, replicatedLogImpl.lastIndex());
68         assertEquals(2, replicatedLogImpl.lastTerm());
69         assertEquals(0, replicatedLogImpl.getFrom(2).size());
70         assertEquals(1, replicatedLogImpl.size());
71         assertFalse(replicatedLogImpl.isPresent(2));
72         assertTrue(replicatedLogImpl.isPresent(3));
73         assertFalse(replicatedLogImpl.isPresent(4));
74         assertTrue(replicatedLogImpl.isInSnapshot(2));
75
76         // append few more entries
77         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("E")));
78         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("F")));
79         replicatedLogImpl.append(new MockReplicatedLogEntry(3, 6, new MockPayload("G")));
80         replicatedLogImpl.append(new MockReplicatedLogEntry(3, 7, new MockPayload("H")));
81
82         // check their values as well
83         assertEquals(5, replicatedLogImpl.size());
84         assertEquals("D", replicatedLogImpl.get(3).getData().toString());
85         assertEquals("E", replicatedLogImpl.get(4).getData().toString());
86         assertEquals("H", replicatedLogImpl.last().getData().toString());
87         assertEquals(3, replicatedLogImpl.lastTerm());
88         assertEquals(7, replicatedLogImpl.lastIndex());
89         assertTrue(replicatedLogImpl.isPresent(7));
90         assertFalse(replicatedLogImpl.isInSnapshot(7));
91         assertEquals(1, replicatedLogImpl.getFrom(7).size());
92         assertEquals(2, replicatedLogImpl.getFrom(6).size());
93
94         // take a second snapshot with 5 entries with 0 unapplied entries left in the log
95         takeSnapshot(5);
96
97         assertEquals(0, replicatedLogImpl.size());
98         assertNull(replicatedLogImpl.last());
99         assertNull(replicatedLogImpl.get(7));
100         assertNull(replicatedLogImpl.get(1));
101         assertFalse(replicatedLogImpl.isPresent(7));
102         assertTrue(replicatedLogImpl.isInSnapshot(7));
103         assertEquals(0, replicatedLogImpl.getFrom(7).size());
104         assertEquals(0, replicatedLogImpl.getFrom(6).size());
105
106     }
107
108     @Test
109     public void testGetFromWithMax(){
110         List<ReplicatedLogEntry> from = replicatedLogImpl.getFrom(0, 1);
111         Assert.assertEquals(1, from.size());
112         Assert.assertEquals(1, from.get(0).getTerm());
113
114         from = replicatedLogImpl.getFrom(0, 20);
115         Assert.assertEquals(4, from.size());
116         Assert.assertEquals(2, from.get(3).getTerm());
117
118         from = replicatedLogImpl.getFrom(1, 2);
119         Assert.assertEquals(2, from.size());
120         Assert.assertEquals(1, from.get(1).getTerm());
121
122     }
123
124     @Test
125     public void testSnapshotPreCommit() {
126         //add 4 more entries
127         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("E")));
128         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("F")));
129         replicatedLogImpl.append(new MockReplicatedLogEntry(3, 6, new MockPayload("G")));
130         replicatedLogImpl.append(new MockReplicatedLogEntry(3, 7, new MockPayload("H")));
131
132         //sending negative values should not cause any changes
133         replicatedLogImpl.snapshotPreCommit(-1, -1);
134         assertEquals(8, replicatedLogImpl.size());
135         assertEquals(-1, replicatedLogImpl.getSnapshotIndex());
136
137         replicatedLogImpl.snapshotPreCommit(4, 3);
138         assertEquals(3, replicatedLogImpl.size());
139         assertEquals(4, replicatedLogImpl.getSnapshotIndex());
140
141         replicatedLogImpl.snapshotPreCommit(6, 3);
142         assertEquals(1, replicatedLogImpl.size());
143         assertEquals(6, replicatedLogImpl.getSnapshotIndex());
144
145         replicatedLogImpl.snapshotPreCommit(7, 3);
146         assertEquals(0, replicatedLogImpl.size());
147         assertEquals(7, replicatedLogImpl.getSnapshotIndex());
148
149         //running it again on an empty list should not throw exception
150         replicatedLogImpl.snapshotPreCommit(7, 3);
151         assertEquals(0, replicatedLogImpl.size());
152         assertEquals(7, replicatedLogImpl.getSnapshotIndex());
153
154     }
155
156     @Test
157     public void testIsPresent() {
158         assertTrue(replicatedLogImpl.isPresent(0));
159         assertTrue(replicatedLogImpl.isPresent(1));
160         assertTrue(replicatedLogImpl.isPresent(2));
161         assertTrue(replicatedLogImpl.isPresent(3));
162
163         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("D")));
164         replicatedLogImpl.snapshotPreCommit(3, 2); //snapshot on 3
165         replicatedLogImpl.snapshotCommit();
166
167         assertFalse(replicatedLogImpl.isPresent(0));
168         assertFalse(replicatedLogImpl.isPresent(1));
169         assertFalse(replicatedLogImpl.isPresent(2));
170         assertFalse(replicatedLogImpl.isPresent(3));
171         assertTrue(replicatedLogImpl.isPresent(4));
172
173         replicatedLogImpl.snapshotPreCommit(4, 2); //snapshot on 4
174         replicatedLogImpl.snapshotCommit();
175         assertFalse(replicatedLogImpl.isPresent(4));
176
177         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("D")));
178         assertTrue(replicatedLogImpl.isPresent(5));
179     }
180
181     @Test
182     public void testRemoveFrom() {
183
184         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("E", 2)));
185         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("F", 3)));
186
187         assertEquals("dataSize", 9, replicatedLogImpl.dataSize());
188
189         long adjusted = replicatedLogImpl.removeFrom(4);
190         assertEquals("removeFrom - adjusted", 4, adjusted);
191         assertEquals("size", 4, replicatedLogImpl.size());
192         assertEquals("dataSize", 4, replicatedLogImpl.dataSize());
193
194         takeSnapshot(1);
195
196         adjusted = replicatedLogImpl.removeFrom(2);
197         assertEquals("removeFrom - adjusted", 1, adjusted);
198         assertEquals("size", 1, replicatedLogImpl.size());
199         assertEquals("dataSize", 1, replicatedLogImpl.dataSize());
200
201         assertEquals("removeFrom - adjusted", -1, replicatedLogImpl.removeFrom(0));
202         assertEquals("removeFrom - adjusted", -1, replicatedLogImpl.removeFrom(100));
203     }
204
205     // create a snapshot for test
206     public Map<Long, String> takeSnapshot(final int numEntries) {
207         Map<Long, String> map = new HashMap<>(numEntries);
208
209         long lastIndex = 0;
210         long lastTerm = 0;
211         for(int i = 0; i < numEntries; i++) {
212             ReplicatedLogEntry entry = replicatedLogImpl.getAtPhysicalIndex(i);
213             map.put(entry.getIndex(), entry.getData().toString());
214             lastIndex = entry.getIndex();
215             lastTerm = entry.getTerm();
216         }
217
218         replicatedLogImpl.snapshotPreCommit(lastIndex, lastTerm);
219         replicatedLogImpl.snapshotCommit();
220
221         return map;
222
223     }
224     class MockAbstractReplicatedLogImpl extends AbstractReplicatedLogImpl {
225         @Override
226         public void appendAndPersist(final ReplicatedLogEntry replicatedLogEntry) {
227         }
228
229         @Override
230         public void removeFromAndPersist(final long index) {
231         }
232
233         @Override
234         public void appendAndPersist(ReplicatedLogEntry replicatedLogEntry, Procedure<ReplicatedLogEntry> callback) {
235         }
236     }
237 }