Skip to content

Commit

Permalink
Added unit tests for RebalancePlan and for cluster xform & verificati…
Browse files Browse the repository at this point in the history
…on methods in RebalanceUtils

RebalancePlanTest
- Test the core use cases for rebalance planning:
  - no-op,
  - rebalance (zone/node topology same, but partition id layout changed),
  - cluster expansion (zone topology same, but new nodes added & partitions moved to them),
  - zone expansion (zone topoloigy changes with partitions moved to new zone).

RebalanceController
- rename method

RebalancePlan
- added getters for aggregate plan statistics

ServerTestUtils
- Changed getLocalZonedCluster to accept array of node IDs. Necessary to generalize other test methods.

ClusterInstanceTest
- pass storage type into helper methods that construct store defs
- chnagned all get???Cluster.*() methods to use new getLocalZonedCluster interface. Also added more such helper methods.

RebalanceUtilsTest
- Test the cluster transformation & verification methods

RepartitionUtilsTest
- simple clean up
  • Loading branch information
jayjwylie committed Jun 20, 2013
1 parent 622ee42 commit 4f0087f
Show file tree
Hide file tree
Showing 9 changed files with 493 additions and 86 deletions.
Expand Up @@ -140,7 +140,7 @@ public void rebalance(Cluster currentCluster,
// Do some verification
if(!rebalanceConfig.isShowPlanEnabled()) {
// Now validate that all the nodes ( new + old ) are in normal state
RebalanceUtils.validateClusterState(newCurrentCluster, adminClient);
RebalanceUtils.validateProdClusterStateIsNormal(newCurrentCluster, adminClient);

// Verify all old RO stores exist at version 2
RebalanceUtils.validateReadOnlyStores(newCurrentCluster, storeDefs, adminClient);
Expand Down
20 changes: 20 additions & 0 deletions src/java/voldemort/client/rebalance/RebalancePlan.java
Expand Up @@ -261,6 +261,26 @@ public List<RebalanceClusterPlan> getPlan() {
return batchPlans;
}

public int getPrimariesMoved() {
return numPrimaryPartitionMoves;
}

public int getPartitionStoresMoved() {
return numPartitionStoreMoves;
}

public int getPartitionStoresMovedXZone() {
return numXZonePartitionStoreMoves;
}

public MoveMap getNodeMoveMap() {
return nodeMoveMap;
}

public MoveMap getZoneMoveMap() {
return zoneMoveMap;
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Expand Down
3 changes: 2 additions & 1 deletion src/java/voldemort/routing/StoreRoutingPlan.java
Expand Up @@ -87,7 +87,8 @@ public List<Integer> getReplicatingPartitionList(int masterPartitionId) {
return this.routingStrategy.getReplicatingPartitionList(masterPartitionId);
}

// TODO: Add test of this method.
// TODO: Add test for this method (if this method is still required after
// the RebalanceController is updated to use RebalancePlan).
/**
*
* @param nodeId
Expand Down
15 changes: 7 additions & 8 deletions src/java/voldemort/utils/RebalanceUtils.java
Expand Up @@ -253,7 +253,8 @@ public static void assertSameDonor(List<RebalancePartitionsInfo> partitionInfos,
* @param adminClient Admin client used to query
* @throws VoldemortRebalancingException if any node is not in normal state
*/
public static void validateClusterState(final Cluster cluster, final AdminClient adminClient) {
public static void validateProdClusterStateIsNormal(final Cluster cluster,
final AdminClient adminClient) {
for(Node node: cluster.getNodes()) {
Versioned<VoldemortState> versioned = adminClient.rebalanceOps.getRemoteServerState(node.getId());

Expand All @@ -271,7 +272,6 @@ public static void validateClusterState(final Cluster cluster, final AdminClient
}
}

// TODO: Add tests for all cluster/store validation methods.
/**
* Verify store definitions are congruent with cluster definition.
*
Expand All @@ -280,12 +280,11 @@ public static void validateClusterState(final Cluster cluster, final AdminClient
*/
public static void validateClusterStores(final Cluster cluster,
final List<StoreDefinition> storeDefs) {
// This is a hack. But, constructing a PartitionBalance object has
// the (desirable in this case) side-effect of verifying that the store
// definition is congruent with the cluster definition. If there are
// issues, exceptions are thrown.
@SuppressWarnings("unused")
PartitionBalance pb = new PartitionBalance(cluster, storeDefs);
// Constructing a PartitionBalance object has the (desirable in this
// case) side-effect of verifying that the store definition is congruent
// with the cluster definition. If there are issues, exceptions are
// thrown.
new PartitionBalance(cluster, storeDefs);
return;
}

Expand Down
38 changes: 20 additions & 18 deletions test/common/voldemort/ServerTestUtils.java
Expand Up @@ -496,32 +496,35 @@ public static Cluster getLocalZonedCluster(int numberOfNodes,
* a nodes in <b>numberOfZones</b> zones. It is important that
* <b>numberOfNodes</b> be divisible by <b>numberOfZones</b>
*
* Does
*
* @param numberOfZones The number of zones in the cluster.
* @param nodesPerZone An array of size <b>numberOfZones<b> indicating the
* number of nodes in each zone.
* @param partitionMap An array of size total number of nodes (derived from
* @param nodeIdsPerZone An array of size <b>numberOfZones<b> in which each
* internal array is a node ID.
* @param partitionMap An array of size total number of nodes (derived from
* <b>nodesPerZone<b> that indicates the specific partitions on each
* node.
* @return
*/
// TODO: Method should eventually accept a list of ZoneIds so that
// non-contig zone Ids can be tested.
public static Cluster getLocalZonedCluster(int numberOfZones,
int[] nodesPerZone,
int[][] nodeIdsPerZone,
int[][] partitionMap) {

if(numberOfZones < 1) {
throw new VoldemortException("The number of zones must be positive (" + numberOfZones
+ ")");
}
if(nodesPerZone.length != numberOfZones) {
if(nodeIdsPerZone.length != numberOfZones) {
throw new VoldemortException("Mismatch between numberOfZones (" + numberOfZones
+ ") and size of nodesPerZone array (" + nodesPerZone
+ ").");
+ ") and size of nodesPerZone array ("
+ nodeIdsPerZone.length + ").");
}

int numNodes = 0;
for(Integer nodesInZone: nodesPerZone) {
numNodes += nodesInZone;
for(int nodeIdsInZone[]: nodeIdsPerZone) {
numNodes += nodeIdsInZone.length;
}
if(partitionMap.length != numNodes) {
throw new VoldemortException("Mismatch between numNodes (" + numNodes
Expand All @@ -531,22 +534,21 @@ public static Cluster getLocalZonedCluster(int numberOfZones,

// Generate nodes
List<Node> nodes = new ArrayList<Node>();
int[] ports = findFreePorts(3 * numNodes);
int nodeId = 0;
int partitionMapOffset = 0;
for(int zoneId = 0; zoneId < numberOfZones; zoneId++) {
for(int j = 0; j < nodesPerZone[zoneId]; j++) {
for(int nodeId: nodeIdsPerZone[zoneId]) {
List<Integer> partitions = new ArrayList<Integer>(partitionMap[nodeId].length);
for(int p: partitionMap[nodeId]) {
for(int p: partitionMap[partitionMapOffset]) {
partitions.add(p);
}
nodes.add(new Node(nodeId,
"localhost",
ports[3 * nodeId],
ports[3 * nodeId + 1],
ports[3 * nodeId + 2],
"node-" + nodeId,
64000,
64001,
64002,
zoneId,
partitions));
nodeId++;
partitionMapOffset++;
}
}

Expand Down
143 changes: 143 additions & 0 deletions test/unit/voldemort/client/rebalance/RebalancePlanTest.java
@@ -0,0 +1,143 @@
/*
* Copyright 2012-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.client.rebalance;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.List;

import org.junit.BeforeClass;
import org.junit.Test;

import voldemort.cluster.Cluster;
import voldemort.store.StoreDefinition;
import voldemort.utils.ClusterInstanceTest;

public class RebalancePlanTest {

static Cluster zzCurrent;
static Cluster zzRebalance;
static Cluster zzClusterExpansion;
static List<StoreDefinition> zzStores;

static Cluster zzzCurrent;
static Cluster zzzRebalance;
static Cluster zzzClusterExpansion;
static Cluster zzzZoneExpansion;
static List<StoreDefinition> zzzStores;

@BeforeClass
public static void setup() {
zzCurrent = ClusterInstanceTest.getZZCluster();
zzRebalance = ClusterInstanceTest.getZZClusterWithSwappedPartitions();
zzClusterExpansion = ClusterInstanceTest.getZZClusterWithPP();
zzStores = ClusterInstanceTest.getZZStoreDefsBDB();

zzzCurrent = ClusterInstanceTest.getZZZCluster();

zzzRebalance = ClusterInstanceTest.getZZZClusterWithSwappedPartitions();
zzzClusterExpansion = ClusterInstanceTest.getZZZClusterWithPPP();
zzzZoneExpansion = ClusterInstanceTest.getZZEClusterXXP();
zzzStores = ClusterInstanceTest.getZZZStoreDefsBDB();
}

RebalancePlan makePlan(Cluster cCluster,
List<StoreDefinition> cStores,
Cluster fCluster,
List<StoreDefinition> fStores) {
// Defaults for plans
boolean stealerBased = true;
int batchSize = 1000;
String outputDir = null;

return new RebalancePlan(cCluster,
cStores,
fCluster,
fStores,
stealerBased,
batchSize,
outputDir);
}

@Test
public void testNoop() {
RebalancePlan rebalancePlan;

// Two zones
rebalancePlan = makePlan(zzCurrent, zzStores, zzCurrent, zzStores);
assertEquals(rebalancePlan.getPlan().size(), 0);
assertEquals(rebalancePlan.getPrimariesMoved(), 0);
assertEquals(rebalancePlan.getPartitionStoresMoved(), 0);
assertEquals(rebalancePlan.getPartitionStoresMovedXZone(), 0);

// Three zones
rebalancePlan = makePlan(zzzCurrent, zzzStores, zzzCurrent, zzzStores);
assertEquals(rebalancePlan.getPlan().size(), 0);
assertEquals(rebalancePlan.getPrimariesMoved(), 0);
assertEquals(rebalancePlan.getPartitionStoresMoved(), 0);
assertEquals(rebalancePlan.getPartitionStoresMovedXZone(), 0);
}

@Test
public void testRebalance() {
RebalancePlan rebalancePlan;

// Two zones
rebalancePlan = makePlan(zzCurrent, zzStores, zzRebalance, zzStores);
assertEquals(rebalancePlan.getPlan().size(), 1);
assertTrue(rebalancePlan.getPrimariesMoved() > 0);
assertTrue(rebalancePlan.getPartitionStoresMoved() > 0);
assertEquals(rebalancePlan.getPartitionStoresMovedXZone(), 0);

// Three zones
rebalancePlan = makePlan(zzzCurrent, zzzStores, zzzRebalance, zzzStores);
assertEquals(rebalancePlan.getPlan().size(), 1);
assertTrue(rebalancePlan.getPrimariesMoved() > 0);
assertTrue(rebalancePlan.getPartitionStoresMoved() > 0);
assertEquals(rebalancePlan.getPartitionStoresMovedXZone(), 0);
}

@Test
public void testClusterExpansion() {
RebalancePlan rebalancePlan;

// Two zones
rebalancePlan = makePlan(zzCurrent, zzStores, zzClusterExpansion, zzStores);
assertEquals(rebalancePlan.getPlan().size(), 1);
assertTrue(rebalancePlan.getPrimariesMoved() > 0);
assertTrue(rebalancePlan.getPartitionStoresMoved() > 0);
assertEquals(rebalancePlan.getPartitionStoresMovedXZone(), 0);

// Three zones
rebalancePlan = makePlan(zzzCurrent, zzzStores, zzzClusterExpansion, zzzStores);
assertEquals(rebalancePlan.getPlan().size(), 1);
assertTrue(rebalancePlan.getPrimariesMoved() > 0);
assertTrue(rebalancePlan.getPartitionStoresMoved() > 0);
assertEquals(rebalancePlan.getPartitionStoresMovedXZone(), 0);
}

@Test
public void testZoneExpansion() {
RebalancePlan rebalancePlan;

rebalancePlan = makePlan(zzCurrent, zzStores, zzzZoneExpansion, zzzStores);
assertEquals(rebalancePlan.getPlan().size(), 1);
assertTrue(rebalancePlan.getPrimariesMoved() > 0);
assertTrue(rebalancePlan.getPartitionStoresMoved() > 0);
assertTrue(rebalancePlan.getPartitionStoresMovedXZone() > 0);
}
}

0 comments on commit 4f0087f

Please sign in to comment.