summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/protocols/implementations/VSRaftProtocol.java11
-rw-r--r--src/test/java/protocols/implementations/VSRaftProtocolTest.java38
2 files changed, 40 insertions, 9 deletions
diff --git a/src/main/java/protocols/implementations/VSRaftProtocol.java b/src/main/java/protocols/implementations/VSRaftProtocol.java
index eaf63a7..6257538 100644
--- a/src/main/java/protocols/implementations/VSRaftProtocol.java
+++ b/src/main/java/protocols/implementations/VSRaftProtocol.java
@@ -33,6 +33,9 @@ public class VSRaftProtocol extends VSAbstractProtocol {
/** The local time when the last heartbeat was observed. */
private long lastHeartbeatTime;
+ /** The randomized local deadline for the next election timeout. */
+ private long electionDeadline;
+
/** PIDs which still have to acknowledge the current operation. */
private ArrayList<Integer> ackPids;
@@ -105,9 +108,9 @@ public class VSRaftProtocol extends VSAbstractProtocol {
* @see protocols.VSAbstractProtocol#onClientSchedule()
*/
public void onClientSchedule() {
- long elapsedSinceHeartbeat = process.getTime() - lastHeartbeatTime;
+ long currentTime = process.getTime();
- if (!isLeader && elapsedSinceHeartbeat >= getLong("electionTimeout")) {
+ if (!isLeader && currentTime >= electionDeadline) {
startElection();
}
}
@@ -137,6 +140,7 @@ public class VSRaftProtocol extends VSAbstractProtocol {
isLeader = false;
isCandidate = false;
lastHeartbeatTime = 0;
+ electionDeadline = 0;
logIndex = 0;
commitIndex = 0;
@@ -183,10 +187,11 @@ public class VSRaftProtocol extends VSAbstractProtocol {
long jitterPercentage = Math.abs(process.getRandomPercentage());
long jitter = (getLong("electionJitter") * jitterPercentage) / 100L;
boolean previousContextIsServer = currentContextIsServer();
+ electionDeadline = process.getTime() + getLong("electionTimeout") + jitter;
currentContextIsServer(false);
removeSchedules();
- scheduleAt(process.getTime() + getLong("electionTimeout") + jitter);
+ scheduleAt(electionDeadline);
currentContextIsServer(previousContextIsServer);
}
diff --git a/src/test/java/protocols/implementations/VSRaftProtocolTest.java b/src/test/java/protocols/implementations/VSRaftProtocolTest.java
index 0ae52ba..8028711 100644
--- a/src/test/java/protocols/implementations/VSRaftProtocolTest.java
+++ b/src/test/java/protocols/implementations/VSRaftProtocolTest.java
@@ -120,7 +120,7 @@ class VSRaftProtocolTest {
}
@Test
- void testOnClientInitSchedulesRandomizedElectionTimeout() {
+ void testOnClientInitSchedulesRandomizedElectionTimeout() throws Exception {
protocol.currentContextIsServer(false);
ArgumentCaptor<VSTask> taskCaptor = ArgumentCaptor.forClass(VSTask.class);
@@ -132,6 +132,7 @@ class VSRaftProtocolTest {
VSTask task = taskCaptor.getValue();
assertEquals(4600L, task.getTaskTime());
+ assertEquals(4600L, getLongField("electionDeadline"));
assertFalse(((VSProtocolScheduleEvent) task.getEvent()).isServerSchedule());
}
@@ -140,7 +141,7 @@ class VSRaftProtocolTest {
protocol.currentContextIsServer(false);
protocol.onClientInit();
clearInvocations(mockProcess, mockTaskManager);
- when(mockProcess.getTime()).thenReturn(4200L);
+ when(mockProcess.getTime()).thenReturn(4700L, 4700L, 4700L);
ArgumentCaptor<VSMessage> messageCaptor =
ArgumentCaptor.forClass(VSMessage.class);
@@ -160,7 +161,8 @@ class VSRaftProtocolTest {
assertFalse(getBooleanField("isLeader"));
assertEquals(1, getIntField("votesReceived"));
assertEquals(7, getIntField("votedFor"));
- assertEquals(8700L, taskCaptor.getValue().getTaskTime());
+ assertEquals(9200L, taskCaptor.getValue().getTaskTime());
+ assertEquals(9200L, getLongField("electionDeadline"));
assertFalse(
((VSProtocolScheduleEvent) taskCaptor.getValue().getEvent())
.isServerSchedule());
@@ -186,14 +188,31 @@ class VSRaftProtocolTest {
}
@Test
+ void testOnClientScheduleDoesNotStartElectionInJitterWindow() throws Exception {
+ protocol.currentContextIsServer(false);
+ protocol.onClientInit();
+ clearInvocations(mockProcess, mockTaskManager);
+ when(mockProcess.getTime()).thenReturn(4500L);
+
+ protocol.onClientSchedule();
+
+ verify(mockProcess, never()).sendMessage(any());
+ verify(mockTaskManager, never()).removeAllTasks(any());
+ verify(mockTaskManager, never()).addTask(any());
+ assertFalse(getBooleanField("isCandidate"));
+ assertEquals(0, getIntField("currentTerm"));
+ assertEquals(4600L, getLongField("electionDeadline"));
+ }
+
+ @Test
void testCandidateTimeoutStartsNewElectionAndReschedules() throws Exception {
protocol.currentContextIsServer(false);
protocol.onClientInit();
- when(mockProcess.getTime()).thenReturn(4200L, 4200L, 4200L);
+ when(mockProcess.getTime()).thenReturn(4700L, 4700L, 4700L);
protocol.onClientSchedule();
clearInvocations(mockProcess, mockTaskManager);
- when(mockProcess.getTime()).thenReturn(8401L, 8401L, 8401L);
+ when(mockProcess.getTime()).thenReturn(9401L, 9401L, 9401L);
ArgumentCaptor<VSMessage> messageCaptor =
ArgumentCaptor.forClass(VSMessage.class);
@@ -210,7 +229,8 @@ class VSRaftProtocolTest {
assertEquals(2, voteRequest.getInteger("term"));
assertEquals(2, getIntField("currentTerm"));
assertEquals(1, getIntField("votesReceived"));
- assertEquals(12901L, taskCaptor.getValue().getTaskTime());
+ assertEquals(13901L, taskCaptor.getValue().getTaskTime());
+ assertEquals(13901L, getLongField("electionDeadline"));
}
@Test
@@ -261,4 +281,10 @@ class VSRaftProtocolTest {
field.setAccessible(true);
return field.getBoolean(protocol);
}
+
+ private long getLongField(String fieldName) throws Exception {
+ Field field = VSRaftProtocol.class.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return field.getLong(protocol);
+ }
}