Search in sources :

Example 1 with VoteRequest

use of io.atomix.protocols.raft.protocol.VoteRequest in project atomix by atomix.

the class CandidateRole method sendVoteRequests.

/**
 * Resets the election timer.
 */
private void sendVoteRequests() {
    raft.checkThread();
    // simply skip the election.
    if (!isRunning()) {
        return;
    }
    // Cancel the current timer task and purge the election timer of cancelled tasks.
    if (currentTimer != null) {
        currentTimer.cancel();
    }
    // When the election timer is reset, increment the current term and
    // restart the election.
    raft.setTerm(raft.getTerm() + 1);
    raft.setLastVotedFor(raft.getCluster().getMember().nodeId());
    final AtomicBoolean complete = new AtomicBoolean();
    final Set<DefaultRaftMember> votingMembers = new HashSet<>(raft.getCluster().getActiveMemberStates().stream().map(RaftMemberContext::getMember).collect(Collectors.toList()));
    // If there are no other members in the cluster, immediately transition to leader.
    if (votingMembers.isEmpty()) {
        log.debug("Single member cluster. Transitioning directly to leader.", raft.getCluster().getMember().nodeId());
        raft.transition(RaftServer.Role.LEADER);
        return;
    }
    // Send vote requests to all nodes. The vote request that is sent
    // to this node will be automatically successful.
    // First check if the quorum is null. If the quorum isn't null then that
    // indicates that another vote is already going on.
    final Quorum quorum = new Quorum(raft.getCluster().getQuorum(), (elected) -> {
        if (!isRunning()) {
            return;
        }
        complete.set(true);
        if (elected) {
            raft.transition(RaftServer.Role.LEADER);
        } else {
            raft.transition(RaftServer.Role.FOLLOWER);
        }
    });
    Duration delay = raft.getElectionTimeout().plus(Duration.ofMillis(random.nextInt((int) raft.getElectionTimeout().toMillis())));
    currentTimer = raft.getThreadContext().schedule(delay, () -> {
        if (!complete.get()) {
            // When the election times out, clear the previous majority vote
            // check and restart the election.
            log.debug("Election timed out");
            quorum.cancel();
            sendVoteRequests();
            log.debug("Restarted election");
        }
    });
    // First, load the last log entry to get its term. We load the entry
    // by its index since the index is required by the protocol.
    final Indexed<RaftLogEntry> lastEntry = raft.getLogWriter().getLastEntry();
    final long lastTerm;
    if (lastEntry != null) {
        lastTerm = lastEntry.entry().term();
    } else {
        lastTerm = 0;
    }
    log.debug("Requesting votes for term {}", raft.getTerm());
    // of the cluster and vote each member for a vote.
    for (DefaultRaftMember member : votingMembers) {
        log.debug("Requesting vote from {} for term {}", member, raft.getTerm());
        VoteRequest request = VoteRequest.builder().withTerm(raft.getTerm()).withCandidate(raft.getCluster().getMember().nodeId()).withLastLogIndex(lastEntry != null ? lastEntry.index() : 0).withLastLogTerm(lastTerm).build();
        raft.getProtocol().vote(member.nodeId(), request).whenCompleteAsync((response, error) -> {
            raft.checkThread();
            if (isRunning() && !complete.get()) {
                if (error != null) {
                    log.warn(error.getMessage());
                    quorum.fail();
                } else {
                    if (response.term() > raft.getTerm()) {
                        log.debug("Received greater term from {}", member);
                        raft.setTerm(response.term());
                        complete.set(true);
                        raft.transition(RaftServer.Role.FOLLOWER);
                    } else if (!response.voted()) {
                        log.debug("Received rejected vote from {}", member);
                        quorum.fail();
                    } else if (response.term() != raft.getTerm()) {
                        log.debug("Received successful vote for a different term from {}", member);
                        quorum.fail();
                    } else {
                        log.debug("Received successful vote from {}", member);
                        quorum.succeed();
                    }
                }
            }
        }, raft.getThreadContext());
    }
}
Also used : AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Quorum(io.atomix.protocols.raft.utils.Quorum) DefaultRaftMember(io.atomix.protocols.raft.cluster.impl.DefaultRaftMember) RaftMemberContext(io.atomix.protocols.raft.cluster.impl.RaftMemberContext) VoteRequest(io.atomix.protocols.raft.protocol.VoteRequest) Duration(java.time.Duration) HashSet(java.util.HashSet) RaftLogEntry(io.atomix.protocols.raft.storage.log.entry.RaftLogEntry)

Aggregations

DefaultRaftMember (io.atomix.protocols.raft.cluster.impl.DefaultRaftMember)1 RaftMemberContext (io.atomix.protocols.raft.cluster.impl.RaftMemberContext)1 VoteRequest (io.atomix.protocols.raft.protocol.VoteRequest)1 RaftLogEntry (io.atomix.protocols.raft.storage.log.entry.RaftLogEntry)1 Quorum (io.atomix.protocols.raft.utils.Quorum)1 Duration (java.time.Duration)1 HashSet (java.util.HashSet)1 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)1