package com.rubiconproject.oss.kv.distributed.test;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import junit.framework.TestCase;
import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
import com.rubiconproject.oss.kv.distributed.Node;
import com.rubiconproject.oss.kv.distributed.NodeLocator;
import com.rubiconproject.oss.kv.distributed.NodeStore;
import com.rubiconproject.oss.kv.distributed.hashing.HashAlgorithm;
import com.rubiconproject.oss.kv.distributed.hashing.KetamaHashAlgorithm;
import com.rubiconproject.oss.kv.distributed.hashing.MD5HashAlgorithm;
import com.rubiconproject.oss.kv.distributed.impl.DefaultNodeImpl;
import com.rubiconproject.oss.kv.distributed.impl.DynamoNodeLocator;
import com.rubiconproject.oss.kv.distributed.impl.KetamaNodeLocator;
public class NodeLocatorTestCase extends TestCase {
public void testKetamaNodeLocator() {
NodeStore store = new DummyNodeStore(createNodeList(10, 3));
KetamaNodeLocator locator = new KetamaNodeLocator();
locator.setActiveNodes(store.getActiveNodes());
store.addChangeListener(locator);
testPhysicalNodeKeyDistribution(locator, new KetamaHashAlgorithm());
}
public void testDynamoNodeLocator() {
int physicalHosts = 10, nodesPerHost = 3;
NodeStore store = new DummyNodeStore(createNodeList(physicalHosts,
nodesPerHost));
DynamoNodeLocator locator = new DynamoNodeLocator();
locator.setActiveNodes(store.getActiveNodes());
store.addChangeListener(locator);
HashAlgorithm hashAlg = new MD5HashAlgorithm();
testPhysicalNodeKeyDistribution(locator, hashAlg);
testTokenKeyDistribution(locator, hashAlg, physicalHosts * nodesPerHost);
}
private void testTokenKeyDistribution(NodeLocator nodeLocator,
HashAlgorithm hashAlg, int nodeCount) {
Random random = new Random();
// array to count key assignments
int[] keyAssignments = new int[nodeCount
* DynamoNodeLocator.DEFAULT_TOKENS_PER_NODE];
for (int i = 0; i < 100000; ++i) {
String key = String.format("/blobs/users/%1$d/%2$d/%3$d", random
.nextInt(100), random.nextInt(10000), random
.nextInt(Integer.MAX_VALUE));
int primary = nodeLocator.getPrimaryNode(hashAlg, key);
++keyAssignments[primary];
}
DescriptiveStatistics stats = new DescriptiveStatistics();
for (int i = 0; i < keyAssignments.length; ++i) {
stats.addValue((double) keyAssignments[i]);
}
System.out.println("min: " + stats.getMin());
System.out.println("max: " + stats.getMax());
System.out.println("avg: " + stats.getMean());
System.out.println("stdev: " + stats.getStandardDeviation());
System.out.println("variance: " + stats.getVariance());
}
private void testPhysicalNodeKeyDistribution(NodeLocator nodeLocator,
HashAlgorithm hashAlg) {
Random random = new Random();
// array to count key assignments
int[] keyAssignments = new int[10 * 3];
for (int i = 0; i < 100000; ++i) {
String key = String.format("/blobs/users/%1$d/%2$d/%3$d", random
.nextInt(100), random.nextInt(10000), random
.nextInt(Integer.MAX_VALUE));
List<Node> nodeList = nodeLocator
.getPreferenceList(hashAlg, key, 3);
for (Node node : nodeList) {
++keyAssignments[node.getId() - 1];
}
}
DescriptiveStatistics stats = new DescriptiveStatistics();
for (int i = 0; i < keyAssignments.length; ++i) {
stats.addValue((double) keyAssignments[i]);
}
System.out.println("min: " + stats.getMin());
System.out.println("max: " + stats.getMax());
System.out.println("avg: " + stats.getMean());
System.out.println("stdev: " + stats.getStandardDeviation());
System.out.println("variance: " + stats.getVariance());
assertEquals(stats.getMean(), 10000.0d);
assertTrue(stats.getStandardDeviation() <= 4000);
}
private List<Node> createNodeList(int physicalHosts, int nodesPerPhysical) {
Random r = new Random();
List<Node> results = new ArrayList<Node>(physicalHosts
* nodesPerPhysical);
int nodeId = 1;
for (int i = 1; i <= physicalHosts; ++i) {
for (int j = 0; j < nodesPerPhysical; ++j) {
addNode(results, nodeId, i, String.format("salt:%1$d:%2$d", i,
j), String.format("uri://host#%1$s:%2$d", i, r
.nextInt(1024)));
++nodeId;
}
}
return results;
}
private void addNode(List<Node> nodes, int nodeId, int physicalId,
String salt, String uri) {
Node n = new DefaultNodeImpl(nodeId, physicalId, salt, uri);
nodes.add(n);
}
}