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;
}
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());
}
}
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");
}
}
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());
}
}
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;
}
Aggregations