Search in sources :

Example 6 with DefaultRaftMember

use of io.atomix.protocols.raft.cluster.impl.DefaultRaftMember in project atomix by atomix.

the class AbstractRole method forward.

/**
 * Forwards the given request to the leader if possible.
 */
protected <T extends RaftRequest, U extends RaftResponse> CompletableFuture<U> forward(T request, BiFunction<NodeId, T, CompletableFuture<U>> function) {
    CompletableFuture<U> future = new CompletableFuture<>();
    DefaultRaftMember leader = raft.getLeader();
    if (leader == null) {
        return Futures.exceptionalFuture(new RaftException.NoLeader("No leader found"));
    }
    function.apply(leader.nodeId(), request).whenCompleteAsync((response, error) -> {
        if (error == null) {
            future.complete(response);
        } else {
            future.completeExceptionally(error);
        }
    }, raft.getThreadContext());
    return future;
}
Also used : CompletableFuture(java.util.concurrent.CompletableFuture) RaftException(io.atomix.protocols.raft.RaftException) DefaultRaftMember(io.atomix.protocols.raft.cluster.impl.DefaultRaftMember)

Example 7 with DefaultRaftMember

use of io.atomix.protocols.raft.cluster.impl.DefaultRaftMember 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)

Example 8 with DefaultRaftMember

use of io.atomix.protocols.raft.cluster.impl.DefaultRaftMember in project atomix by atomix.

the class RaftContext method setLastVotedFor.

/**
 * Sets the state last voted for candidate.
 *
 * @param candidate The candidate that was voted for.
 */
public void setLastVotedFor(NodeId candidate) {
    // If we've already voted for another candidate in this term then the last voted for candidate cannot be overridden.
    checkState(!(lastVotedFor != null && candidate != null), "Already voted for another candidate");
    DefaultRaftMember member = cluster.getMember(candidate);
    checkState(member != null, "Unknown candidate: %d", candidate);
    this.lastVotedFor = candidate;
    meta.storeVote(this.lastVotedFor);
    if (candidate != null) {
        log.debug("Voted for {}", member.nodeId());
    } else {
        log.trace("Reset last voted for");
    }
}
Also used : DefaultRaftMember(io.atomix.protocols.raft.cluster.impl.DefaultRaftMember)

Example 9 with DefaultRaftMember

use of io.atomix.protocols.raft.cluster.impl.DefaultRaftMember in project atomix by atomix.

the class FollowerRole method sendPollRequests.

/**
 * Polls all members of the cluster to determine whether this member should transition to the CANDIDATE state.
 */
