use of io.atomix.protocols.raft.cluster.RaftMember in project atomix by atomix.
the class RaftClusterContext method bootstrap.
@Override
public CompletableFuture<Void> bootstrap(Collection<NodeId> cluster) {
if (joinFuture != null)
return joinFuture;
if (configuration == null) {
member.setType(RaftMember.Type.ACTIVE);
// Create a set of active members.
Set<RaftMember> activeMembers = cluster.stream().filter(m -> !m.equals(member.nodeId())).map(m -> new DefaultRaftMember(m, RaftMember.Type.ACTIVE, member.getLastUpdated())).collect(Collectors.toSet());
// Add the local member to the set of active members.
activeMembers.add(member);
// Create a new configuration and store it on disk to ensure the cluster can fall back to the configuration.
configure(new Configuration(0, 0, member.getLastUpdated().toEpochMilli(), activeMembers));
}
return join();
}
use of io.atomix.protocols.raft.cluster.RaftMember in project atomix by atomix.
the class RaftContext method anoint.
/**
* Attempts to become the leader.
*/
public CompletableFuture<Void> anoint() {
if (role.role() == RaftServer.Role.LEADER) {
return CompletableFuture.completedFuture(null);
}
CompletableFuture<Void> future = new CompletableFuture<>();
threadContext.execute(() -> {
// Register a leader election listener to wait for the election of this node.
Consumer<RaftMember> electionListener = new Consumer<RaftMember>() {
@Override
public void accept(RaftMember member) {
if (member.nodeId().equals(cluster.getMember().nodeId())) {
future.complete(null);
} else {
future.completeExceptionally(new RaftException.ProtocolException("Failed to transfer leadership"));
}
removeLeaderElectionListener(this);
}
};
addLeaderElectionListener(electionListener);
// If a leader already exists, request a leadership transfer from it. Otherwise, transition to the candidate
// state and attempt to get elected.
RaftMember member = getCluster().getMember();
RaftMember leader = getLeader();
if (leader != null) {
protocol.transfer(leader.nodeId(), TransferRequest.builder().withMember(member.nodeId()).build()).whenCompleteAsync((response, error) -> {
if (error != null) {
future.completeExceptionally(error);
} else if (response.status() == RaftResponse.Status.ERROR) {
future.completeExceptionally(response.error().createException());
} else {
transition(RaftServer.Role.CANDIDATE);
}
}, threadContext);
} else {
transition(RaftServer.Role.CANDIDATE);
}
});
return future;
}
use of io.atomix.protocols.raft.cluster.RaftMember in project atomix by atomix.
the class RaftClusterContext method listen.
@Override
public synchronized CompletableFuture<Void> listen(Collection<NodeId> cluster) {
if (joinFuture != null)
return joinFuture;
// If no configuration was loaded from disk, create a new configuration.
if (configuration == null) {
member.setType(RaftMember.Type.PASSIVE);
// Create a set of cluster members, excluding the local member which is joining a cluster.
Set<RaftMember> activeMembers = cluster.stream().filter(m -> !m.equals(member.nodeId())).map(m -> new DefaultRaftMember(m, RaftMember.Type.ACTIVE, member.getLastUpdated())).collect(Collectors.toSet());
// fail the join.
if (activeMembers.isEmpty()) {
return Futures.exceptionalFuture(new IllegalStateException("cannot join empty cluster"));
}
// Create a new configuration and configure the cluster. Once the cluster is configured, the configuration
// will be stored on disk to ensure the cluster can fall back to the provided configuration if necessary.
configure(new Configuration(0, 0, member.getLastUpdated().toEpochMilli(), activeMembers));
}
return join();
}
use of io.atomix.protocols.raft.cluster.RaftMember in project atomix by atomix.
the class RaftClusterContext method configure.
/**
* Configures the cluster state.
*
* @param configuration The cluster configuration.
* @return The cluster state.
*/
public RaftClusterContext configure(Configuration configuration) {
checkNotNull(configuration, "configuration cannot be null");
// Configurations can be persisted and applying old configurations can revert newer configurations.
if (this.configuration != null && configuration.index() <= this.configuration.index()) {
return this;
}
Instant time = Instant.ofEpochMilli(configuration.time());
// Iterate through members in the new configuration, add any missing members, and update existing members.
boolean transition = false;
for (RaftMember member : configuration.members()) {
if (member.equals(this.member)) {
transition = this.member.getType().ordinal() < member.getType().ordinal();
this.member.update(member.getType(), time);
members.add(this.member);
} else {
// If the member state doesn't already exist, create it.
RaftMemberContext state = membersMap.get(member.nodeId());
if (state == null) {
DefaultRaftMember defaultMember = new DefaultRaftMember(member.nodeId(), member.getType(), time);
state = new RaftMemberContext(defaultMember, this);
state.resetState(raft.getLog());
this.members.add(state.getMember());
this.remoteMembers.add(state);
membersMap.put(member.nodeId(), state);
listeners.forEach(l -> l.onEvent(new RaftClusterEvent(RaftClusterEvent.Type.JOIN, defaultMember, time.toEpochMilli())));
}
// If the member type has changed, update the member type and reset its state.
if (state.getMember().getType() != member.getType()) {
state.getMember().update(member.getType(), time);
state.resetState(raft.getLog());
}
// Update the optimized member collections according to the member type.
for (List<RaftMemberContext> memberType : memberTypes.values()) {
memberType.remove(state);
}
List<RaftMemberContext> memberType = memberTypes.get(member.getType());
if (memberType == null) {
memberType = new CopyOnWriteArrayList<>();
memberTypes.put(member.getType(), memberType);
}
memberType.add(state);
}
}
// can commit the configuration change prior to shutting down.
if (transition) {
raft.transition(this.member.getType());
}
// Iterate through configured members and remove any that no longer exist in the configuration.
int i = 0;
while (i < this.remoteMembers.size()) {
RaftMemberContext member = this.remoteMembers.get(i);
if (!configuration.members().contains(member.getMember())) {
this.members.remove(member.getMember());
this.remoteMembers.remove(i);
for (List<RaftMemberContext> memberType : memberTypes.values()) {
memberType.remove(member);
}
membersMap.remove(member.getMember().nodeId());
listeners.forEach(l -> l.onEvent(new RaftClusterEvent(RaftClusterEvent.Type.LEAVE, member.getMember(), time.toEpochMilli())));
} else {
i++;
}
}
// If the local member was removed from the cluster, remove it from the members list.
if (!configuration.members().contains(member)) {
members.remove(member);
}
this.configuration = configuration;
// Store the configuration if it's already committed.
if (raft.getCommitIndex() >= configuration.index()) {
raft.getMetaStore().storeConfiguration(configuration);
}
return this;
}
use of io.atomix.protocols.raft.cluster.RaftMember 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