use of io.atomix.cluster.Member in project atomix by atomix.
the class HashBasedPrimaryElection method recomputeTerm.
/**
* Recomputes the current term.
*/
private synchronized void recomputeTerm(PartitionGroupMembership membership) {
if (membership == null) {
return;
}
// Create a list of candidates based on the availability of members in the group.
List<GroupMember> candidates = new ArrayList<>();
for (MemberId memberId : membership.members()) {
Member member = clusterMembershipService.getMember(memberId);
if (member != null && member.isReachable()) {
candidates.add(new GroupMember(memberId, MemberGroupId.from(memberId.id())));
}
}
// Sort the candidates by a hash of their member ID.
candidates.sort((a, b) -> {
int aoffset = Hashing.murmur3_32().hashString(a.memberId().id(), StandardCharsets.UTF_8).asInt() % partitionId.id();
int boffset = Hashing.murmur3_32().hashString(b.memberId().id(), StandardCharsets.UTF_8).asInt() % partitionId.id();
return aoffset - boffset;
});
// Store the current term in a local variable avoid repeated volatile reads.
PrimaryTerm currentTerm = this.currentTerm;
// Compute the primary from the sorted candidates list.
GroupMember primary = candidates.isEmpty() ? null : candidates.get(0);
// Remove the primary from the candidates list.
candidates = candidates.isEmpty() ? Collections.emptyList() : candidates.subList(1, candidates.size());
// If the primary has changed, increment the term. Otherwise, use the current term from the replicated counter.
long term = currentTerm != null && Objects.equals(currentTerm.primary(), primary) && Objects.equals(currentTerm.candidates(), candidates) ? currentTerm() : incrementTerm();
// Create the new primary term. If the term has changed update the term and trigger an event.
PrimaryTerm newTerm = new PrimaryTerm(term, primary, candidates);
if (!Objects.equals(currentTerm, newTerm)) {
this.currentTerm = newTerm;
LOGGER.debug("{} - Recomputed term for partition {}: {}", clusterMembershipService.getLocalMember().id(), partitionId, newTerm);
post(new PrimaryElectionEvent(PrimaryElectionEvent.Type.CHANGED, partitionId, newTerm));
broadcastCounters();
}
}
use of io.atomix.cluster.Member in project atomix by atomix.
the class SwimProtocolTest method testSwimProtocol.
@Test
public void testSwimProtocol() throws Exception {
// Start a node and check its events.
startProtocol(member1);
checkEvent(member1, MEMBER_ADDED, member1);
checkMembers(member1, member1);
// Start a node and check its events.
startProtocol(member2);
checkEvent(member2, MEMBER_ADDED, member2);
checkEvent(member2, MEMBER_ADDED, member1);
checkMembers(member2, member1, member2);
checkEvent(member1, MEMBER_ADDED, member2);
checkMembers(member1, member1, member2);
// Start a node and check its events.
startProtocol(member3);
checkEvent(member3, MEMBER_ADDED, member3);
checkEvent(member3, MEMBER_ADDED);
checkEvent(member3, MEMBER_ADDED);
checkMembers(member3, member1, member2, member3);
checkEvent(member2, MEMBER_ADDED, member3);
checkMembers(member2, member1, member2, member3);
checkEvent(member1, MEMBER_ADDED, member3);
checkMembers(member1, member1, member2, member3);
// Isolate node 3 from the rest of the cluster.
partition(member3);
// Nodes 1 and 2 should see REACHABILITY_CHANGED events and then MEMBER_REMOVED events.
checkEvent(member1, REACHABILITY_CHANGED, member3);
checkEvent(member2, REACHABILITY_CHANGED, member3);
checkEvent(member1, MEMBER_REMOVED, member3);
checkEvent(member2, MEMBER_REMOVED, member3);
// Verify that node 3 was removed from nodes 1 and 2.
checkMembers(member1, member1, member2);
checkMembers(member2, member1, member2);
// Node 3 should also see REACHABILITY_CHANGED and MEMBER_REMOVED events for nodes 1 and 2.
checkEvents(member3, new GroupMembershipEvent(REACHABILITY_CHANGED, member1), new GroupMembershipEvent(REACHABILITY_CHANGED, member2), new GroupMembershipEvent(MEMBER_REMOVED, member1), new GroupMembershipEvent(MEMBER_REMOVED, member2));
// Verify that nodes 1 and 2 were removed from node 3.
checkMembers(member3, member3);
// Heal the partition.
heal(member3);
// Verify that the nodes discovery each other again.
checkEvent(member1, MEMBER_ADDED, member3);
checkEvent(member2, MEMBER_ADDED, member3);
checkEvents(member3, new GroupMembershipEvent(MEMBER_ADDED, member1), new GroupMembershipEvent(MEMBER_ADDED, member2));
// Partition node 1 from node 2.
partition(member1, member2);
// Verify that neither node is ever removed from the cluster since node 3 can still ping nodes 1 and 2.
Thread.sleep(5000);
checkMembers(member1, member1, member2, member3);
checkMembers(member2, member1, member2, member3);
checkMembers(member3, member1, member2, member3);
// Heal the partition.
heal(member1, member2);
// Update node 1's metadata.
member1.properties().put("foo", "bar");
// Verify the metadata change is propagated throughout the cluster.
checkEvent(member1, METADATA_CHANGED, member1);
checkEvent(member2, METADATA_CHANGED, member1);
checkEvent(member3, METADATA_CHANGED, member1);
// Stop member 3 and change its version.
stopProtocol(member3);
Member member = member(member3.id().id(), member3.address().host(), member3.address().port(), version2);
startProtocol(member);
// Verify that version 1 is removed and version 2 is added.
checkEvent(member1, MEMBER_REMOVED, member3);
checkEvent(member1, MEMBER_ADDED, member);
checkEvent(member2, MEMBER_REMOVED, member3);
checkEvent(member2, MEMBER_ADDED, member);
}
use of io.atomix.cluster.Member in project atomix by atomix.
the class DefaultClusterEventServiceTest method testClusterEventService.
@Test
public void testClusterEventService() throws Exception {
TestMessagingServiceFactory messagingServiceFactory = new TestMessagingServiceFactory();
TestUnicastServiceFactory unicastServiceFactory = new TestUnicastServiceFactory();
TestBroadcastServiceFactory broadcastServiceFactory = new TestBroadcastServiceFactory();
Collection<Node> bootstrapLocations = buildBootstrapNodes(3);
Member localMember1 = buildNode(1);
MessagingService messagingService1 = messagingServiceFactory.newMessagingService(localMember1.address()).start().join();
BootstrapService bootstrapService1 = new TestBootstrapService(messagingService1, unicastServiceFactory.newUnicastService(localMember1.address()).start().join(), broadcastServiceFactory.newBroadcastService().start().join());
ManagedClusterMembershipService clusterService1 = new DefaultClusterMembershipService(localMember1, Version.from("1.0.0"), new DefaultNodeDiscoveryService(bootstrapService1, localMember1, new BootstrapDiscoveryProvider(bootstrapLocations)), bootstrapService1, new HeartbeatMembershipProtocol(new HeartbeatMembershipProtocolConfig()));
ClusterMembershipService clusterMembershipService1 = clusterService1.start().join();
ManagedClusterEventService clusterEventingService1 = new DefaultClusterEventService(clusterMembershipService1, messagingService1);
ClusterEventService eventService1 = clusterEventingService1.start().join();
Member localMember2 = buildNode(2);
MessagingService messagingService2 = messagingServiceFactory.newMessagingService(localMember2.address()).start().join();
BootstrapService bootstrapService2 = new TestBootstrapService(messagingService2, unicastServiceFactory.newUnicastService(localMember2.address()).start().join(), broadcastServiceFactory.newBroadcastService().start().join());
ManagedClusterMembershipService clusterService2 = new DefaultClusterMembershipService(localMember2, Version.from("1.0.0"), new DefaultNodeDiscoveryService(bootstrapService2, localMember2, new BootstrapDiscoveryProvider(bootstrapLocations)), bootstrapService2, new HeartbeatMembershipProtocol(new HeartbeatMembershipProtocolConfig()));
ClusterMembershipService clusterMembershipService2 = clusterService2.start().join();
ManagedClusterEventService clusterEventingService2 = new DefaultClusterEventService(clusterMembershipService2, messagingService2);
ClusterEventService eventService2 = clusterEventingService2.start().join();
Member localMember3 = buildNode(3);
MessagingService messagingService3 = messagingServiceFactory.newMessagingService(localMember3.address()).start().join();
BootstrapService bootstrapService3 = new TestBootstrapService(messagingService3, unicastServiceFactory.newUnicastService(localMember1.address()).start().join(), broadcastServiceFactory.newBroadcastService().start().join());
ManagedClusterMembershipService clusterService3 = new DefaultClusterMembershipService(localMember3, Version.from("1.0.0"), new DefaultNodeDiscoveryService(bootstrapService3, localMember3, new BootstrapDiscoveryProvider(bootstrapLocations)), bootstrapService3, new HeartbeatMembershipProtocol(new HeartbeatMembershipProtocolConfig()));
ClusterMembershipService clusterMembershipService3 = clusterService3.start().join();
ManagedClusterEventService clusterEventingService3 = new DefaultClusterEventService(clusterMembershipService3, messagingService3);
ClusterEventService eventService3 = clusterEventingService3.start().join();
Thread.sleep(100);
Set<Integer> events = new CopyOnWriteArraySet<>();
eventService1.<String>subscribe("test1", SERIALIZER::decode, message -> {
assertEquals("Hello world!", message);
events.add(1);
}, MoreExecutors.directExecutor()).join();
eventService2.<String>subscribe("test1", SERIALIZER::decode, message -> {
assertEquals("Hello world!", message);
events.add(2);
}, MoreExecutors.directExecutor()).join();
eventService2.<String>subscribe("test1", SERIALIZER::decode, message -> {
assertEquals("Hello world!", message);
events.add(3);
}, MoreExecutors.directExecutor()).join();
eventService3.broadcast("test1", "Hello world!", SERIALIZER::encode);
Thread.sleep(100);
assertEquals(3, events.size());
events.clear();
eventService3.unicast("test1", "Hello world!");
Thread.sleep(100);
assertEquals(1, events.size());
assertTrue(events.contains(3));
events.clear();
eventService3.unicast("test1", "Hello world!");
Thread.sleep(100);
assertEquals(1, events.size());
assertTrue(events.contains(1));
events.clear();
eventService3.unicast("test1", "Hello world!");
Thread.sleep(100);
assertEquals(1, events.size());
assertTrue(events.contains(2));
events.clear();
eventService3.unicast("test1", "Hello world!");
Thread.sleep(100);
assertEquals(1, events.size());
assertTrue(events.contains(3));
events.clear();
eventService1.<String, String>subscribe("test2", SERIALIZER::decode, message -> {
events.add(1);
return message;
}, SERIALIZER::encode, MoreExecutors.directExecutor()).join();
eventService2.<String, String>subscribe("test2", SERIALIZER::decode, message -> {
events.add(2);
return message;
}, SERIALIZER::encode, MoreExecutors.directExecutor()).join();
assertEquals("Hello world!", eventService3.send("test2", "Hello world!").join());
assertEquals(1, events.size());
assertTrue(events.contains(1));
events.clear();
assertEquals("Hello world!", eventService3.send("test2", "Hello world!").join());
assertEquals(1, events.size());
assertTrue(events.contains(2));
events.clear();
assertEquals("Hello world!", eventService3.send("test2", "Hello world!").join());
assertEquals(1, events.size());
assertTrue(events.contains(1));
CompletableFuture.allOf(new CompletableFuture[] { clusterEventingService1.stop(), clusterEventingService2.stop(), clusterEventingService3.stop() }).join();
CompletableFuture.allOf(new CompletableFuture[] { clusterService1.stop(), clusterService2.stop(), clusterService3.stop() }).join();
}
use of io.atomix.cluster.Member in project atomix by atomix.
the class DefaultClusterEventService method gossip.
/**
* Sends a gossip message to an active peer.
*/
private void gossip() {
List<Member> members = membershipService.getMembers().stream().filter(node -> !localMemberId.equals(node.id())).filter(node -> node.isReachable()).collect(Collectors.toList());
if (!members.isEmpty()) {
Collections.shuffle(members);
Member member = members.get(0);
updateNode(member);
}
}
use of io.atomix.cluster.Member in project atomix by atomix.
the class DefaultClusterEventService method broadcast.
@Override
public <M> void broadcast(String topic, M message, Function<M, byte[]> encoder) {
byte[] payload = SERIALIZER.encode(new InternalMessage(InternalMessage.Type.ALL, encoder.apply(message)));
getSubscriberNodes(topic).forEach(memberId -> {
Member member = membershipService.getMember(memberId);
if (member != null && member.isReachable()) {
messagingService.sendAsync(member.address(), topic, payload);
}
});
}
Aggregations