private void StartElection()
		{
			LastHeartbeatTime = DateTime.UtcNow;
			_votesForMyLeadership.Clear();

			long currentTerm = Engine.PersistentState.CurrentTerm + 1;
			if (_wonTrialElection || _termIncreaseMightGetMyVote) // only in the real election (or if we have to), we increment the current term 
			{
				Engine.PersistentState.UpdateTermTo(Engine, currentTerm);
			}
			if (_wonTrialElection)// and only if we won an election do we record a firm vote for ourselves
				Engine.PersistentState.RecordVoteFor(Engine.Name, currentTerm);

			_termIncreaseMightGetMyVote = false;

			Engine.CurrentLeader = null;
			_log.Info("Calling for {0} election in term {1}",
				_wonTrialElection ? "an" : "a trial", currentTerm);

			var lastLogEntry = Engine.PersistentState.LastLogEntry();
			var rvr = new RequestVoteRequest
			{
				LastLogIndex = lastLogEntry.Index,
				LastLogTerm = lastLogEntry.Term,
				Term = currentTerm,
				From = Engine.Name,
				ClusterTopologyId = Engine.CurrentTopology.TopologyId,
				TrialOnly = _wonTrialElection == false,
				ForcedElection = _forcedElection
			};

			var allVotingNodes = Engine.CurrentTopology.AllVotingNodes;

			// don't send to yourself the message
			foreach (var votingPeer in allVotingNodes)
			{
				if (votingPeer.Name == Engine.Name)
					continue;
				Engine.Transport.Send(votingPeer, rvr);
			}

			Engine.OnCandidacyAnnounced();
			_log.Info("Voting for myself in {1}election for term {0}", currentTerm, _wonTrialElection ? " " : " trial ");
			Handle(new RequestVoteResponse
			{
				CurrentTerm = Engine.PersistentState.CurrentTerm,
				VoteGranted = true,
				Message = String.Format("{0} -> Voting for myself", Engine.Name),
				From = Engine.Name,
				ClusterTopologyId = Engine.CurrentTopology.TopologyId,
				VoteTerm = currentTerm,
				TrialOnly = _wonTrialElection == false
			});
		}
		public RequestVoteResponse Handle(RequestVoteRequest req)
		{
            if (FromOurTopology(req) == false)
            {
                _log.Info("Got a request vote message outside my cluster topology (id: {0}), ignoring", req.ClusterTopologyId);
                return new RequestVoteResponse
                {
                    VoteGranted = false,
                    CurrentTerm = Engine.PersistentState.CurrentTerm,
                    VoteTerm = req.Term,
                    Message = "Cannot vote for a node outside my topology, my topology id is: " + Engine.CurrentTopology.TopologyId,
                    From = Engine.Name,
                    ClusterTopologyId = Engine.CurrentTopology.TopologyId,
                    TrialOnly = req.TrialOnly,
                    TermIncreaseMightGetMyVote = false
                };
            }

			//disregard RequestVoteRequest if this node receives regular messages and the leader is known
			// Raft paper section 6 (cluster membership changes), this apply only if we are a follower, because
			// candidate and leaders both generate their own heartbeat messages
			var timeSinceLastHeartbeat = (DateTime.UtcNow - LastMessageTime).TotalMilliseconds;

			var halfTimeout = (long)(Timeout / 2);
			if (State == RaftEngineState.Follower && req.ForcedElection == false &&
				(timeSinceLastHeartbeat < halfTimeout) && Engine.CurrentLeader != null)
			{
				_log.Info("Received RequestVoteRequest from a node within election timeout while leader exists, rejecting " );
				return new RequestVoteResponse
				{
					VoteGranted = false,
					CurrentTerm = Engine.PersistentState.CurrentTerm,
					VoteTerm = req.Term,
					Message = "I currently have a leader and I am receiving heartbeats within election timeout.",
					From = Engine.Name,
					ClusterTopologyId = Engine.CurrentTopology.TopologyId,
					TrialOnly = req.TrialOnly,
					TermIncreaseMightGetMyVote = false
				};
			}

			if (Engine.CurrentTopology.IsVoter(req.From) == false 
				// if it isn't in my cluster, but we don't have any voters, than we are probably a new node, so we'll accept this 
				// and allow it to become our leader
				&& Engine.CurrentTopology.HasVoters) 
			{
				_log.Info("Received RequestVoteRequest from a node that isn't a voting member in the cluster: {0}, rejecting", req.From);
				return new RequestVoteResponse
				{
					VoteGranted = false,
					CurrentTerm = Engine.PersistentState.CurrentTerm,
					VoteTerm = req.Term,
					Message = "You are not a memeber in my cluster, and cannot be a leader",
					From = Engine.Name,
					ClusterTopologyId = Engine.CurrentTopology.TopologyId,
					TrialOnly = req.TrialOnly,
					TermIncreaseMightGetMyVote = false
				};
			}

			_log.Debug("Received RequestVoteRequest, req.CandidateId = {0}, term = {1}", req.From, req.Term);

			if (req.Term < Engine.PersistentState.CurrentTerm)
			{
				var msg = string.Format("Rejecting request vote because term {0} is lower than current term {1}",
					req.Term, Engine.PersistentState.CurrentTerm);
				_log.Info(msg);
				return new RequestVoteResponse
				{
					VoteGranted = false,
					CurrentTerm = Engine.PersistentState.CurrentTerm,
					VoteTerm = req.Term,
					Message = msg,
					From = Engine.Name,
					TrialOnly = req.TrialOnly,
					ClusterTopologyId = Engine.CurrentTopology.TopologyId,
					TermIncreaseMightGetMyVote = false
				};
			}

			if (req.Term > Engine.PersistentState.CurrentTerm && req.TrialOnly == false)
			{
				Engine.UpdateCurrentTerm(req.Term, null);
			}

			if (Engine.PersistentState.VotedFor != null && Engine.PersistentState.VotedFor != req.From &&
				Engine.PersistentState.VotedForTerm >= req.Term)
			{
				var msg = string.Format("Rejecting request vote because already voted for {0} in term {1}",
					Engine.PersistentState.VotedFor, req.Term);

				_log.Info(msg);
				return new RequestVoteResponse
				{
					VoteGranted = false,
					CurrentTerm = Engine.PersistentState.CurrentTerm,
					VoteTerm = req.Term,
					Message = msg,
					From = Engine.Name,
					ClusterTopologyId = Engine.CurrentTopology.TopologyId,
					TrialOnly = req.TrialOnly,
					TermIncreaseMightGetMyVote = true
				};
			}

			if (Engine.LogIsUpToDate(req.LastLogTerm, req.LastLogIndex) == false)
			{
				var msg = string.Format("Rejecting request vote because remote log for {0} in not up to date.", req.From);
				_log.Info(msg);
				return new RequestVoteResponse

				{
					VoteGranted = false,
					CurrentTerm = Engine.PersistentState.CurrentTerm,
					VoteTerm = req.Term,
					Message = msg,
					From = Engine.Name,
					ClusterTopologyId = Engine.CurrentTopology.TopologyId,
					TrialOnly = req.TrialOnly,
					TermIncreaseMightGetMyVote = false
				};
			}
			
			if (req.TrialOnly == false)
			{
				// we said we would be voting for this guy, so we can give it a full election timeout, 
				// by treating this as a heart beat. This means we won't be timing out ourselves and trying
				// to become the leader
				LastHeartbeatTime = DateTime.UtcNow;
				LastMessageTime = DateTime.UtcNow;

				_log.Info("Recording vote for candidate = {0}", req.From);
				Engine.PersistentState.RecordVoteFor(req.From, req.Term);
			}
			else
			{
				_log.Info("Voted for candidate = {0} in trial election for term {1}", req.From, req.Term);
			}
			return new RequestVoteResponse
			{
				VoteGranted = true,
				CurrentTerm = Engine.PersistentState.CurrentTerm,
				VoteTerm = req.Term,
				Message = "Vote granted",
				From = Engine.Name,
				ClusterTopologyId = Engine.CurrentTopology.TopologyId,
				TrialOnly = req.TrialOnly,
				TermIncreaseMightGetMyVote = false
			};
		}
