/// <summary> /// Node receives answer votes (to become Leader) from other nodes. /// Is called from tryCatch and in lock /// </summary> /// <param name="data"></param> void ParseVoteOfCandidate(NodeAddress address, byte[] data) { //Node received a node var vote = VoteOfCandidate.BiserDecode(data); var termState = CompareCurrentTermWithIncoming(vote.TermId); if (this.NodeState != eNodeState.Candidate) { return; } switch (vote.VoteType) { case VoteOfCandidate.eVoteType.VoteFor: //Calculating if node has Majority of //VotesQuantity++; VotesQuantity.Add(address.EndPointSID); if ((VotesQuantity.Count + 1) >= this.GetMajorityQuantity()) { //Majority //Node becomes a Leader this.NodeState = eNodeState.Leader; this.NodeStateLog.FlushSleCache(); this.NodeStateLog.ClearLogAcceptance(); this.NodeStateLog.ClearLogEntryForDistribution(); VerbosePrint("Node {0} state is {1} _ParseVoteOfCandidate", NodeAddress.NodeAddressId, this.NodeState); VerbosePrint("Node {0} is Leader **********************************************", NodeAddress.NodeAddressId); //Stopping timers this.RemoveElectionTimer(); this.RemoveLeaderHeartbeatWaitingTimer(); /* * It's possible that we receive higher term from another leader * (in case if this leader was disconnected for some seconds from the network, * other leader can be elected and it will definitely have higher Term, so every Leader node must be ready to it) */ this.RunLeaderTimer(); } //else //{ // //Accumulating voices // //Do nothing //} break; case VoteOfCandidate.eVoteType.VoteReject: //Do nothing break; } }
/// <summary> /// /// </summary> void SetNodeFollower() { if (this.NodeState != eNodeState.Follower) { VerbosePrint("Node {0} state is {1} of {2}", NodeAddress.NodeAddressId, this.NodeState, this.LeaderNodeAddress?.NodeAddressId); } this.NodeState = eNodeState.Follower; this.NodeStateLog.LeaderSynchronizationIsActive = false; //Removing timers RemoveElectionTimer(); RemoveLeaderHeartbeatWaitingTimer(); RemoveLeaderTimer(); RemoveLeaderLogResendTimer(); //Starting Leaderheartbeat RunLeaderHeartbeatWaitingTimer(); }
/// <summary> /// Stops the node /// </summary> public void NodeStop() { lock (lock_Operations) { RemoveElectionTimer(); RemoveLeaderHeartbeatWaitingTimer(); RemoveLeaderTimer(); RemoveDelayedPersistenceTimer(); RemoveNoLeaderAddCommandTimer(); this.NodeState = eNodeState.Follower; this.NodeStateLog.LeaderSynchronizationIsActive = false; this.NodeStateLog.FlushSleCache(); VerbosePrint("Node {0} state is {1}", NodeAddress.NodeAddressId, this.NodeState); IsRunning = false; } }
/// <summary> /// Time to become a candidate /// </summary> /// <param name="userToken"></param> void ElectionTimeout(object userToken) { CandidateRequest req = null; try { lock (lock_Operations) { if (Election_TimerId == 0) //Timer was switched off and we don't need to run it again { return; } Election_TimerId = 0; if (this.NodeState == eNodeState.Leader) { return; } VerbosePrint("Node {0} election timeout", NodeAddress.NodeAddressId); this.NodeState = eNodeState.Candidate; this.LeaderNodeAddress = null; VerbosePrint("Node {0} state is {1} _ElectionTimeout", NodeAddress.NodeAddressId, this.NodeState); //Voting for self //VotesQuantity = 1; VotesQuantity.Clear(); //Increasing local term number NodeTerm++; req = new CandidateRequest() { TermId = this.NodeTerm, LastLogId = NodeStateLog.StateLogId, LastTermId = NodeStateLog.StateLogTerm }; //send to all was here //Setting up new Election Timer RunElectionTimer(); } this.Sender.SendToAll(eRaftSignalType.CandidateRequest, req.SerializeBiser(), this.NodeAddress, entitySettings.EntityName); } catch (Exception ex) { Log.Log(new WarningLogEntry() { Exception = ex, Method = "Raft.RaftNode.ElectionTimeout" }); } }