+ private RaftActorBehavior handleElectionTimeout(Object message) {
+ // If the message is ElectionTimeout, verify we haven't actually seen a message from the leader
+ // during the election timeout interval. It may that the election timer expired b/c this actor
+ // was busy and messages got delayed, in which case leader messages would be backed up in the
+ // queue but would be processed before the ElectionTimeout message and thus would restart the
+ // lastLeaderMessageTimer.
+ long lastLeaderMessageInterval = lastLeaderMessageTimer.elapsed(TimeUnit.MILLISECONDS);
+ boolean noLeaderMessageReceived = !lastLeaderMessageTimer.isRunning() || lastLeaderMessageInterval >=
+ context.getConfigParams().getElectionTimeOutInterval().toMillis();
+
+ if(canStartElection()) {
+ if(message instanceof TimeoutNow || noLeaderMessageReceived) {
+ LOG.debug("{}: Received {} - switching to Candidate", logName(), message.getClass().getSimpleName());
+ return internalSwitchBehavior(RaftState.Candidate);
+ } else {
+ LOG.debug("{}: Received ElectionTimeout but lastLeaderMessageInterval {} < election timeout",
+ logName(), lastLeaderMessageInterval);
+ scheduleElection(electionDuration());
+ }
+ } else if(message instanceof ElectionTimeout) {
+ if(noLeaderMessageReceived) {
+ setLeaderId(null);
+ }
+
+ scheduleElection(electionDuration());
+ }
+
+ return this;
+ }
+