Skip to content

Commit

Permalink
Initial version of repartitioning tool 'RepartitionCLI'.
Browse files Browse the repository at this point in the history
RebalanceCLI
- removed all "generate" options and code from this tool (now tools/RepartitionCLI)
- removed "analysis" option from this tool (now tools/PartitionAnalysisCLI)
- Fixed errors in help/usage docs

RepartitionCLI
- Removed unnecessary options that restricted partition movement to be 'within a zone'. This decouples the repartition tool from the planning tool. This also assumes that optimizations can be done at plan time to keep all movement within a zone (when possible).
- cleaned up option names to be more concise
- Added options for target cluster/stores to allow zone expansion to be fully specified

PartitionBalance
- New class extracted from ClusterInstance
- Made all partition analysis zone-aware. This prevents zones with fewer nodes, or partitions, from skewing any analysis. I.e., each zone's balance analysis is now correctly normalized to be comparable with other zones' balance analyses.
- added inner ZoneBalanceStats class that makes stats tracking clearer
- added getUtility methods to hide the exact utiliyt method from the interfaces
- Changed utility methods to sum over all zones and to combine zone primary analysis with nary analysis.

RebalanceClusterUtils.java
- changed/extended balancePrimaryPartitions to handle zone expansion
- Stripped out a bunch of unnecessary options/code. I.e., options/code that tried to minimize cross zone moves during repartitioning (rather than during planning!)
- Added some helper methods to clean up repeated code.

RebalanceUtilsTest
- test new helper methods
  • Loading branch information
jayjwylie committed Jun 20, 2013
1 parent 4f1c00b commit 92c1139
Show file tree
Hide file tree
Showing 5 changed files with 475 additions and 542 deletions.
183 changes: 3 additions & 180 deletions src/java/voldemort/client/rebalance/RebalanceCLI.java
Expand Up @@ -30,11 +30,8 @@
import voldemort.VoldemortException;
import voldemort.cluster.Cluster;
import voldemort.store.StoreDefinition;
import voldemort.utils.ClusterInstance;
import voldemort.utils.CmdUtils;
import voldemort.utils.Entropy;
import voldemort.utils.PartitionBalance;
import voldemort.utils.RebalanceClusterUtils;
import voldemort.xml.ClusterMapper;
import voldemort.xml.StoreDefinitionsMapper;

