package org.rakam.kume;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.junit.Ignore;
import org.junit.Test;
import org.rakam.kume.util.ConsistentHashRing;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class ConsistentHashingTest {
/*
We need an extensive testing framework in order to efficiently test ConsistentHashRing
- testing for immutability
- testing for balancing mechanism
- testing for edge cases
- add same node twice
- remove same node twice
*/
@Test
public void testAdd() {
Member member = new Member("127.0.0.1", 0);
ConsistentHashRing ring = new ConsistentHashRing(Lists.newArrayList(member), 8, 2);
List<Member> members = Lists.newArrayList(member);
for (int i = 1; i <= 100; i++) {
Map<ConsistentHashRing.TokenRange, List<Member>> buckets = ring.getBuckets();
assertEquals(buckets.size(), 8 * members.size());
buckets.forEach((token, memberList) -> {
if (members.size() <= 2) {
assertEquals(members.size(), memberList.size());
for (Member m : members) {
assertTrue(memberList.contains(m));
}
} else {
assertEquals(memberList.size(), 2);
assertEquals(new HashSet<>(memberList).size(), 2);
}
});
Member member1 = new Member("127.0.0.1", i);
ring = ring.addNode(member1);
members.add(member1);
}
// final ConsistentHashRing finalRing = ring;
// ring.getMembers().forEach(y -> {
// long count = finalRing.getBuckets().entrySet().stream().filter(x -> x.getValue().contains(y)).count();
// System.out.println(count + " " + y);
// });
}
@Ignore
@Test
public void testMemberContainsFixedBuckets() {
Member member = new Member("127.0.0.1", 0);
ConsistentHashRing ring = new ConsistentHashRing(Lists.newArrayList(member), 8, 2);
List<Member> members = Lists.newArrayList(member);
for (int i = 1; i < 100; i++) {
Map<ConsistentHashRing.TokenRange, List<Member>> buckets = ring.getBuckets();
ring.getMembers().forEach(m -> {
long count = buckets.entrySet().stream().filter(x -> x.getValue().contains(m)).count();
assertEquals(Math.min(8 * 2, buckets.size()), count);
});
Member member1 = new Member("127.0.0.1", i);
ring = ring.addNode(member1);
members.add(member1);
}
}
// @Test
public void testRemove() {
Member member0 = new Member("127.0.0.1", 0);
Member member1 = new Member("127.0.0.1", 1);
ConsistentHashRing ring = new ConsistentHashRing(Lists.newArrayList(member0, member1), 8, 2);
ConsistentHashRing removedRing = ring.removeNode(member0);
Map<ConsistentHashRing.TokenRange, List<Member>> buckets = removedRing.getBuckets();
assertEquals(buckets.size(), 8);
buckets.forEach((range, members) -> {
assertEquals(members.size(), 1);
assertTrue(members.contains(member1));
});
}
// @Test
public void testRemoveMultiple() {
Member member0 = new Member("127.0.0.1", 0);
Member member1 = new Member("127.0.0.1", 1);
Member member2 = new Member("127.0.0.1", 2);
ConsistentHashRing ring = new ConsistentHashRing(Lists.newArrayList(member0, member1, member2), 8, 2);
ConsistentHashRing removedRing = ring.removeNode(member2);
Map<ConsistentHashRing.TokenRange, List<Member>> buckets = removedRing.getBuckets();
assertEquals(buckets.size(), 16);
buckets.forEach((range, members) -> {
assertEquals(members.size(), 2);
assertTrue(members.contains(member1));
assertTrue(members.contains(member0));
});
}
// @Test
public void testBalanced() {
Member member0 = new Member("127.0.0.1", 0);
Member member1 = new Member("127.0.0.1", 1);
Member member2 = new Member("127.0.0.1", 2);
Member member3 = new Member("127.0.0.1", 3);
Member member4 = new Member("127.0.0.1", 4);
Member member5 = new Member("127.0.0.1", 5);
ConsistentHashRing ring = new ConsistentHashRing(Lists.newArrayList(member0, member1), 8, 2);
ConsistentHashRing newRing = ring.addNode(member2).addNode(member3).addNode(member4).addNode(member5);
newRing.getMembers().forEach(x -> System.out.println(x.getAddress().getPort() + " " + newRing.getTotalRingRange(x)));
newRing.getBuckets().forEach((range, members) -> {
long percentage = (range.gap() / 100) / (Long.MAX_VALUE / 5000);
System.out.println("%" + percentage + " " + members.stream().map(x -> Integer.toString(x.getAddress().getPort())).collect(Collectors.joining(", ")));
});
}
public void printRing(ConsistentHashRing ring) {
ring.getBuckets()
.forEach((token, members) -> System.out.println(token + " ->" + members));
}
@Test()
public void testEquals() {
ConsistentHashRing ring1 = new ConsistentHashRing(Sets.newHashSet(new Member("127.0.0.1", 0)), 8, 2);
ConsistentHashRing ring2 = new ConsistentHashRing(Sets.newHashSet(new Member("127.0.0.1", 0)), 8, 2);
assertEquals(ring1, ring2);
for (int i = 1; i < 100; i++) {
Member member = new Member("127.0.0.1", i);
ring1 = ring1.addNode(member);
ring2 = ring2.addNode(member);
assertEquals(ring1, ring2);
}
}
}