/*
* Copyright 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 java.io.IOException;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
public class ConsistencyFixCLI {
public static void printUsage() {
StringBuilder sb = new StringBuilder();
sb.append("\n");
sb.append("Required arguments: \n");
sb.append("\t--url <url>\n");
sb.append("\t--store <storeName>\n");
sb.append("\t--bad-key-file-in <FileNameOfInputListOfKeysToFix>\n");
sb.append("\t--bad-key-file-out <FileNameOfOutputListOfKeysNotFixed>)\n");
sb.append("Optional arguments: \n");
sb.append("\t--orphan-format\n");
sb.append("\t--dry-run\n");
sb.append("\t--progress-period-ops <progressPeriodOps>\n");
sb.append("\t--parallelism <parallelism>\n");
sb.append("\t--per-server-iops-limit <perServerIOPSLimit>\n");
sb.append("\n");
System.out.println(sb.toString());
}
public static void printUsage(String errMessage, OptionParser parser) {
System.err.println("Error: " + errMessage);
try {
parser.printHelpOn(System.out);
} catch(IOException ioe) {
System.err.println("Caught IOException while trying to print out parser options: "
+ ioe.getMessage());
}
printUsage();
System.exit(1);
}
private static class Options {
public final static int defaultParallelism = 8;
public final static long defaultProgressPeriodOps = 1000;
public final static long defaultPerServerQPSLimit = 100;
public String url = null;
public String storeName = null;
public String badKeyFileIn = null;
public boolean badKeyFileInOrphanFormat = false;
public String badKeyFileOut = null;
public int parallelism = defaultParallelism;
public long progressPeriodOps = defaultProgressPeriodOps;
public long perServerQPSLimit = defaultPerServerQPSLimit;
public boolean dryRun = false;
public boolean parseOnly = false;
}
/**
* All the logic for parsing and validating options.
*
* @param args
* @return A struct containing validated options.
* @throws IOException
*/
private static ConsistencyFixCLI.Options parseArgs(String[] args) {
OptionParser parser = new OptionParser();
parser.accepts("help", "print help information");
parser.accepts("url", "The bootstrap url.")
.withRequiredArg()
.describedAs("bootstrapUrl")
.ofType(String.class);
parser.accepts("store", "The store name.")
.withRequiredArg()
.describedAs("storeName")
.ofType(String.class);
parser.accepts("bad-key-file-in",
"Name of bad-key-file-in. " + "Each key must be in hexadecimal format. "
+ "Each key must be on a separate line in the file. ")
.withRequiredArg()
.describedAs("badKeyFileIn")
.ofType(String.class);
parser.accepts("orphan-format",
"Indicates format of bad-key-file-in is of 'orphan' key-values.");
parser.accepts("dry-run",
"Indicates to go through all of the read actions until the point of issuing repair puts. Then, do a 'no-op'.");
parser.accepts("parse-only",
"Indicates to only parse the input file. Does not perform any key queries or repair puts. "
+ "Does bootstrap though so bootstrapUrl and storeName must be specified.");
parser.accepts("bad-key-file-out",
"Name of bad-key-file-out. "
+ "Keys that are not mae consistent are output to this file.")
.withRequiredArg()
.describedAs("badKeyFileOut")
.ofType(String.class);
parser.accepts("parallelism",
"Number of consistency fix messages outstanding in parallel. ")
.withRequiredArg()
.describedAs("parallelism [Default value: " + Options.defaultParallelism + "]")
.ofType(Integer.class);
parser.accepts("progress-period-ops",
"Number of operations between 'info' progress messages. ")
.withRequiredArg()
.describedAs("period (in operations) between outputting progress [Default value: "
+ Options.defaultProgressPeriodOps + "]")
.ofType(Long.class);
parser.accepts("per-server-qps-limit",
"Number of operations that the consistency fixer will issue to any individual server in one second. ")
.withRequiredArg()
.describedAs("perServerQPSLimit [Default value: " + Options.defaultPerServerQPSLimit
+ "]")
.ofType(Long.class);
OptionSet optionSet = parser.parse(args);
if(optionSet.hasArgument("help")) {
try {
parser.printHelpOn(System.out);
} catch(IOException e) {
e.printStackTrace();
}
printUsage();
System.exit(0);
}
if(!optionSet.hasArgument("url")) {
printUsage("Missing required 'url' argument.", parser);
}
if(!optionSet.hasArgument("store")) {
printUsage("Missing required 'store' argument.", parser);
}
if(!optionSet.has("bad-key-file-in")) {
printUsage("Missing required 'bad-key-file-in' argument.", parser);
}
if(!optionSet.has("bad-key-file-out")) {
printUsage("Missing required 'bad-key-file-out' argument.", parser);
}
Options options = new Options();
options.url = (String) optionSet.valueOf("url");
options.storeName = (String) optionSet.valueOf("store");
options.badKeyFileIn = (String) optionSet.valueOf("bad-key-file-in");
options.badKeyFileOut = (String) optionSet.valueOf("bad-key-file-out");
if(optionSet.has("orphan-format")) {
options.badKeyFileInOrphanFormat = true;
}
if(optionSet.has("parallelism")) {
options.parallelism = (Integer) optionSet.valueOf("parallelism");
}
if(optionSet.has("progress-period-ops")) {
options.progressPeriodOps = (Long) optionSet.valueOf("progress-period-ops");
}
if(optionSet.has("per-server-qps-limit")) {
options.perServerQPSLimit = (Long) optionSet.valueOf("per-server-qps-limit");
}
if(optionSet.has("dry-run")) {
options.dryRun = true;
}
if(optionSet.has("parse-only")) {
options.parseOnly = true;
}
return options;
}
public static void main(String[] args) throws Exception {
Options options = parseArgs(args);
ConsistencyFix consistencyFix = new ConsistencyFix(options.url,
options.storeName,
options.progressPeriodOps,
options.perServerQPSLimit,
options.dryRun,
options.parseOnly);
String summary = consistencyFix.execute(options.parallelism,
options.badKeyFileIn,
options.badKeyFileInOrphanFormat,
options.badKeyFileOut);
consistencyFix.close();
System.out.println(summary);
}
}