/** * Bootstraps the service. */ private CompletableFuture<Void> bootstrap() { return bootstrap(0, new CompletableFuture<>()); }
/** * Bootstraps the service from the given node. */ @SuppressWarnings("unchecked") private CompletableFuture<Void> bootstrap(Member member) { return bootstrap(member, new CompletableFuture<>()); }
@Override public CompletableFuture<PartitionGroupMembershipService> start() { threadContext = new SingleThreadContext(namedThreads("atomix-partition-group-membership-service-%d", LOGGER)); membershipService.addListener(membershipEventListener); messagingService.subscribe(BOOTSTRAP_SUBJECT, serializer::decode, this::handleBootstrap, serializer::encode, threadContext); return bootstrap().thenApply(v -> { LOGGER.info("Started"); started.set(true); return this; }); }
/** * Recursively bootstraps the service, retrying if necessary until a system partition group is found. */ private CompletableFuture<Void> bootstrap(int attempt, CompletableFuture<Void> future) { Futures.allOf(membershipService.getMembers().stream() .filter(node -> !node.id().equals(membershipService.getLocalMember().id())) .map(node -> bootstrap(node)) .collect(Collectors.toList())) .whenComplete((result, error) -> { if (error == null) { if (systemGroup == null) { LOGGER.warn("Failed to locate management group via bootstrap nodes. Please ensure partition " + "groups are configured either locally or remotely and the node is able to reach partition group members."); threadContext.schedule(Duration.ofSeconds(FIBONACCI_NUMBERS[Math.min(attempt, 4)]), () -> bootstrap(attempt + 1, future)); } else if (groups.isEmpty() && attempt < MAX_PARTITION_GROUP_ATTEMPTS) { LOGGER.warn("Failed to locate partition group(s) via bootstrap nodes. Please ensure partition " + "groups are configured either locally or remotely and the node is able to reach partition group members."); threadContext.schedule(Duration.ofSeconds(FIBONACCI_NUMBERS[Math.min(attempt, 4)]), () -> bootstrap(attempt + 1, future)); } else { future.complete(null); } } else { future.completeExceptionally(error); } }); return future; }
error = Throwables.getRootCause(error); if (error instanceof MessagingException.NoRemoteHandler || error instanceof TimeoutException) { threadContext.schedule(Duration.ofSeconds(1), () -> bootstrap(member, future)); } else { LOGGER.debug("{} - Failed to bootstrap from member {}", membershipService.getLocalMember().id(), member, error);
/** * Handles a cluster membership change. */ private void handleMembershipChange(ClusterMembershipEvent event) { if (event.type() == ClusterMembershipEvent.Type.MEMBER_ADDED) { bootstrap(event.subject()); } else if (event.type() == ClusterMembershipEvent.Type.MEMBER_REMOVED) { threadContext.execute(() -> { PartitionGroupMembership systemGroup = this.systemGroup; if (systemGroup != null && systemGroup.members().contains(event.subject().id())) { Set<MemberId> newMembers = Sets.newHashSet(systemGroup.members()); newMembers.remove(event.subject().id()); PartitionGroupMembership newMembership = new PartitionGroupMembership(systemGroup.group(), systemGroup.config(), ImmutableSet.copyOf(newMembers), true); this.systemGroup = newMembership; post(new PartitionGroupMembershipEvent(MEMBERS_CHANGED, newMembership)); } groups.values().forEach(group -> { if (group.members().contains(event.subject().id())) { Set<MemberId> newMembers = Sets.newHashSet(group.members()); newMembers.remove(event.subject().id()); PartitionGroupMembership newMembership = new PartitionGroupMembership(group.group(), group.config(), ImmutableSet.copyOf(newMembers), false); groups.put(group.group(), newMembership); post(new PartitionGroupMembershipEvent(MEMBERS_CHANGED, newMembership)); } }); }); } }