use of io.atomix.primitive.partition.GroupMember 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.primitive.partition.GroupMember in project atomix by atomix.
the class PrimaryElectorServiceTest method testEnterSeveralPartitions.
@Test
public void testEnterSeveralPartitions() {
PrimaryElectorService elector = newService();
PrimaryTerm term = null;
int numParts = 10;
int numMembers = 20;
List<List<GroupMember>> allMembers = new ArrayList<>();
List<PrimaryTerm> terms = new ArrayList<>();
for (int p = 0; p < numParts; p++) {
PartitionId partId = new PartitionId("test", p);
allMembers.add(new ArrayList<>());
// Add all members in same group.
for (int i = 0; i < numMembers; i++) {
GroupMember m = createGroupMember("node" + i, "group1");
allMembers.get(p).add(m);
Session<?> s = createSession(m);
term = elector.enter(createEnterOp(partId, m, s));
}
if (term != null) {
terms.add(term);
}
}
// Check primary and candidates in each partition.
for (int p = 0; p < numParts; p++) {
assertEquals(1L, terms.get(p).term());
assertEquals(allMembers.get(p).get(0), terms.get(p).primary());
assertEquals(numMembers, terms.get(p).candidates().size());
for (int i = 0; i < numMembers; i++) {
assertEquals(allMembers.get(p).get(i), terms.get(p).candidates().get(i));
}
}
}
use of io.atomix.primitive.partition.GroupMember in project atomix by atomix.
the class PrimaryElectorServiceTest method testEnterAndExpireSessions.
@Test
public void testEnterAndExpireSessions() {
PrimaryElectorService elector = newService();
PartitionId partId = new PartitionId("test", 1);
PrimaryTerm term = null;
int numMembers = 9;
// Add 9 members in 3 different groups.
List<Session<?>> sessions = new ArrayList<>();
List<GroupMember> members = new ArrayList<>();
for (int i = 0; i < numMembers; i++) {
GroupMember m = createGroupMember("node" + i, "group" + (i / 3));
members.add(m);
Session<?> s = createSession(m);
sessions.add(s);
term = elector.enter(createEnterOp(partId, m, s));
}
// Check current primary.
assertEquals(1L, term.term());
assertEquals(members.get(0), term.primary());
assertEquals(numMembers, term.candidates().size());
List<GroupMember> backups1 = term.backups(2);
assertEquals(members.get(3), backups1.get(0));
assertEquals(members.get(6), backups1.get(1));
// Expire session of primary and check new term.
// New primary should be the first of the old backups.
elector.onExpire(sessions.get(0));
term = elector.getTerm(createGetTermOp(partId, members.get(3), sessions.get(3)));
assertEquals(2L, term.term());
assertEquals(members.get(3), term.primary());
assertEquals(numMembers - 1, term.candidates().size());
List<GroupMember> backups2 = term.backups(2);
assertEquals(members.get(6), backups2.get(0));
assertEquals(members.get(1), backups2.get(1));
// Expire session of backup and check term updated.
elector.onExpire(sessions.get(6));
term = elector.getTerm(createGetTermOp(partId, members.get(5), sessions.get(5)));
assertEquals(2L, term.term());
assertEquals(members.get(3), term.primary());
assertEquals(numMembers - 2, term.candidates().size());
List<GroupMember> backups3 = term.backups(2);
assertEquals(members.get(1), backups3.get(0));
assertEquals(members.get(4), backups3.get(1));
}
use of io.atomix.primitive.partition.GroupMember in project atomix by atomix.
the class PrimaryElectorServiceTest method testEnterSinglePartitionWithGroups.
@Test
public void testEnterSinglePartitionWithGroups() {
PrimaryElectorService elector = newService();
PartitionId partId = new PartitionId("test", 1);
PrimaryTerm term = null;
int numMembers = 9;
// Add 9 members in 3 different groups.
List<GroupMember> members = new ArrayList<>();
for (int i = 0; i < numMembers; i++) {
GroupMember m = createGroupMember("node" + i, "group" + (i / 3));
members.add(m);
Session<?> s = createSession(m);
term = elector.enter(createEnterOp(partId, m, s));
}
// Check primary and candidates.
assertEquals(1L, term.term());
assertEquals(members.get(0), term.primary());
assertEquals(numMembers, term.candidates().size());
// Check backups are selected in different groups.
List<GroupMember> backups2 = term.backups(2);
assertEquals(members.get(3), backups2.get(0));
assertEquals(members.get(6), backups2.get(1));
List<GroupMember> backups3 = term.backups(3);
assertEquals(members.get(3), backups3.get(0));
assertEquals(members.get(6), backups3.get(1));
assertEquals(members.get(1), backups3.get(2));
}
use of io.atomix.primitive.partition.GroupMember in project atomix by atomix.
the class DistributedLogServerContext method start.
@Override
public CompletableFuture<Void> start() {
registerListeners();
compactTimer = threadContext.schedule(Duration.ofSeconds(30), this::compact);
return memberGroupService.start().thenComposeAsync(v -> {
MemberGroup group = memberGroupService.getMemberGroup(clusterMembershipService.getLocalMember());
primaryElection.addListener(primaryElectionListener);
if (group != null) {
return primaryElection.enter(new GroupMember(clusterMembershipService.getLocalMember().id(), group.id())).thenApply(term -> {
changeRole(term);
return null;
});
}
return CompletableFuture.completedFuture(null);
}, threadContext).thenApply(v -> {
started.set(true);
return null;
});
}
Aggregations