use of io.atomix.protocols.raft.storage.system.Configuration in project atomix by atomix.
the class DefaultRaftMember method configure.
/**
* Recursively reconfigures the cluster.
*/
private void configure(RaftMember.Type type, CompletableFuture<Void> future) {
// Set a timer to retry the attempt to leave the cluster.
configureTimeout = cluster.getContext().getThreadContext().schedule(cluster.getContext().getElectionTimeout(), () -> {
configure(type, future);
});
// Attempt to leave the cluster by submitting a LeaveRequest directly to the server state.
// Non-leader states should forward the request to the leader if there is one. Leader states
// will log, replicate, and commit the reconfiguration.
cluster.getContext().getRaftRole().onReconfigure(ReconfigureRequest.builder().withIndex(cluster.getConfiguration().index()).withTerm(cluster.getConfiguration().term()).withMember(new DefaultRaftMember(id, type, updated)).build()).whenComplete((response, error) -> {
if (error == null) {
if (response.status() == RaftResponse.Status.OK) {
cancelConfigureTimer();
cluster.configure(new Configuration(response.index(), response.term(), response.timestamp(), response.members()));
future.complete(null);
} else if (response.error() == null || response.error().type() == RaftError.Type.UNAVAILABLE || response.error().type() == RaftError.Type.PROTOCOL_ERROR || response.error().type() == RaftError.Type.NO_LEADER) {
cancelConfigureTimer();
configureTimeout = cluster.getContext().getThreadContext().schedule(cluster.getContext().getElectionTimeout().multipliedBy(2), () -> configure(type, future));
} else {
cancelConfigureTimer();
future.completeExceptionally(response.error().createException());
}
} else {
future.completeExceptionally(error);
}
});
}
use of io.atomix.protocols.raft.storage.system.Configuration in project atomix by atomix.
the class RaftClusterContext method leave.
/**
* Attempts to leave the cluster.
*/
private void leave(CompletableFuture<Void> future) {
// Set a timer to retry the attempt to leave the cluster.
leaveTimeout = raft.getThreadContext().schedule(raft.getElectionTimeout(), () -> {
leave(future);
});
// Attempt to leave the cluster by submitting a LeaveRequest directly to the server state.
// Non-leader states should forward the request to the leader if there is one. Leader states
// will log, replicate, and commit the reconfiguration.
raft.getRaftRole().onLeave(LeaveRequest.builder().withMember(getMember()).build()).whenComplete((response, error) -> {
// Cancel the leave timer.
cancelLeaveTimer();
if (error == null && response.status() == RaftResponse.Status.OK) {
Configuration configuration = new Configuration(response.index(), response.term(), response.timestamp(), response.members());
// Configure the cluster and commit the configuration as we know the successful response
// indicates commitment.
configure(configuration).commit();
future.complete(null);
} else {
// Reset the leave timer.
leaveTimeout = raft.getThreadContext().schedule(raft.getElectionTimeout(), () -> {
leave(future);
});
}
});
}
use of io.atomix.protocols.raft.storage.system.Configuration in project atomix by atomix.
the class RaftClusterContext method join.
@Override
public synchronized CompletableFuture<Void> join(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.PROMOTABLE);
// 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().thenCompose(v -> {
if (member.getType() == RaftMember.Type.ACTIVE) {
return CompletableFuture.completedFuture(null);
} else {
return member.promote(RaftMember.Type.ACTIVE);
}
});
}
use of io.atomix.protocols.raft.storage.system.Configuration 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.storage.system.Configuration in project atomix by atomix.
the class RaftClusterContext method join.
/**
* Recursively attempts to join the cluster.
*/
private void join(Iterator<RaftMemberContext> iterator) {
if (iterator.hasNext()) {
cancelJoinTimer();
joinTimeout = raft.getThreadContext().schedule(raft.getElectionTimeout().multipliedBy(2), () -> {
join(iterator);
});
RaftMemberContext member = iterator.next();
log.debug("Attempting to join via {}", member.getMember().nodeId());
JoinRequest request = JoinRequest.builder().withMember(new DefaultRaftMember(getMember().nodeId(), getMember().getType(), getMember().getLastUpdated())).build();
raft.getProtocol().join(member.getMember().nodeId(), request).whenCompleteAsync((response, error) -> {
// Cancel the join timer.
cancelJoinTimer();
if (error == null) {
if (response.status() == RaftResponse.Status.OK) {
log.info("Successfully joined via {}", member.getMember().nodeId());
Configuration configuration = new Configuration(response.index(), response.term(), response.timestamp(), response.members());
// Configure the cluster with the join response.
// Commit the configuration as we know it was committed via the successful join response.
configure(configuration).commit();
// If the local member is not present in the configuration, fail the future.
if (!members.contains(this.member)) {
joinFuture.completeExceptionally(new IllegalStateException("not a member of the cluster"));
} else if (joinFuture != null) {
joinFuture.complete(null);
}
} else if (response.error() == null || response.error().type() == RaftError.Type.CONFIGURATION_ERROR) {
// If the response error is null, that indicates that no error occurred but the leader was
// in a state that was incapable of handling the join request. Attempt to join the leader
// again after an election timeout.
log.debug("Failed to join {}", member.getMember().nodeId());
resetJoinTimer();
} else {
// If the response error was non-null, attempt to join via the next server in the members list.
log.debug("Failed to join {}", member.getMember().nodeId());
join(iterator);
}
} else {
log.debug("Failed to join {}", member.getMember().nodeId());
join(iterator);
}
}, raft.getThreadContext());
} else // If join attempts remain, schedule another attempt after two election timeouts. This allows enough time
// for servers to potentially timeout and elect a leader.
{
log.debug("Failed to join cluster, retrying...");
resetJoinTimer();
}
}
Aggregations