/*
* Copyright 2011-2013 LinkedIn, Inc
*
* 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 voldemort.utils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import voldemort.ClusterTestUtils;
import voldemort.ServerTestUtils;
import voldemort.VoldemortException;
import voldemort.cluster.Cluster;
import voldemort.cluster.Node;
import com.google.common.collect.Lists;
public class RebalanceUtilsTest {
@Test
public void testUpdateCluster() {
Cluster currentCluster = ServerTestUtils.getLocalCluster(2, new int[][] {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8 }, {} });
Cluster finalCluster = ServerTestUtils.getLocalCluster(2, new int[][] {
{ 0, 1, 4, 5, 6, 7, 8 }, { 2, 3 } });
Cluster updatedCluster = UpdateClusterUtils.updateCluster(currentCluster,
new ArrayList<Node>(finalCluster.getNodes()));
assertEquals("updated cluster should match finalCluster", updatedCluster, finalCluster);
}
@Test
public void testGetNodeIds() {
List<Node> nodes = Lists.newArrayList();
// Test with empty node list
assertEquals(Utils.nodeListToNodeIdList(nodes).size(), 0);
// Add one node
nodes.add(new Node(0, "localhost", 1, 2, 3, new ArrayList<Integer>()));
assertEquals(Utils.nodeListToNodeIdList(nodes).size(), 1);
assertEquals(Utils.nodeListToNodeIdList(nodes).get(0).intValue(), 0);
}
@Test
public void testGetClusterWithNewNodes() {
Cluster cluster = ServerTestUtils.getLocalCluster(2, 10, 1);
// Generate a new cluster which contains 4 nodes instead of 2
List<Node> nodes = Lists.newArrayList();
for(int nodeId = 0; nodeId < 4; nodeId++) {
List<Integer> partitionIds = Lists.newArrayList();
for(int partitionId = nodeId * 5; partitionId < (nodeId + 1) * 5; partitionId++) {
partitionIds.add(partitionId);
}
Node node = new Node(nodeId, "b", 0, 1, 2, 0, partitionIds);
nodes.add(node);
}
Cluster newCluster = new Cluster(cluster.getName(),
nodes,
Lists.newArrayList(cluster.getZones()));
Cluster generatedCluster = RebalanceUtils.getInterimCluster(cluster, newCluster);
assertEquals(generatedCluster.getNumberOfNodes(), 4);
assertEquals(Utils.compareList(generatedCluster.getNodeById(0).getPartitionIds(),
cluster.getNodeById(0).getPartitionIds()), true);
assertEquals(Utils.compareList(generatedCluster.getNodeById(1).getPartitionIds(),
cluster.getNodeById(1).getPartitionIds()), true);
assertEquals(generatedCluster.getNodeById(2).getPartitionIds().size(), 0);
assertEquals(generatedCluster.getNodeById(3).getPartitionIds().size(), 0);
}
private void doClusterTransformationBase(Cluster currentC,
Cluster interimC,
Cluster finalC,
boolean verify) {
Cluster derivedInterim1 = RebalanceUtils.getInterimCluster(currentC, interimC);
if(verify)
assertEquals(interimC, derivedInterim1);
Cluster derivedInterim2 = RebalanceUtils.getInterimCluster(currentC, finalC);
if(verify)
assertEquals(interimC, derivedInterim2);
RebalanceUtils.validateCurrentFinalCluster(currentC, finalC);
RebalanceUtils.validateCurrentInterimCluster(currentC, interimC);
RebalanceUtils.validateInterimFinalCluster(interimC, finalC);
}
private void doClusterTransformation(Cluster currentC, Cluster interimC, Cluster finalC) {
doClusterTransformationBase(currentC, interimC, finalC, false);
}
public void doClusterTransformationAndVerification(Cluster currentC,
Cluster interimC,
Cluster finalC) {
doClusterTransformationBase(currentC, interimC, finalC, true);
}
@Test
public void testClusterTransformationAndVerification() {
// Two-zone cluster: no-op
doClusterTransformationAndVerification(ClusterTestUtils.getZZCluster(),
ClusterTestUtils.getZZCluster(),
ClusterTestUtils.getZZCluster());
// Two-zone cluster: rebalance
doClusterTransformationAndVerification(ClusterTestUtils.getZZCluster(),
ClusterTestUtils.getZZCluster(),
ClusterTestUtils.getZZClusterWithSwappedPartitions());
// Two-zone cluster: cluster expansion
doClusterTransformationAndVerification(ClusterTestUtils.getZZCluster(),
ClusterTestUtils.getZZClusterWithNN(),
ClusterTestUtils.getZZClusterWithPP());
// Three-zone cluster: no-op
doClusterTransformationAndVerification(ClusterTestUtils.getZZZCluster(),
ClusterTestUtils.getZZZCluster(),
ClusterTestUtils.getZZZCluster());
// Three-zone cluster: rebalance
doClusterTransformationAndVerification(ClusterTestUtils.getZZZCluster(),
ClusterTestUtils.getZZZCluster(),
ClusterTestUtils.getZZZClusterWithSwappedPartitions());
// Three-zone cluster: cluster expansion
doClusterTransformationAndVerification(ClusterTestUtils.getZZZCluster(),
ClusterTestUtils.getZZZClusterWithNNN(),
ClusterTestUtils.getZZZClusterWithPPP());
doClusterTransformationAndVerification(ClusterTestUtils.getZZCluster(),
ClusterTestUtils.getZZECluster(),
ClusterTestUtils.getZZEClusterXXP());
}
@Test
public void testClusterTransformationAndVerificationExceptions() {
boolean excepted;
// Two-zone cluster: rebalance with extra partitions in interim cluster
excepted = false;
try {
doClusterTransformation(ClusterTestUtils.getZZCluster(),
ClusterTestUtils.getZZClusterWithExtraPartitions(),
ClusterTestUtils.getZZClusterWithSwappedPartitions());
} catch(VoldemortException ve) {
excepted = true;
}
assertTrue(excepted);
// Two-zone cluster: rebalance with extra partitions in final
excepted = false;
try {
doClusterTransformation(ClusterTestUtils.getZZCluster(),
ClusterTestUtils.getZZCluster(),
ClusterTestUtils.getZZClusterWithExtraPartitions());
} catch(VoldemortException ve) {
excepted = true;
}
assertTrue(excepted);
// Two-zone cluster: node ids swapped in interim cluster
excepted = false;
try {
doClusterTransformation(ClusterTestUtils.getZZCluster(),
ClusterTestUtils.getZZClusterWithNNWithSwappedNodeIds(),
ClusterTestUtils.getZZClusterWithPP());
} catch(VoldemortException ve) {
excepted = true;
}
assertTrue(excepted);
// Two-zone cluster: node ids swapped in final is OK because this is the
// same as partitions being migrated among nodes.
excepted = false;
try {
doClusterTransformation(ClusterTestUtils.getZZCluster(),
ClusterTestUtils.getZZClusterWithNN(),
ClusterTestUtils.getZZClusterWithPPWithSwappedNodeIds());
} catch(VoldemortException ve) {
excepted = true;
}
assertFalse(excepted);
// Two-zone cluster: too many node ids in final
excepted = false;
try {
doClusterTransformation(ClusterTestUtils.getZZCluster(),
ClusterTestUtils.getZZClusterWithNN(),
ClusterTestUtils.getZZClusterWithPPWithTooManyNodes());
} catch(VoldemortException ve) {
excepted = true;
}
assertTrue(excepted);
}
}