/**
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.katta.master;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
public class HostAwareDistributionPolicyTest {
private HostAwareDistributionPolicy _distributionPolicy;
Map<String, List<String>> _currentShard2NodesMap;
Map<String, List<String>> _currentNode2ShardsMap;
@Before
public void setUp() {
_distributionPolicy = new HostAwareDistributionPolicy();
_currentShard2NodesMap = new HashMap<String, List<String>>();
_currentNode2ShardsMap = new HashMap<String, List<String>>();
}
@Test
public void testInitialDistribution() throws Exception {
int replicationLevel = 3;
List<String> nodes = createNodes("node1:20001", "node1:20002", "node2:20001", "node2:20002", "node3:20001", "node3:20002");
Set<String> shards = createShards("shard1", "shard2", "shard3", "shard4");
Map<String, List<String>> node2ShardsMap = _distributionPolicy.createDistributionPlan(_currentShard2NodesMap,
_currentNode2ShardsMap, nodes, replicationLevel);
assertEquals(nodes.size(), node2ShardsMap.size());
assertSufficientDistribution(replicationLevel, nodes, shards, node2ShardsMap);
assertTrue( Collections.disjoint(node2ShardsMap.get("node1:20001"), node2ShardsMap.get("node1:20002")));
assertTrue( Collections.disjoint(node2ShardsMap.get("node2:20001"), node2ShardsMap.get("node2:20002")));
assertTrue( Collections.disjoint(node2ShardsMap.get("node3:20001"), node2ShardsMap.get("node3:20002")));
}
@Test
public void testThreeRoundsDistribution() throws Exception {
int replicationLevel = 1;
List<String> nodes = createNodes("node1:20001", "node1:20002", "node2:20001", "node2:20002", "node3:20001", "node3:20002");
Set<String> shards = createShards("shard1", "shard2", "shard3", "shard4");
Map<String, List<String>> firstNode2ShardsMap = _distributionPolicy.createDistributionPlan(_currentShard2NodesMap,
_currentNode2ShardsMap, nodes, replicationLevel);
assertEquals(nodes.size(), firstNode2ShardsMap.size());
updateShardToNodesMap(firstNode2ShardsMap);
replicationLevel = 2;
Map<String, List<String>> secondNode2ShardsMap = _distributionPolicy.createDistributionPlan(_currentShard2NodesMap, firstNode2ShardsMap, nodes, replicationLevel);
assertEquals(nodes.size(), secondNode2ShardsMap.size());
assertSufficientDistribution(replicationLevel, nodes, shards, secondNode2ShardsMap);
updateShardToNodesMap(secondNode2ShardsMap);
replicationLevel = 3;
Map<String, List<String>> thirdNode2ShardsMap = _distributionPolicy.createDistributionPlan(_currentShard2NodesMap, secondNode2ShardsMap, nodes, replicationLevel);
assertEquals(nodes.size(), thirdNode2ShardsMap.size());
assertSufficientDistribution(replicationLevel, nodes, shards, thirdNode2ShardsMap);
assertTrue( Collections.disjoint(thirdNode2ShardsMap.get("node1:20001"), thirdNode2ShardsMap.get("node1:20002")));
assertTrue( Collections.disjoint(thirdNode2ShardsMap.get("node2:20001"), thirdNode2ShardsMap.get("node2:20002")));
assertTrue( Collections.disjoint(thirdNode2ShardsMap.get("node3:20001"), thirdNode2ShardsMap.get("node3:20002")));
}
@Test
public void testTypicalDeployedDistribution() throws Exception {
int replicationLevel = 2;
List<String> nodes = createNodesByHosts(4, 6);
createNumShards(46);
Map<String, List<String>> node2ShardsMap = _distributionPolicy.createDistributionPlan(_currentShard2NodesMap,
_currentNode2ShardsMap, nodes, replicationLevel);
assertEquals(nodes.size(), node2ShardsMap.size());
assertDisjointNodesOnEachHost(4, 6, node2ShardsMap);
}
@Test
public void testEqualDistributionOnMultipleSequentialDeploys() throws Exception {
int replicationLevel = 1;
List<String> nodes = createNodes("node1:20000", "node2:20000", "node3:20000", "node4:20000");
createShards("shard1", "shard2");
Map<String, List<String>> node2ShardsMap = _distributionPolicy.createDistributionPlan(_currentShard2NodesMap,
_currentNode2ShardsMap, nodes, replicationLevel);
System.out.println(node2ShardsMap);
_currentShard2NodesMap.clear();
createShards("shard3", "shard4");
node2ShardsMap = _distributionPolicy.createDistributionPlan(_currentShard2NodesMap, _currentNode2ShardsMap, nodes,
replicationLevel);
for (String node : node2ShardsMap.keySet()) {
assertEquals("shards are not equally distributed: " + node2ShardsMap, 1, node2ShardsMap.get(node).size());
}
System.out.println(node2ShardsMap);
}
@Test
public void testInitialDistribution_TooLessNodes() throws Exception {
List<String> nodes = createNodes("node1:20000");
Set<String> shards = createShards("shard1", "shard2");
Map<String, List<String>> node2ShardsMap = _distributionPolicy.createDistributionPlan(_currentShard2NodesMap,
_currentNode2ShardsMap, nodes, 3);
assertEquals(nodes.size(), node2ShardsMap.size());
assertEquals(shards.size(), node2ShardsMap.get("node1:20000").size());
}
@Test
public void testUnderReplicatedDistribution() throws Exception {
int replicationLevel = 3;
List<String> nodes = createNodes("node1:20000", "node2:20000", "node3:20000");
Set<String> shards = createShards("shard1", "shard2", "shard3");
addMapping("shard1", "node1:20000", "node2:20000", "node3:20000");
addMapping("shard2", "node1:20000");
Map<String, List<String>> node2ShardsMap = _distributionPolicy.createDistributionPlan(_currentShard2NodesMap,
_currentNode2ShardsMap, nodes, replicationLevel);
assertEquals(nodes.size(), node2ShardsMap.size());
assertSufficientDistribution(replicationLevel, nodes, shards, node2ShardsMap);
}
@Test
public void testOverReplicatedDistribution() throws Exception {
int replicationLevel = 2;
List<String> nodes = createNodes("node1:20000", "node2:20000", "node3:20000", "node4:20000");
Set<String> shards = createShards("shard1", "shard2");
addMapping("shard1", "node1:20000", "node2:20000", "node3:20000", "node4:20000");
addMapping("shard2", "node1:20000", "node2:20000");
Map<String, List<String>> node2ShardsMap = _distributionPolicy.createDistributionPlan(_currentShard2NodesMap,
_currentNode2ShardsMap, nodes, replicationLevel);
assertEquals(nodes.size(), node2ShardsMap.size());
assertSufficientDistribution(replicationLevel, nodes, shards, node2ShardsMap);
}
private void assertSufficientDistribution(int replicationLevel, List<String> nodes, Set<String> shards,
Map<String, List<String>> node2ShardsMap) {
int deployedShardCount = 0;
for (String node : nodes) {
deployedShardCount += node2ShardsMap.get(node).size();
assertTrue(node2ShardsMap.get(node).size() >= 1);
assertTrue(node2ShardsMap.get(node).size() <= replicationLevel);
}
assertEquals(shards.size() * replicationLevel, deployedShardCount);
}
private void assertDisjointNodesOnEachHost(int numNodes, int numHosts, Map<String, List<String>> node2ShardsMap) {
for (int h = 1; h < numHosts; h++) {
for (int n = 1; n < numNodes; n++) {
String node1 = createNodeString(n, h);
String node2 = createNodeString(n+1, h);
assertTrue( Collections.disjoint( node2ShardsMap.get(node1), node2ShardsMap.get(node2)));
}
}
}
private void updateShardToNodesMap(Map<String, List<String>> node2ShardsMap) {
for (String node : node2ShardsMap.keySet()) {
for (String shard : node2ShardsMap.get(node)) {
if (!_currentShard2NodesMap.containsKey(shard)) {
_currentShard2NodesMap.put(shard, new ArrayList<String>());
}
if (!_currentShard2NodesMap.get(shard).contains(node)) {
_currentShard2NodesMap.get(shard).add(node);
}
}
}
}
private void addMapping(String shard, String... nodes) {
List<String> shardNodes = _currentShard2NodesMap.get(shard);
for (String node : nodes) {
List<String> shards = _currentNode2ShardsMap.get(node);
shards.add(shard);
shardNodes.add(node);
}
}
private List<String> createNodes(String... nodeNames) {
List<String> nodes = new ArrayList<String>();
for (String nodeName : nodeNames) {
nodes.add(nodeName);
_currentNode2ShardsMap.put(nodeName, new ArrayList<String>());
}
return nodes;
}
private Set<String> createShards(String... shardNames) {
Set<String> shards = new HashSet<String>();
for (String shardName : shardNames) {
shards.add(shardName);
if (!_currentNode2ShardsMap.containsKey(shardName)) {
_currentShard2NodesMap.put(shardName, new ArrayList<String>());
}
}
return shards;
}
private List<String> createNodesByHosts(int numNodes, int numHosts) {
List<String> nodeNames = new ArrayList<String>();
for (int n = 1; n <= numNodes; n++) {
for (int h = 1; h <= numHosts; h++) {
String node = createNodeString(n, h);
nodeNames.add(node);
}
}
return createNodes(nodeNames.toArray(new String[numNodes*numHosts]));
}
private String createNodeString(int n, int h) {
return String.format("host%d:2000%d", h, n);
}
private Set<String> createNumShards(int num) {
List<String> shardNames = new ArrayList<String>();
for (int i = 1; i <= num; i++) {
shardNames.add(String.format("shard%d", i));
}
return createShards(shardNames.toArray(new String[num]));
}
}