示例#3
0
		public void Send(NodeConnectionInfo dest, RequestVoteRequest req)
		{
			_sender.Send(dest, req);
		}
示例#4
0
		public void Send(NodeConnectionInfo dest, RequestVoteRequest req)
		{
			HttpClient client;
			using (GetConnection(dest, out client))
			{
				LogStatus("request vote from " + dest, async () =>
				{
					var requestUri = string.Format("raft/requestVote?term={0}&lastLogIndex={1}&lastLogTerm={2}&trialOnly={3}&forcedElection={4}&from={5}&clusterTopologyId={6}", 
						req.Term, req.LastLogIndex, req.LastLogTerm, req.TrialOnly, req.ForcedElection, req.From, req.ClusterTopologyId);
					var httpResponseMessage = await client.GetAsync(requestUri);
					var reply = await httpResponseMessage.Content.ReadAsStringAsync();
					if (httpResponseMessage.IsSuccessStatusCode == false && httpResponseMessage.StatusCode != HttpStatusCode.NotAcceptable)
					{
						_log.Warn("Error requesting vote from {0}. Status: {1}\r\n{2}", dest.Name, httpResponseMessage.StatusCode, reply);
						return;
					}
					var requestVoteResponse = JsonConvert.DeserializeObject<RequestVoteResponse>(reply);
					SendToSelf(requestVoteResponse);
				});
			}
		}