/** * Bootstraps a single-node cluster. * <p> * Bootstrapping a single-node cluster results in the server forming a new cluster to which additional servers * can be joined. * <p> * Only {@link RaftMember.Type#ACTIVE} members can be included in a bootstrap configuration. If the local server is * not initialized as an active member, it cannot be part of the bootstrap configuration for the cluster. * <p> * When the cluster is bootstrapped, the local server will be transitioned into the active state and begin * participating in the Raft consensus algorithm. When the cluster is first bootstrapped, no leader will exist. * The bootstrapped members will elect a leader amongst themselves. Once a cluster has been bootstrapped, additional * members may be {@link #join(MemberId...) joined} to the cluster. In the event that the bootstrapped members cannot * reach a quorum to elect a leader, bootstrap will continue until successful. * <p> * It is critical that all servers in a bootstrap configuration be started with the same exact set of members. * Bootstrapping multiple servers with different configurations may result in split brain. * <p> * The {@link CompletableFuture} returned by this method will be completed once the cluster has been bootstrapped, * a leader has been elected, and the leader has been notified of the local server's client configurations. * * @return A completable future to be completed once the cluster has been bootstrapped. */ default CompletableFuture<RaftServer> bootstrap() { return bootstrap(Collections.emptyList()); }
/** * Bootstraps the cluster using the provided cluster configuration. * <p> * Bootstrapping the cluster results in a new cluster being formed with the provided configuration. The initial * nodes in a cluster must always be bootstrapped. This is necessary to prevent split brain. If the provided * configuration is empty, the local server will form a single-node cluster. * <p> * Only {@link RaftMember.Type#ACTIVE} members can be included in a bootstrap configuration. If the local server is * not initialized as an active member, it cannot be part of the bootstrap configuration for the cluster. * <p> * When the cluster is bootstrapped, the local server will be transitioned into the active state and begin * participating in the Raft consensus algorithm. When the cluster is first bootstrapped, no leader will exist. * The bootstrapped members will elect a leader amongst themselves. Once a cluster has been bootstrapped, additional * members may be {@link #join(MemberId...) joined} to the cluster. In the event that the bootstrapped members cannot * reach a quorum to elect a leader, bootstrap will continue until successful. * <p> * It is critical that all servers in a bootstrap configuration be started with the same exact set of members. * Bootstrapping multiple servers with different configurations may result in split brain. * <p> * The {@link CompletableFuture} returned by this method will be completed once the cluster has been bootstrapped, * a leader has been elected, and the leader has been notified of the local server's client configurations. * * @param members The bootstrap cluster configuration. * @return A completable future to be completed once the cluster has been bootstrapped. */ default CompletableFuture<RaftServer> bootstrap(MemberId... members) { return bootstrap(Arrays.asList(members)); }
/** * Creates a set of Raft servers. */ private List<RaftServer> createServers(int nodes) throws Exception { List<RaftServer> servers = new ArrayList<>(); for (int i = 0; i < nodes; i++) { members.add(nextNode()); } CountDownLatch latch = new CountDownLatch(nodes); for (int i = 0; i < nodes; i++) { RaftServer server = createServer(members.get(i), Lists.newArrayList(members)); server.bootstrap(members.stream().map(Member::id).collect(Collectors.toList())).thenRun(latch::countDown); servers.add(server); } latch.await(30000, TimeUnit.MILLISECONDS); return servers; }
/** * Creates a set of Raft servers. */ private List<RaftServer> createServers(int nodes) throws Exception { List<RaftServer> servers = new ArrayList<>(); for (int i = 0; i < nodes; i++) { members.add(nextMember(RaftMember.Type.ACTIVE)); } CountDownLatch latch = new CountDownLatch(nodes); for (int i = 0; i < nodes; i++) { RaftServer server = createServer(members.get(i)); server.bootstrap(members.stream().map(RaftMember::memberId).collect(Collectors.toList())).thenRun(latch::countDown); servers.add(server); } latch.await(30000, TimeUnit.MILLISECONDS); return servers; }
@Override public CompletableFuture<RaftPartitionServer> start() { log.info("Starting server for partition {}", partition.id()); CompletableFuture<RaftServer> serverOpenFuture; if (partition.members().contains(localMemberId)) { if (server != null && server.isRunning()) { return CompletableFuture.completedFuture(null); } synchronized (this) { try { server = buildServer(); } catch (StorageException e) { return Futures.exceptionalFuture(e); } } serverOpenFuture = server.bootstrap(partition.members()); } else { serverOpenFuture = CompletableFuture.completedFuture(null); } return serverOpenFuture.whenComplete((r, e) -> { if (e == null) { log.debug("Successfully started server for partition {}", partition.id()); } else { log.warn("Failed to start server for partition {}", partition.id(), e); } }).thenApply(v -> this); }
} else { System.out.println("Bootstrapping server: " + newServer.cluster().getMember().memberId()); joinFuture = newServer.bootstrap(members.stream().map(RaftMember::memberId).collect(Collectors.toList()));