/** * 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()); }
@Override public boolean isRunning() { return server.isRunning(); } }
/** * Closes the server and exits the partition. * * @return future that is completed when the operation is complete */ public CompletableFuture<Void> leave() { return server.leave(); }
CompletableFuture<Void> leaveFuture; if (remove) { System.out.println("Removing server: " + server.cluster().getMember().memberId()); leaveFuture = server.leave(); } else { System.out.println("Shutting down server: " + server.cluster().getMember().memberId()); leaveFuture = server.shutdown(); RaftServer newServer = createServer(server.cluster().getMember()); servers.set(serverIndex, newServer); CompletableFuture<RaftServer> joinFuture; if (remove) { System.out.println("Adding server: " + newServer.cluster().getMember().memberId()); joinFuture = newServer.join(members.get(members.size() - 1).memberId()); } else { System.out.println("Bootstrapping server: " + newServer.cluster().getMember().memberId()); joinFuture = newServer.bootstrap(members.stream().map(RaftMember::memberId).collect(Collectors.toList()));
@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); }
/** * Returns a new Raft server builder using the default host:port. * <p> * The server will be constructed at 0.0.0.0:8700. * * @return The server builder. */ static Builder builder() { try { InetAddress address = InetAddress.getByName("0.0.0.0"); return builder(MemberId.from(address.getHostName())); } catch (UnknownHostException e) { throw new ConfigurationException("Cannot configure local node", e); } }
return join(Arrays.asList(members));
@Override public CompletableFuture<Void> stop() { return server.shutdown(); }
/** * Returns whether the server is the leader. * * @return whether the server is the leader */ default boolean isLeader() { return getRole() == Role.LEADER; }
/** * Takes a snapshot of the partition server. * * @return a future to be completed once the snapshot has been taken */ public CompletableFuture<Void> snapshot() { return server.compact(); }
/** * Joins the cluster as a passive listener. * * @param cluster A collection of cluster members to join. * @return A completable future to be completed once the local server has joined the cluster as a listener. */ default CompletableFuture<RaftServer> listen(MemberId... cluster) { return listen(Arrays.asList(checkNotNull(cluster))); }
/** * Creates a Raft server. */ private RaftServer createServer(RaftMember member) { RaftServerProtocol protocol; if (USE_NETTY) { Address address = Address.from(++port); MessagingService messagingManager = new NettyMessagingService("test", address, new MessagingConfig()).start().join(); messagingServices.add(messagingManager); addressMap.put(member.memberId(), address); protocol = new RaftServerMessagingProtocol(messagingManager, PROTOCOL_SERIALIZER, addressMap::get); } else { protocol = protocolFactory.newServerProtocol(member.memberId()); } RaftServer.Builder builder = RaftServer.builder(member.memberId()) .withProtocol(protocol) .withStorage(RaftStorage.builder() .withStorageLevel(StorageLevel.DISK) .withDirectory(new File(String.format("target/fuzz-logs/%s", member.memberId()))) .withNamespace(STORAGE_NAMESPACE) .withMaxSegmentSize(1024 * 1024) .build()); RaftServer server = builder.build(); servers.add(server); return server; }
public CompletableFuture<Void> join(Collection<MemberId> otherMembers) { log.info("Joining partition {} ({})", partition.id(), partition.name()); server = buildServer(); return server.join(otherMembers).whenComplete((r, e) -> { if (e == null) { log.debug("Successfully joined partition {} ({})", partition.id(), partition.name()); } else { log.warn("Failed to join partition {} ({})", partition.id(), partition.name(), e); } }).thenApply(v -> null); }
/** * Returns whether the server is a follower. * * @return whether the server is a follower */ default boolean isFollower() { return getRole() == Role.FOLLOWER; }
RaftServer.Builder builder = RaftServer.builder(member.id()) .withProtocol(protocol) .withThreadModel(ThreadModel.SHARED_THREAD_POOL)
/** * 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)); }
private RaftServer buildServer() { return RaftServer.builder(localMemberId) .withName(partition.name()) .withMembershipService(membershipService) .withProtocol(new RaftServerCommunicator( partition.name(), Serializer.using(RaftNamespaces.RAFT_PROTOCOL), clusterCommunicator)) .withPrimitiveTypes(primitiveTypes) .withElectionTimeout(Duration.ofMillis(ELECTION_TIMEOUT_MILLIS)) .withHeartbeatInterval(Duration.ofMillis(HEARTBEAT_INTERVAL_MILLIS)) .withStorage(RaftStorage.builder() .withPrefix(partition.name()) .withDirectory(partition.dataDirectory()) .withStorageLevel(config.getStorageConfig().getLevel()) .withMaxSegmentSize((int) config.getStorageConfig().getSegmentSize().bytes()) .withMaxEntrySize((int) config.getStorageConfig().getMaxEntrySize().bytes()) .withFlushOnCommit(config.getStorageConfig().isFlushOnCommit()) .withDynamicCompaction(config.getCompactionConfig().isDynamic()) .withFreeDiskBuffer(config.getCompactionConfig().getFreeDiskBuffer()) .withFreeMemoryBuffer(config.getCompactionConfig().getFreeMemoryBuffer()) .withNamespace(RaftNamespaces.RAFT_STORAGE) .build()) .withThreadContextFactory(threadContextFactory) .build(); }
/** * 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; }