Пример #1
0
        private async Task Candidacy(CancellationToken c)
        {
            while (_role == Role.Candidate)
            {
                var forMe     = 1; // vote for yourself
                var againstMe = 0;

                var peers     = _peerManager.GetPeers().ToArray();
                var concensus = (peers.Length / 2) + 1;
                var proxies   = peers.Select(x => _peerManager.GetProxy(x.Address));
                var retry     = TheTrace.LogPolicy(_meAsAPeer.ShortName).WaitAndRetryAsync(3, (i) => TimeSpan.FromMilliseconds(i * i * 30));
                var policy    = Policy.TimeoutAsync(_settings.CandidacyTimeout).WrapAsync(retry);
                var request   = new RequestVoteRequest()
                {
                    CandidateId  = State.Id,
                    CurrentTerm  = State.CurrentTerm,
                    LastLogIndex = _logPersister.LastIndex,
                    LastLogTerm  = _logPersister.LastEntryTerm
                };

                var all = await Task.WhenAll(proxies.Select(p => policy.ExecuteAndCaptureAsync(() => p.RequestVoteAsync(request))));

                var maxTerm = 0L;
                foreach (var r in all)
                {
                    if (r.Outcome == OutcomeType.Successful)
                    {
                        if (r.Result.CurrentTerm > maxTerm)
                        {
                            maxTerm = r.Result.CurrentTerm;
                        }

                        if (r.Result != null && r.Result.VoteGranted)
                        {
                            forMe++;
                        }
                        else
                        {
                            againstMe++;
                        }
                    }
                }

                if (againstMe >= concensus)
                {
                    TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Result of the candidacy for term {State.CurrentTerm}. I got rejected with {againstMe} votes :/");
                    BecomeFollower(maxTerm);
                }
                else if (forMe >= concensus)
                {
                    TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Result of the candidacy for term {State.CurrentTerm}. I got elected with {forMe} votes! :)");
                    BecomeLeader();
                }
                else
                {
                    TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Result of the candidacy for term {State.CurrentTerm}. Non-conclusive with {forMe} for me and {againstMe} against me.");
                }
            }
        }
Пример #2
0
        /// <inheritdoc />
        public Task <RequestVoteResponse> RequestVoteAsync(RequestVoteRequest request)
        {
            var peers    = _peerManager.GetPeers();
            var peer     = peers.Where(x => x.Id == request.CandidateId).FirstOrDefault();
            var peerName = peer?.ShortName ?? request.CandidateId.ToString();

            lock (State)
            {
                if (request.CurrentTerm > State.CurrentTerm)
                {
                    BecomeFollower(request.CurrentTerm);
                }

                // Reply false if term < currentTerm
                if (State.CurrentTerm > request.CurrentTerm)
                {
                    TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Rejecting vote of {peerName} due to backward term");
                    return(Task.FromResult(new RequestVoteResponse()
                    {
                        CurrentTerm = State.CurrentTerm,
                        VoteGranted = false
                    }));
                }


                // If votedFor is null or candidateId, and candidate’s log is at least as up-to-date as receiver’s log, grant vote(§5.2, §5.4)
                if (!State.LastVotedForId.HasValue && _logPersister.LastIndex <= request.LastLogIndex)
                {
                    State.LastVotedForId = request.CandidateId;

                    // If election timeout elapses without receiving AppendEntries RPC from current leader OR GRANTING VOTE TO CANDIDATE: convert to candidate
                    _lastHeartbeat.Set();

                    TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Voting for {peerName} for term {request.CurrentTerm}");
                    return(Task.FromResult(new RequestVoteResponse()
                    {
                        CurrentTerm = State.CurrentTerm,
                        VoteGranted = true
                    }));
                }

                TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Rejecting vote of {peerName} for term {request.CurrentTerm} as it did not fulfil");

                // assume the rest we send back no
                return(Task.FromResult(new RequestVoteResponse()
                {
                    CurrentTerm = State.CurrentTerm,
                    VoteGranted = false
                }));
            }
        }