private void sendPollRequests() {
    // Set a new timer within which other nodes must respond in order for this node to transition to candidate.
    heartbeatTimer = raft.getThreadContext().schedule(raft.getElectionTimeout(), () -> {
        log.debug("Failed to poll a majority of the cluster in {}", raft.getElectionTimeout());
        resetHeartbeatTimeout();
    });
    // Create a quorum that will track the number of nodes that have responded to the poll request.
    final AtomicBoolean complete = new AtomicBoolean();
    final Set<DefaultRaftMember> votingMembers = raft.getCluster().getActiveMemberStates().stream().map(RaftMemberContext::getMember).collect(Collectors.toSet());
    // If there are no other members in the cluster, immediately transition to leader.
    if (votingMembers.isEmpty()) {
        raft.transition(RaftServer.Role.CANDIDATE);
        return;
    }
    final Quorum quorum = new Quorum(raft.getCluster().getQuorum(), (elected) -> {
        // If a majority of the cluster indicated they would vote for us then transition to candidate.
        complete.set(true);
        if (elected) {
            raft.transition(RaftServer.Role.CANDIDATE);
        } else {
            resetHeartbeatTimeout();
        }
    });
    // 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("Polling members {}", votingMembers);
    // of the cluster and vote each member for a vote.
    for (DefaultRaftMember member : votingMembers) {
        log.debug("Polling {} for next term {}", member, raft.getTerm() + 1);
        PollRequest request = PollRequest.builder().withTerm(raft.getTerm()).withCandidate(raft.getCluster().getMember().nodeId()).withLastLogIndex(lastEntry != null ? lastEntry.index() : 0).withLastLogTerm(lastTerm).build();
        raft.getProtocol().poll(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()) {
                        raft.setTerm(response.term());
                    }
                    if (!response.accepted()) {
                        log.debug("Received rejected poll from {}", member);
                        quorum.fail();
                    } else if (response.term() != raft.getTerm()) {
                        log.debug("Received accepted poll for a different term from {}", member);
                        quorum.fail();
                    } else {
                        log.debug("Received accepted poll 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) PollRequest(io.atomix.protocols.raft.protocol.PollRequest) RaftLogEntry(io.atomix.protocols.raft.storage.log.entry.RaftLogEntry)

Example 10 with DefaultRaftMember

use of io.atomix.protocols.raft.cluster.impl.DefaultRaftMember in project atomix by atomix.

the class LeaderRole method onReconfigure.

@Override
public CompletableFuture<ReconfigureResponse> onReconfigure(final ReconfigureRequest request) {
    raft.checkThread();
    logRequest(request);
    // See https://groups.google.com/forum/#!topic/raft-dev/t4xj6dJTP6E
    if (configuring() || initializing()) {
        return CompletableFuture.completedFuture(logResponse(ReconfigureResponse.builder().withStatus(RaftResponse.Status.ERROR).build()));
    }
    // If the member is not a known member of the cluster, fail the promotion.
    DefaultRaftMember existingMember = raft.getCluster().getMember(request.member().nodeId());
    if (existingMember == null) {
        return CompletableFuture.completedFuture(logResponse(ReconfigureResponse.builder().withStatus(RaftResponse.Status.ERROR).withError(RaftError.Type.UNKNOWN_SESSION).build()));
    }
    // the leader, fail the request to ensure servers can't reconfigure an old configuration.
    if (request.index() > 0 && request.index() < raft.getCluster().getConfiguration().index() || request.term() != raft.getCluster().getConfiguration().term()) {
        return CompletableFuture.completedFuture(logResponse(ReconfigureResponse.builder().withStatus(RaftResponse.Status.ERROR).withError(RaftError.Type.CONFIGURATION_ERROR).build()));
    }
    // If the member type has not changed, complete the configuration change successfully.
    if (existingMember.getType() == request.member().getType()) {
        Configuration configuration = raft.getCluster().getConfiguration();
        return CompletableFuture.completedFuture(logResponse(ReconfigureResponse.builder().withStatus(RaftResponse.Status.OK).withIndex(configuration.index()).withTerm(raft.getCluster().getConfiguration().term()).withTime(raft.getCluster().getConfiguration().time()).withMembers(configuration.members()).build()));
    }
    // Update the member type.
    existingMember.update(request.member().getType(), Instant.now());
    Collection<RaftMember> members = raft.getCluster().getMembers();
    CompletableFuture<ReconfigureResponse> future = new CompletableFuture<>();
    configure(members).whenComplete((index, error) -> {
        if (error == null) {
            future.complete(logResponse(ReconfigureResponse.builder().withStatus(RaftResponse.Status.OK).withIndex(index).withTerm(raft.getCluster().getConfiguration().term()).withTime(raft.getCluster().getConfiguration().time()).withMembers(members).build()));
        } else {
            future.complete(logResponse(ReconfigureResponse.builder().withStatus(RaftResponse.Status.ERROR).withError(RaftError.Type.PROTOCOL_ERROR).build()));
        }
    });
    return future;
}
Also used : RaftMember(io.atomix.protocols.raft.cluster.RaftMember) DefaultRaftMember(io.atomix.protocols.raft.cluster.impl.DefaultRaftMember) CompletableFuture(java.util.concurrent.CompletableFuture) DefaultRaftMember(io.atomix.protocols.raft.cluster.impl.DefaultRaftMember) Configuration(io.atomix.protocols.raft.storage.system.Configuration) ReconfigureResponse(io.atomix.protocols.raft.protocol.ReconfigureResponse)

Aggregations

DefaultRaftMember (io.atomix.protocols.raft.cluster.impl.DefaultRaftMember)10 RaftLogEntry (io.atomix.protocols.raft.storage.log.entry.RaftLogEntry)4 CompletableFuture (java.util.concurrent.CompletableFuture)3 RaftMember (io.atomix.protocols.raft.cluster.RaftMember)2 RaftLogReader (io.atomix.protocols.raft.storage.log.RaftLogReader)2 Quorum (io.atomix.protocols.raft.utils.Quorum)2 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)2 RaftException (io.atomix.protocols.raft.RaftException)1 RaftMemberContext (io.atomix.protocols.raft.cluster.impl.RaftMemberContext)1 AppendRequest (io.atomix.protocols.raft.protocol.AppendRequest)1 InstallRequest (io.atomix.protocols.raft.protocol.InstallRequest)1 JoinResponse (io.atomix.protocols.raft.protocol.JoinResponse)1 PollRequest (io.atomix.protocols.raft.protocol.PollRequest)1 ReconfigureResponse (io.atomix.protocols.raft.protocol.ReconfigureResponse)1 VoteRequest (io.atomix.protocols.raft.protocol.VoteRequest)1 SnapshotReader (io.atomix.protocols.raft.storage.snapshot.SnapshotReader)1 Configuration (io.atomix.protocols.raft.storage.system.Configuration)1 Duration (java.time.Duration)1 ArrayList (java.util.ArrayList)1 HashSet (java.util.HashSet)1