use of io.atomix.protocols.raft.cluster.impl.DefaultRaftMember in project atomix by atomix.
the class LeaderRole method onJoin.
@Override
public CompletableFuture<JoinResponse> onJoin(final JoinRequest request) {
raft.checkThread();
logRequest(request);
// See https://groups.google.com/forum/#!topic/raft-dev/t4xj6dJTP6E
if (configuring() || initializing()) {
return CompletableFuture.completedFuture(logResponse(JoinResponse.builder().withStatus(RaftResponse.Status.ERROR).build()));
}
// If the member is already a known member of the cluster, complete the join successfully.
if (raft.getCluster().getMember(request.member().nodeId()) != null) {
return CompletableFuture.completedFuture(logResponse(JoinResponse.builder().withStatus(RaftResponse.Status.OK).withIndex(raft.getCluster().getConfiguration().index()).withTerm(raft.getCluster().getConfiguration().term()).withTime(raft.getCluster().getConfiguration().time()).withMembers(raft.getCluster().getMembers()).build()));
}
RaftMember member = request.member();
// Add the joining member to the members list. If the joining member's type is ACTIVE, join the member in the
// PROMOTABLE state to allow it to get caught up without impacting the quorum size.
Collection<RaftMember> members = raft.getCluster().getMembers();
members.add(new DefaultRaftMember(member.nodeId(), member.getType(), Instant.now()));
CompletableFuture<JoinResponse> future = new CompletableFuture<>();
configure(members).whenComplete((index, error) -> {
if (error == null) {
future.complete(logResponse(JoinResponse.builder().withStatus(RaftResponse.Status.OK).withIndex(index).withTerm(raft.getCluster().getConfiguration().term()).withTime(raft.getCluster().getConfiguration().time()).withMembers(members).build()));
} else {
future.complete(logResponse(JoinResponse.builder().withStatus(RaftResponse.Status.ERROR).withError(RaftError.Type.PROTOCOL_ERROR).build()));
}
});
return future;
}
use of io.atomix.protocols.raft.cluster.impl.DefaultRaftMember in project atomix by atomix.
the class RaftContext method setLeader.
/**
* Sets the state leader.
*
* @param leader The state leader.
*/
public void setLeader(NodeId leader) {
if (!Objects.equals(this.leader, leader)) {
if (leader == null) {
this.leader = null;
} else {
// If a valid leader ID was specified, it must be a member that's currently a member of the
// ACTIVE members configuration. Note that we don't throw exceptions for unknown members. It's
// possible that a failure following a configuration change could result in an unknown leader
// sending AppendRequest to this server. Simply configure the leader if it's known.
DefaultRaftMember member = cluster.getMember(leader);
if (member != null) {
this.leader = leader;
log.info("Found leader {}", member.nodeId());
electionListeners.forEach(l -> l.accept(member));
}
}
this.lastVotedFor = null;
meta.storeVote(null);
}
}
use of io.atomix.protocols.raft.cluster.impl.DefaultRaftMember in project atomix by atomix.
the class AbstractAppender method buildAppendEntriesRequest.
/**
* Builds a populated AppendEntries request.
*/
@SuppressWarnings("unchecked")
protected AppendRequest buildAppendEntriesRequest(RaftMemberContext member, long lastIndex) {
final RaftLogReader reader = member.getLogReader();
final Indexed<RaftLogEntry> prevEntry = reader.getCurrentEntry();
final DefaultRaftMember leader = raft.getLeader();
AppendRequest.Builder builder = AppendRequest.builder().withTerm(raft.getTerm()).withLeader(leader != null ? leader.nodeId() : null).withPrevLogIndex(prevEntry != null ? prevEntry.index() : reader.getFirstIndex() - 1).withPrevLogTerm(prevEntry != null ? prevEntry.entry().term() : 0).withCommitIndex(raft.getCommitIndex());
// Build a list of entries to send to the member.
final List<RaftLogEntry> entries = new ArrayList<>();
// Build a list of entries up to the MAX_BATCH_SIZE. Note that entries in the log may
// be null if they've been compacted and the member to which we're sending entries is just
// joining the cluster or is otherwise far behind. Null entries are simply skipped and not
// counted towards the size of the batch.
// If there exists an entry in the log with size >= MAX_BATCH_SIZE the logic ensures that
// entry will be sent in a batch of size one
int size = 0;
// Iterate through the log until the last index or the end of the log is reached.
while (reader.hasNext()) {
// Otherwise, read the next entry and add it to the batch.
Indexed<RaftLogEntry> entry = reader.next();
entries.add(entry.entry());
size += entry.size();
if (entry.index() == lastIndex || size >= MAX_BATCH_SIZE) {
break;
}
}
// Add the entries to the request builder and build the request.
return builder.withEntries(entries).build();
}
use of io.atomix.protocols.raft.cluster.impl.DefaultRaftMember in project atomix by atomix.
the class AbstractAppender method buildInstallRequest.
/**
* Builds an install request for the given member.
*/
protected InstallRequest buildInstallRequest(RaftMemberContext member, Snapshot snapshot) {
if (member.getNextSnapshotIndex() != snapshot.index()) {
member.setNextSnapshotIndex(snapshot.index());
member.setNextSnapshotOffset(0);
}
InstallRequest request;
synchronized (snapshot) {
// Open a new snapshot reader.
try (SnapshotReader reader = snapshot.openReader()) {
// Skip to the next batch of bytes according to the snapshot chunk size and current offset.
reader.skip(member.getNextSnapshotOffset() * MAX_BATCH_SIZE);
byte[] data = new byte[Math.min(MAX_BATCH_SIZE, reader.remaining())];
reader.read(data);
// Create the install request, indicating whether this is the last chunk of data based on the number
// of bytes remaining in the buffer.
DefaultRaftMember leader = raft.getLeader();
request = InstallRequest.builder().withTerm(raft.getTerm()).withLeader(leader != null ? leader.nodeId() : null).withIndex(snapshot.index()).withOffset(member.getNextSnapshotOffset()).withData(data).withComplete(!reader.hasRemaining()).build();
}
}
return request;
}
use of io.atomix.protocols.raft.cluster.impl.DefaultRaftMember in project atomix by atomix.
the class AbstractAppender method buildAppendEmptyRequest.
/**
* Builds an empty AppendEntries request.
* <p>
* Empty append requests are used as heartbeats to followers.
*/
protected AppendRequest buildAppendEmptyRequest(RaftMemberContext member) {
final RaftLogReader reader = member.getLogReader();
// Read the previous entry from the reader.
// The reader can be null for RESERVE members.
Indexed<RaftLogEntry> prevEntry = reader != null ? reader.getCurrentEntry() : null;
DefaultRaftMember leader = raft.getLeader();
return AppendRequest.builder().withTerm(raft.getTerm()).withLeader(leader != null ? leader.nodeId() : null).withPrevLogIndex(prevEntry != null ? prevEntry.index() : reader != null ? reader.getFirstIndex() - 1 : 0).withPrevLogTerm(prevEntry != null ? prevEntry.entry().term() : 0).withEntries(Collections.emptyList()).withCommitIndex(raft.getCommitIndex()).build();
}
Aggregations