package com.cloudera.util.consistenthash;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
static final Logger LOG = LoggerFactory.getLogger(TestConsistentHash.class);
List<String> machines = Arrays.asList("machine A", "machine B", "machine C",
"machine D", "machine E");
@Test
int replicas = 100;
ConsistentHash<String> hash = new ConsistentHash<String>(replicas, machines);
List<String> orig = new ArrayList<String>(20);
StringBuilder sb = new StringBuilder();
sb.append("Before: ");
for (int i = 0; i < 20; i++) {
String s = "this is a the key " + i;
String bin = hash.getBinFor(s);
sb.append(bin + ", ");
orig.add(bin);
}
LOG.info(sb.toString());
int diffs = 0;
sb = new StringBuilder();
sb.append("after adding a machine: ");
hash.addBin("machine F");
for (int i = 0; i < 20; i++) {
String s = "this is a the key " + i;
String bin = hash.getBinFor(s);
sb.append(bin + ", ");
if (!orig.get(i).equals(bin)) {
diffs++;
}
}
LOG.info(sb.toString());
LOG.info("Adding one caused " + diffs + " out of 20 to change");
LOG.info("after adding a machine: ");
hash.removeBin("machine F");
sb = new StringBuilder();
for (int i = 0; i < 20; i++) {
String s = "this is a the key " + i;
String bin = hash.getBinFor(s);
sb.append(bin + ", ");
assertEquals(orig.get(i), bin);
}
LOG.info(sb.toString());
}
@Test
int replicas = 100;
ConsistentHash<String> hash = new ConsistentHash<String>(replicas, machines);
List<String> orig = new ArrayList<String>(20);
StringBuilder sb = new StringBuilder();
LOG.info("Before: ");
for (int i = 0; i < 20; i++) {
String s = "this is a the key " + i;
List<String> l = hash.getNBinsFor(s, 3);
String bin = l.toString();
sb.append(bin + ", ");
orig.add(l.get(0).toString());
}
LOG.info(sb.toString());
sb = new StringBuilder();
int diffs = 0;
LOG.info("after adding a machine: ");
hash.addBin("machine F");
for (int i = 0; i < 20; i++) {
String s = "this is a the key " + i;
List<String> l = hash.getNBinsFor(s, 3);
String bin = l.toString();
sb.append(bin + ", ");
if (!orig.get(i).equals(l.get(0))) {
diffs++;
}
}
LOG.info(sb.toString());
LOG.info("Adding one caused " + diffs + " out of 20 to change");
LOG.info("after adding a machine: ");
sb = new StringBuilder();
hash.removeBin("machine F");
for (int i = 0; i < 20; i++) {
String s = "this is a the key " + i;
String bin = hash.getBinFor(s);
sb.append(bin + ", ");
assertEquals(orig.get(i), bin);
}
LOG.info(sb.toString());
}
@Test
int replicas = 100;
ConsistentHash<String> hash = new ConsistentHash<String>(replicas, machines);
StringBuilder sb = new StringBuilder();
List<String> orig = new ArrayList<String>(20);
LOG.info("Before: ");
for (int i = 0; i < 20; i++) {
String s = "this is a the key " + i;
List<String> l = hash.getNUniqueBinsFor(s, 3);
String bin = l.toString();
sb.append(bin + ", ");
orig.add(l.get(0).toString());
Set<String> set = new HashSet<String>(l);
assertEquals(3, set.size());
}
LOG.info(sb.toString());
int diffs = 0;
LOG.info("after adding a machine: ");
hash.addBin("machine F");
for (int i = 0; i < 20; i++) {
String s = "this is a the key " + i;
String bin = hash.getNUniqueBinsFor(s, 3).toString();
sb.append(bin + ", ");
if (!orig.get(i).equals(bin)) {
diffs++;
}
}
LOG.info(sb.toString());
LOG.info("Adding one caused " + diffs + " out of 20 to change");
sb = new StringBuilder();
LOG.info("after adding a machine: ");
hash.removeBin("machine F");
for (int i = 0; i < 20; i++) {
String s = "this is a the key " + i;
String bin = hash.getBinFor(s);
sb.append(bin + ", ");
assertEquals(orig.get(i), bin);
}
LOG.info(sb.toString());
}
@Test
int replicas = 100;
ConsistentHash<String> hash = new ConsistentHash<String>(replicas, Arrays
.asList("single machine"));
StringBuilder sb = new StringBuilder();
List<String> orig = new ArrayList<String>(20);
LOG.info("Before: ");
for (int i = 0; i < 20; i++) {
String s = "this is a the key " + i;
List<String> l = hash.getNUniqueBinsFor(s, 3);
String bin = l.toString();
sb.append(bin + ", ");
orig.add(l.get(0).toString());
Set<String> set = new HashSet<String>(l);
assertEquals(1, set.size());
}
LOG.info(sb.toString());
}
@Test
int replicas = 100;
int values = 50;
ConsistentLists<String, String> lists = new ConsistentLists<String, String>(
replicas);
lists.addMoveListener(new MoveHandler<String, String>() {
@Override
public void moved(String from, String to, List<String> values) {
LOG.info(String.format("from %s to %s : values %s", from, to, values));
}
@Override
public void rebuild(String key, List<String> allVals) {
LOG.info("Rebuild: " + key);
}
});
for (String m : machines) {
lists.addBin(m);
}
LOG.info("Before: ");
for (int i = 0; i < values; i++) {
lists.addValue(String.format("value%04d", i));
}
int sum = 0;
int buckets = 0;
for (List<String> vs : lists.getValueLists().values()) {
sum += vs.size();
buckets++;
}
assertEquals(buckets, 5);
assertEquals(sum, values);
LOG.info("adding a machine F: ");
lists.addBin("machine F");
LOG.info("Lists values:\n" + lists);
sum = 0;
buckets = 0;
for (List<String> vs : lists.getValueLists().values()) {
sum += vs.size();
buckets++;
}
assertEquals(buckets, 6);
assertEquals(sum, values);
LOG.info("removing machine B: ");
lists.removeBin("machine B");
LOG.info("Lists values:\n" + lists);
sum = 0;
buckets = 0;
for (List<String> vs : lists.getValueLists().values()) {
sum += vs.size();
buckets++;
}
assertEquals(buckets, 5);
assertEquals(sum, values);
LOG.info("removing machine D: ");
lists.removeBin("machine D");
LOG.info("Lists values:\n" + lists);
sum = 0;
buckets = 0;
for (List<String> vs : lists.getValueLists().values()) {
int sz = vs.size();
sum += sz;
buckets++;
}
assertEquals(sum, values);
assertEquals(buckets, 4);
Map<String, List<String>> vls = lists.getValueLists();
assertEquals(13, vls.get("machine E").size());
assertEquals(18, vls.get("machine F").size());
assertEquals(8, vls.get("machine C").size());
assertEquals(11, vls.get("machine A").size());
}
}