+ @Override
+ public RaftActorBehavior switchBehavior(final RaftActorBehavior behavior) {
+ return internalSwitchBehavior(behavior);
+ }
+
+ protected RaftActorBehavior internalSwitchBehavior(final RaftState newState) {
+ return internalSwitchBehavior(createBehavior(context, newState));
+ }
+
+ @SuppressWarnings("checkstyle:IllegalCatch")
+ protected RaftActorBehavior internalSwitchBehavior(final RaftActorBehavior newBehavior) {
+ if (!context.getRaftPolicy().automaticElectionsEnabled()) {
+ return this;
+ }
+
+ log.info("{} :- Switching from behavior {} to {}, election term: {}", logName(), this.state(),
+ newBehavior.state(), context.getTermInformation().getCurrentTerm());
+ try {
+ close();
+ } catch (RuntimeException e) {
+ log.error("{}: Failed to close behavior : {}", logName(), this.state(), e);
+ }
+ return newBehavior;
+ }
+
+
+ protected int getMajorityVoteCount(final int numPeers) {
+ // Votes are required from a majority of the peers including self.
+ // The numMajority field therefore stores a calculated value
+ // of the number of votes required for this candidate to win an
+ // election based on it's known peers.
+ // If a peer was added during normal operation and raft replicas
+ // came to know about them then the new peer would also need to be
+ // taken into consideration when calculating this value.
+ // Here are some examples for what the numMajority would be for n
+ // peers
+ // 0 peers = 1 numMajority -: (0 + 1) / 2 + 1 = 1
+ // 2 peers = 2 numMajority -: (2 + 1) / 2 + 1 = 2
+ // 4 peers = 3 numMajority -: (4 + 1) / 2 + 1 = 3
+
+ int numMajority = 0;
+ if (numPeers > 0) {
+ int self = 1;
+ numMajority = (numPeers + self) / 2 + 1;
+ }
+ return numMajority;
+
+ }
+
+
+ /**
+ * Performs a snapshot with no capture on the replicated log. It clears the log from the supplied index or
+ * lastApplied-1 which ever is minimum.
+ *
+ * @param snapshotCapturedIndex the index from which to clear
+ */
+ protected void performSnapshotWithoutCapture(final long snapshotCapturedIndex) {
+ long actualIndex = context.getSnapshotManager().trimLog(snapshotCapturedIndex);
+
+ if (actualIndex != -1) {
+ setReplicatedToAllIndex(actualIndex);
+ }
+ }
+
+ protected String getId() {
+ return context.getId();
+ }
+
+ // Check whether we should update the term. In case of half-connected nodes, we want to ignore RequestVote
+ // messages, as the candidate is not able to receive our response.
+ protected boolean shouldUpdateTerm(final RaftRPC rpc) {
+ if (!(rpc instanceof RequestVote)) {
+ return true;
+ }
+
+ final RequestVote requestVote = (RequestVote) rpc;
+ log.debug("{}: Found higher term in RequestVote rpc, verifying whether it's safe to update term.", logName());
+ final Optional<Cluster> maybeCluster = context.getCluster();
+ if (!maybeCluster.isPresent()) {
+ return true;
+ }
+
+ final Cluster cluster = maybeCluster.get();
+
+ final Set<Member> unreachable = cluster.state().getUnreachable();
+ log.debug("{}: Cluster state: {}", logName(), unreachable);
+
+ for (Member member : unreachable) {
+ for (String role : member.getRoles()) {
+ if (requestVote.getCandidateId().startsWith(role)) {
+ log.debug("{}: Unreachable member: {}, matches candidateId in: {}, not updating term", logName(),
+ member, requestVote);
+ return false;
+ }
+ }
+ }
+
+ log.debug("{}: Candidate in requestVote:{} with higher term appears reachable, updating term.", logName(),
+ requestVote);
+ return true;