Expand All @@ -47,13 +44,6 @@ public class RebalanceCLI {
private final static int HELP_EXIT_CODE = 2;
private final static Logger logger = Logger.getLogger(RebalanceCLI.class);

private final static int DEFAULT_GENERATE_RANDOM_SWAP_ATTEMPTS = 100;
private final static int DEFAULT_GENERATE_RANDOM_SWAP_SUCCESSES = 100;
private final static int DEFAULT_GENERATE_GREEDY_SWAP_ATTEMPTS = 5;
private final static int DEFAULT_GENERATE_GREEDY_MAX_PARTITIONS_PER_NODE = 5;
private final static int DEFAULT_GENERATE_GREEDY_MAX_PARTITIONS_PER_ZONE = 25;
private final static int DEFAULT_GENERATE_MAX_CONTIGUOUS_PARTITIONS = 0;

public static void main(String[] args) throws Exception {
int exitCode = ERROR_EXIT_CODE;
RebalanceController rebalanceController = null;
Expand Down Expand Up @@ -83,60 +73,6 @@ public static void main(String[] args) throws Exception {
.withRequiredArg()
.ofType(Integer.class)
.describedAs("num-tries");
parser.accepts("generate",
"Optimize the target cluster which has new nodes with empty partitions");
parser.accepts("generate-disable-primary-balancing",
"Make sure that all nodes within every zone have the same (within one) number of primary partitions [default: enabled]");
parser.accepts("generate-enable-xzone-primary-moves",
"Allow primary partitions to move across zones [Default: disabled]");
parser.accepts("generate-enable-any-xzone-nary-moves",
"Allow non-primary partitions to move across zones at any time (i.e., does not check for xzone moves) [Default: disabled]");
parser.accepts("generate-enable-last-resort-xzone-nary-moves",
"Allow non-primary partitions to move across zones as a last resort (i.e., checks for xzone moves and prefers to avoid them, unless a xzone move is required to achieve balance) [Default: disabled]");
parser.accepts("generate-enable-xzone-shuffle",
"Allows non-primary partitions to move across zones in random or greedy shuffles. [Default: disabled]");
parser.accepts("generate-enable-random-swaps",
"Enable attempts to improve balance by random partition swaps within a zone. [Default: disabled]");
parser.accepts("generate-random-swap-attempts",
"Number of random swaps to attempt. [Default:"
+ DEFAULT_GENERATE_RANDOM_SWAP_ATTEMPTS + " ]")
.withRequiredArg()
.ofType(Integer.class)
.describedAs("num-attempts");
parser.accepts("generate-random-swap-successes",
"Number of successful random swaps to permit exit before completing all swap attempts. [Default:"
+ DEFAULT_GENERATE_RANDOM_SWAP_SUCCESSES + " ]")
.withRequiredArg()
.ofType(Integer.class)
.describedAs("num-successes");
parser.accepts("generate-enable-greedy-swaps",
"Enable attempts to improve balance by greedily swapping (random) partitions within a zone. [Default: disabled]");
parser.accepts("generate-greedy-swap-attempts",
"Number of greedy (random) swaps to attempt. [Default:"
+ DEFAULT_GENERATE_GREEDY_SWAP_ATTEMPTS + " ]")
.withRequiredArg()
.ofType(Integer.class)
.describedAs("num-attempts");
parser.accepts("generate-greedy-max-partitions-per-node",
"Max number of partitions per-node to evaluate swapping with other partitions within the zone. [Default:"
+ DEFAULT_GENERATE_GREEDY_MAX_PARTITIONS_PER_NODE + " ]")
.withRequiredArg()
.ofType(Integer.class)
.describedAs("max-partitions-per-node");
parser.accepts("generate-greedy-max-partitions-per-zone",
"Max number of (random) partitions per-zone to evaluate swapping with partitions from node being evaluated. [Default:"
+ DEFAULT_GENERATE_GREEDY_MAX_PARTITIONS_PER_ZONE + " ]")
.withRequiredArg()
.ofType(Integer.class)
.describedAs("max-partitions-per-zone");
parser.accepts("generate-max-contiguous-partitions",
"Limit the number of contiguous partition IDs allowed within a zone. [Default:"
+ DEFAULT_GENERATE_MAX_CONTIGUOUS_PARTITIONS
+ " (indicating no limit)]")
.withRequiredArg()
.ofType(Integer.class)
.describedAs("num-contiguous");
parser.accepts("analyze", "Analyze how balanced given cluster is.");
parser.accepts("entropy",
"True - if we want to run the entropy calculator. False - if we want to store keys")
.withRequiredArg()
Expand Down Expand Up @@ -202,31 +138,6 @@ public static void main(String[] args) throws Exception {
boolean stealerBasedRebalancing = CmdUtils.valueOf(options,
"stealer-based",
RebalanceClientConfig.STEALER_BASED_REBALANCING);
boolean generateDisablePrimaryBalancing = options.has("generate-disable-primary-balancing");
boolean generateEnableXzonePrimary = options.has("generate-enable-xzone-primary-moves");
boolean generateEnableAllXzoneNary = options.has("generate-enable-any-xzone-nary-moves");
boolean generateEnableLastResortXzoneNary = options.has("generate-enable-last-resort-xzone-nary-moves");
boolean generateEnableXzoneShuffle = options.has("generate-enable-xzone-shuffle");
boolean generateEnableRandomSwaps = options.has("generate-enable-random-swaps");
int generateRandomSwapAttempts = CmdUtils.valueOf(options,
"generate-random-swap-attempts",
DEFAULT_GENERATE_RANDOM_SWAP_ATTEMPTS);
int generateRandomSwapSuccesses = CmdUtils.valueOf(options,
"generate-random-swap-successes",
DEFAULT_GENERATE_RANDOM_SWAP_SUCCESSES);
boolean generateEnableGreedySwaps = options.has("generate-enable-greedy-swaps");
int generateGreedySwapAttempts = CmdUtils.valueOf(options,
"generate-greedy-swap-attempts",
DEFAULT_GENERATE_GREEDY_SWAP_ATTEMPTS);
int generateGreedyMaxPartitionsPerNode = CmdUtils.valueOf(options,
"generate-greedy-max-partitions-per-node",
DEFAULT_GENERATE_GREEDY_MAX_PARTITIONS_PER_NODE);
int generateGreedyMaxPartitionsPerZone = CmdUtils.valueOf(options,
"generate-greedy-max-partitions-per-zone",
DEFAULT_GENERATE_GREEDY_MAX_PARTITIONS_PER_ZONE);
int generateMaxContiguousPartitionsPerZone = CmdUtils.valueOf(options,
"generate-max-contiguous-partitions",
DEFAULT_GENERATE_MAX_CONTIGUOUS_PARTITIONS);

RebalanceClientConfig config = new RebalanceClientConfig();
config.setMaxParallelRebalancing(parallelism);
Expand Down Expand Up @@ -273,6 +184,7 @@ public static void main(String[] args) throws Exception {
Cluster currentCluster = new ClusterMapper().readCluster(new File(currentClusterXML));
List<StoreDefinition> storeDefs = new StoreDefinitionsMapper().readStoreList(new File(currentStoresXML));

// TODO: Remove this option.
if(options.has("entropy")) {

if(!config.hasOutputDirectory()) {
Expand All @@ -293,13 +205,6 @@ public static void main(String[] args) throws Exception {

}

if(options.has("analyze")) {
PartitionBalance partitionBalance = new ClusterInstance(currentCluster,
storeDefs).getPartitionBalance();
System.out.println(partitionBalance);
return;
}

if(!options.has("target-cluster")) {
System.err.println("Missing required arguments: target-cluster");
printHelp(System.err, parser);
Expand All @@ -309,65 +214,6 @@ public static void main(String[] args) throws Exception {
String targetClusterXML = (String) options.valueOf("target-cluster");
Cluster targetCluster = new ClusterMapper().readCluster(new File(targetClusterXML));

if(options.has("generate")) {
if(generateDisablePrimaryBalancing && !generateEnableRandomSwaps
&& !generateEnableGreedySwaps && generateMaxContiguousPartitionsPerZone == 0) {
System.err.println("Specified generate but did not enable any forms for generation (balance primary partitoins, greedy swaps, random swaps, max contiguous partitions).");
printHelp(System.err, parser);
System.exit(ERROR_EXIT_CODE);
}
if((options.has("generate-random-swap-attempts") || options.has("generate-random-swap-successes"))
&& !generateEnableRandomSwaps) {
System.err.println("Provided arguments for generate random swaps but disabled the feature");
printHelp(System.err, parser);
System.exit(ERROR_EXIT_CODE);
}
if((options.has("generate-greedy-swap-attempts")
|| options.has("generate-greedy-max-partitions-per-node") || options.has("generate-greedy-max-partitions-per-zone"))
&& !generateEnableGreedySwaps) {
System.err.println("Provided arguments for generate greedy swaps but disabled the feature");
printHelp(System.err, parser);
System.exit(ERROR_EXIT_CODE);
}
if(generateEnableAllXzoneNary && generateEnableLastResortXzoneNary) {
System.err.println("Specified both generate-enable-any-xzone-nary-moves and generate-enable-last-resort-xzone-nary-moves. Please specify at most one of these mutually exclusive options.");
printHelp(System.err, parser);
System.exit(ERROR_EXIT_CODE);
}
if(generateDisablePrimaryBalancing
&& (generateEnableAllXzoneNary || generateEnableLastResortXzoneNary)) {
System.err.println("Specified generate-disable-primary-balancing but also specified either generate-enable-any-xzone-nary-moves or generate-enable-last-resort-xzone-nary-moves which will have no effect.");
printHelp(System.err, parser);
System.exit(ERROR_EXIT_CODE);
}
if(generateEnableXzoneShuffle
&& !(generateEnableRandomSwaps || generateEnableGreedySwaps)) {
System.err.println("Specified generate-enable-xzone-shuffle but did not specify one of generate-enable-random-swaps or generate-enable-greedy-swaps.");
printHelp(System.err, parser);
System.exit(ERROR_EXIT_CODE);
}

RebalanceClusterUtils.balanceTargetCluster(currentCluster,
targetCluster,
storeDefs,
config.getOutputDirectory(),
config.getMaxTriesRebalancing(),
generateDisablePrimaryBalancing,
generateEnableXzonePrimary,
generateEnableAllXzoneNary,
generateEnableLastResortXzoneNary,
generateEnableXzoneShuffle,
generateEnableRandomSwaps,
generateRandomSwapAttempts,
generateRandomSwapSuccesses,
generateEnableGreedySwaps,
generateGreedySwapAttempts,
generateGreedyMaxPartitionsPerNode,
generateGreedyMaxPartitionsPerZone,
generateMaxContiguousPartitionsPerZone);
return;
}

rebalanceController = new RebalanceController(currentCluster, config);
rebalanceController.rebalance(currentCluster, targetCluster, storeDefs);

Expand Down Expand Up @@ -402,38 +248,15 @@ public static void printHelp(PrintStream stream, OptionParser parser) throws IOE
stream.println();
stream.println("REBALANCE (GENERATE PLAN)");
stream.println("b) --current-cluster <path> --current-stores <path> --target-cluster <path>");
stream.println("\t (1) --no-delete [ Will not delete the data after rebalancing ]");
stream.println("\t (1) --delete [ Will delete the data after rebalancing ]");
stream.println("\t (2) --show-plan [ Will generate only the plan ]");
stream.println("\t (3) --output-dir [ Path to output dir where we store intermediate metadata ]");
stream.println("\t (4) --parallelism [ Number of parallel stealer - donor node tasks to run in parallel ] ");
stream.println("\t (5) --tries [ Number of times we try to move the data before declaring failure ]");
stream.println("\t (6) --timeout [ Timeout in seconds for one rebalancing task ( stealer - donor tuple ) ]");
stream.println("\t (7) --batch [ Number of primary partitions to move together ]");
stream.println("\t (8) --stealer-based [ Run the rebalancing from the stealers perspective ]");
stream.println("\t (8) --stealer-based false [ Run the rebalancing from the donor's perspective ]");

stream.println();
stream.println("GENERATE");
stream.println("a) --current-cluster <path> --current-stores <path> --target-cluster <path> --generate [ Generates a new cluster xml with least number of movements."
+ " Uses target cluster i.e. current-cluster + new nodes ( with empty partitions ) ]");
stream.println("\t (1) --output-dir [ Output directory is where we store the optimized cluster ]");
stream.println("\t (2) --tries [ Number of optimization cycles ] ");
stream.println("\t (3) --generate-disable-primary-balancing [ Do not balance number of primary partitions across nodes within each zone ] ");
stream.println("\t (4) --generate-enable-xzone-primary-moves [ Allow primary partitions to move across zones ] ");
stream.println("\t (5) --generate-enable-any-xzone-nary-moves [ Allow non-primary partitions to move across zones. ]");
stream.println("\t (6) --generate-enable-last-resort-xzone-nary-moves [ Allow non-primary partitions to move across zones as a last resort --- Will only do such a move if all possible moves result in xzone move.] ");
stream.println("\t (7) --generate-enable-xzone-shuffle [ Allow non-primary partitions to move across zones for random swaps or greedy swaps.] ");
stream.println("\t (8) --generate-enable-random-swaps [ Attempt to randomly swap partitions within a zone to improve balance ] ");
stream.println("\t (9) --generate-random-swap-attempts num-attempts [ Number of random swaps to attempt in hopes of improving balance ] ");
stream.println("\t(10) --generate-random-swap-successes num-successes [ Stop after num-successes successful random swap atttempts ] ");
stream.println("\t(11) --generate-enable-greedy-swaps [ Attempt to greedily (randomly) swap partitions within a zone to improve balance. Greedily/randomly means sample many swaps for each node and choose best swap. ] ");
stream.println("\t(12) --generate-greedy-swap-attempts num-attempts [ Number of greedy swap passes to attempt. Each pass can be fairly expensive. ] ");
stream.println("\t(13) --generate-greedy-max-partitions-per-node num-partitions [ num-partitions per node to consider in each greedy pass. Partitions selected randomly from each node. ] ");
stream.println("\t(14) --generate-greedy-max-partitions-per-zone num-partitions [ num-partitions per zone to consider in each greedy pass. Partitions selected randomly from all partitions in zone not on node being considered. ] ");
stream.println("\t(15) --generate-max-contiguous-partitions num-contiguous [ Max allowed contiguous partition IDs within a zone ] ");

stream.println();
stream.println("ANALYZE");
stream.println("a) --current-cluster <path> --current-stores <path> --analyze [ Analyzes a cluster xml for balance]");
stream.println();
stream.println("ENTROPY");
stream.println("a) --current-cluster <path> --current-stores <path> --entropy <true / false> --output-dir <path> [ Runs the entropy calculator if "
Expand Down

0 comments on commit 92c1139

Please sign in to comment.