You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by xe...@apache.org on 2013/12/24 03:08:42 UTC
[3/6] Improve Stress Tool patch by Benedict;
reviewed by Pavel Yaskevich for CASSANDRA-6199
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/server/StressThread.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/server/StressThread.java b/tools/stress/src/org/apache/cassandra/stress/server/StressThread.java
deleted file mode 100644
index 158a09f..0000000
--- a/tools/stress/src/org/apache/cassandra/stress/server/StressThread.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 org.apache.cassandra.stress.server;
-
-import org.apache.cassandra.stress.Session;
-import org.apache.cassandra.stress.StressAction;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.PrintStream;
-import java.net.Socket;
-
-public class StressThread extends Thread
-{
- private final Socket socket;
-
- public StressThread(Socket client)
- {
- this.socket = client;
- }
-
- public void run()
- {
- try
- {
- ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
- PrintStream out = new PrintStream(socket.getOutputStream());
-
- StressAction action = new StressAction((Session) in.readObject(), out);
- action.start();
-
- while (action.isAlive())
- {
- try
- {
- if (in.readInt() == 1)
- {
- action.stopAction();
- break;
- }
- }
- catch (Exception e)
- {
- // continue without problem
- }
- }
-
- out.close();
- in.close();
- socket.close();
- }
- catch (IOException e)
- {
- throw new RuntimeException(e.getMessage(), e);
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/CliOption.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/CliOption.java b/tools/stress/src/org/apache/cassandra/stress/settings/CliOption.java
new file mode 100644
index 0000000..76c7509
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/CliOption.java
@@ -0,0 +1,58 @@
+package org.apache.cassandra.stress.settings;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum CliOption
+{
+ KEY("Key details such as size in bytes and value distribution", SettingsKey.helpPrinter()),
+ COL("Column details such as size and count distribution, data generator, names, comparator and if super columns should be used", SettingsColumn.helpPrinter()),
+ RATE("Thread count, rate limit or automatic mode (default is auto)", SettingsRate.helpPrinter()),
+ MODE("Thrift or CQL with options", SettingsMode.helpPrinter()),
+ SCHEMA("Replication settings, compression, compaction, etc.", SettingsSchema.helpPrinter()),
+ NODE("Nodes to connect to", SettingsNode.helpPrinter()),
+ LOG("Where to log progress to, and the interval at which to do it", SettingsLog.helpPrinter()),
+ TRANSPORT("Custom transport factories", SettingsTransport.helpPrinter()),
+ PORT("The port to connect to cassandra nodes on", SettingsPort.helpPrinter()),
+ SENDTO("-send-to", "Specify a stress server to send this command to", SettingsMisc.sendToDaemonHelpPrinter())
+ ;
+
+ private static final Map<String, CliOption> LOOKUP;
+ static
+ {
+ final Map<String, CliOption> lookup = new HashMap<>();
+ for (CliOption cmd : values())
+ {
+ lookup.put("-" + cmd.toString().toLowerCase(), cmd);
+ if (cmd.extraName != null)
+ lookup.put(cmd.extraName, cmd);
+ }
+ LOOKUP = lookup;
+ }
+
+ public static CliOption get(String command)
+ {
+ return LOOKUP.get(command.toLowerCase());
+ }
+
+ public final String extraName;
+ public final String description;
+ private final Runnable helpPrinter;
+
+ private CliOption(String description, Runnable helpPrinter)
+ {
+ this(null, description, helpPrinter);
+ }
+ private CliOption(String extraName, String description, Runnable helpPrinter)
+ {
+ this.extraName = extraName;
+ this.description = description;
+ this.helpPrinter = helpPrinter;
+ }
+
+ public void printHelp()
+ {
+ helpPrinter.run();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/Command.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/Command.java b/tools/stress/src/org/apache/cassandra/stress/settings/Command.java
new file mode 100644
index 0000000..4bd843e
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/Command.java
@@ -0,0 +1,101 @@
+package org.apache.cassandra.stress.settings;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum Command
+{
+
+ READ(false,
+ SettingsCommand.helpPrinter("read"),
+ "Multiple concurrent reads - the cluster must first be populated by a write test",
+ CommandCategory.BASIC
+ ),
+ WRITE(true,
+ SettingsCommand.helpPrinter("write"),
+ "insert",
+ "Multiple concurrent writes against the cluster",
+ CommandCategory.BASIC
+ ),
+ MIXED(true,
+ SettingsCommandMixed.helpPrinter(),
+ "Interleaving of any basic commands, with configurable ratio and distribution - the cluster must first be populated by a write test",
+ CommandCategory.MIXED
+ ),
+ RANGESLICE(false,
+ SettingsCommandMulti.helpPrinter("range_slice"),
+ "Range slice queries - the cluster must first be populated by a write test",
+ CommandCategory.MULTI
+ ),
+ IRANGESLICE(false,
+ SettingsCommandMulti.helpPrinter("indexed_range_slice"),
+ "Range slice queries through a secondary index. The cluster must first be populated by a write test, with indexing enabled.",
+ CommandCategory.MULTI
+ ),
+ READMULTI(false,
+ SettingsCommandMulti.helpPrinter("readmulti"),
+ "multi_read",
+ "Multiple concurrent reads fetching multiple rows at once. The cluster must first be populated by a write test.",
+ CommandCategory.MULTI
+ ),
+ COUNTERWRITE(true,
+ SettingsCommand.helpPrinter("counteradd"),
+ "counter_add",
+ "Multiple concurrent updates of counters.",
+ CommandCategory.BASIC
+ ),
+ COUNTERREAD(false,
+ SettingsCommand.helpPrinter("counterread"),
+ "counter_get",
+ "Multiple concurrent reads of counters. The cluster must first be populated by a counterwrite test.",
+ CommandCategory.BASIC
+ ),
+
+ HELP(false, SettingsMisc.helpHelpPrinter(), "-?", "Print help for a command or option", null),
+ PRINT(false, SettingsMisc.printHelpPrinter(), "Inspect the output of a distribution definition", null),
+ LEGACY(false, Legacy.helpPrinter(), "Legacy support mode", null)
+
+ ;
+
+ private static final Map<String, Command> LOOKUP;
+ static
+ {
+ final Map<String, Command> lookup = new HashMap<>();
+ for (Command cmd : values())
+ {
+ lookup.put(cmd.toString().toLowerCase(), cmd);
+ if (cmd.extraName != null)
+ lookup.put(cmd.extraName, cmd);
+ }
+ LOOKUP = lookup;
+ }
+
+ public static Command get(String command)
+ {
+ return LOOKUP.get(command.toLowerCase());
+ }
+
+ public final boolean updates;
+ public final CommandCategory category;
+ public final String extraName;
+ public final String description;
+ public final Runnable helpPrinter;
+
+ Command(boolean updates, Runnable helpPrinter, String description, CommandCategory category)
+ {
+ this(updates, helpPrinter, null, description, category);
+ }
+ Command(boolean updates, Runnable helpPrinter, String extra, String description, CommandCategory category)
+ {
+ this.updates = updates;
+ this.category = category;
+ this.helpPrinter = helpPrinter;
+ this.extraName = extra;
+ this.description = description;
+ }
+ public void printHelp()
+ {
+ helpPrinter.run();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/CommandCategory.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/CommandCategory.java b/tools/stress/src/org/apache/cassandra/stress/settings/CommandCategory.java
new file mode 100644
index 0000000..87a13f7
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/CommandCategory.java
@@ -0,0 +1,8 @@
+package org.apache.cassandra.stress.settings;
+
+public enum CommandCategory
+{
+ BASIC,
+ MULTI,
+ MIXED
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/ConnectionAPI.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/ConnectionAPI.java b/tools/stress/src/org/apache/cassandra/stress/settings/ConnectionAPI.java
new file mode 100644
index 0000000..c647f66
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/ConnectionAPI.java
@@ -0,0 +1,7 @@
+package org.apache.cassandra.stress.settings;
+
+public enum ConnectionAPI
+{
+ THRIFT, THRIFT_SMART, SIMPLE_NATIVE, JAVA_DRIVER_NATIVE
+}
+
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/ConnectionStyle.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/ConnectionStyle.java b/tools/stress/src/org/apache/cassandra/stress/settings/ConnectionStyle.java
new file mode 100644
index 0000000..6e77f4a
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/ConnectionStyle.java
@@ -0,0 +1,9 @@
+package org.apache.cassandra.stress.settings;
+
+public enum ConnectionStyle
+{
+ CQL,
+ CQL_PREPARED,
+ THRIFT
+}
+
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/CqlVersion.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/CqlVersion.java b/tools/stress/src/org/apache/cassandra/stress/settings/CqlVersion.java
new file mode 100644
index 0000000..853e399
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/CqlVersion.java
@@ -0,0 +1,48 @@
+package org.apache.cassandra.stress.settings;
+
+public enum CqlVersion
+{
+
+ NOCQL(null),
+ CQL2("2.0.0"),
+ CQL3("3.0.0");
+
+ public final String connectVersion;
+
+ private CqlVersion(String connectVersion)
+ {
+ this.connectVersion = connectVersion;
+ }
+
+ static CqlVersion get(String version)
+ {
+ if (version == null)
+ return NOCQL;
+ switch(version.charAt(0))
+ {
+ case '2':
+ return CQL2;
+ case '3':
+ return CQL3;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ public boolean isCql()
+ {
+ return this != NOCQL;
+ }
+
+ public boolean isCql2()
+ {
+ return this == CQL2;
+ }
+
+ public boolean isCql3()
+ {
+ return this == CQL3;
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/GroupedOptions.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/GroupedOptions.java b/tools/stress/src/org/apache/cassandra/stress/settings/GroupedOptions.java
new file mode 100644
index 0000000..fe965c9
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/GroupedOptions.java
@@ -0,0 +1,104 @@
+package org.apache.cassandra.stress.settings;
+
+import java.io.PrintStream;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public abstract class GroupedOptions
+{
+
+ int accepted = 0;
+
+ public boolean accept(String param)
+ {
+ for (Option option : options())
+ {
+ if (option.accept(param))
+ {
+ accepted++;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean happy()
+ {
+ for (Option option : options())
+ if (!option.happy())
+ return false;
+ return true;
+ }
+
+ public abstract List<? extends Option> options();
+
+ // hands the parameters to each of the option groups, and returns the first provided
+ // option group that is happy() after this is done, that also accepted all the parameters
+ public static <G extends GroupedOptions> G select(String[] params, G... groupings)
+ {
+ for (String param : params)
+ {
+ boolean accepted = false;
+ for (GroupedOptions grouping : groupings)
+ accepted |= grouping.accept(param);
+ if (!accepted)
+ throw new IllegalArgumentException("Invalid parameter " + param);
+ }
+ for (G grouping : groupings)
+ if (grouping.happy() && grouping.accepted == params.length)
+ return grouping;
+ return null;
+ }
+
+ // pretty prints all of the option groupings
+ public static void printOptions(PrintStream out, String command, GroupedOptions... groupings)
+ {
+ out.println();
+ boolean firstRow = true;
+ for (GroupedOptions grouping : groupings)
+ {
+ if (!firstRow)
+ {
+ out.println(" OR ");
+ }
+ firstRow = false;
+
+ StringBuilder sb = new StringBuilder("Usage: " + command);
+ for (Option option : grouping.options())
+ {
+ sb.append(" ");
+ sb.append(option.shortDisplay());
+ }
+ out.println(sb.toString());
+ }
+ out.println();
+ final Set<Option> printed = new HashSet<>();
+ for (GroupedOptions grouping : groupings)
+ {
+ for (Option option : grouping.options())
+ {
+ if (printed.add(option))
+ {
+ if (option.longDisplay() != null)
+ {
+ out.println(" " + option.longDisplay());
+ for (String row : option.multiLineDisplay())
+ out.println(" " + row);
+ }
+ }
+ }
+ }
+ }
+
+ public static String formatLong(String longDisplay, String description)
+ {
+ return String.format("%-40s %s", longDisplay, description);
+ }
+
+ public static String formatMultiLine(String longDisplay, String description)
+ {
+ return String.format("%-36s %s", longDisplay, description);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/Legacy.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/Legacy.java b/tools/stress/src/org/apache/cassandra/stress/settings/Legacy.java
new file mode 100644
index 0000000..6242425
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/Legacy.java
@@ -0,0 +1,369 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cassandra.stress.settings;
+
+import java.io.Serializable;
+import java.util.*;
+
+import org.apache.commons.cli.*;
+import org.apache.commons.cli.Option;
+
+public class Legacy implements Serializable
+{
+
+ // command line options
+ public static final Options availableOptions = new Options();
+
+ private static final String SSL_TRUSTSTORE = "truststore";
+ private static final String SSL_TRUSTSTORE_PW = "truststore-password";
+ private static final String SSL_PROTOCOL = "ssl-protocol";
+ private static final String SSL_ALGORITHM = "ssl-alg";
+ private static final String SSL_STORE_TYPE = "store-type";
+ private static final String SSL_CIPHER_SUITES = "ssl-ciphers";
+
+ static
+ {
+ availableOptions.addOption("h", "help", false, "Show this help message and exit");
+ availableOptions.addOption("n", "num-keys", true, "Number of keys, default:1000000");
+ availableOptions.addOption("F", "num-different-keys", true, "Number of different keys (if < NUM-KEYS, the same key will re-used multiple times), default:NUM-KEYS");
+ availableOptions.addOption("t", "threadCount", true, "Number of threadCount to use, default:50");
+ availableOptions.addOption("c", "columns", true, "Number of columns per key, default:5");
+ availableOptions.addOption("S", "column-size", true, "Size of column values in bytes, default:34");
+ availableOptions.addOption("C", "unique columns", true, "Max number of unique columns per key, default:50");
+ availableOptions.addOption("RC", "unique rows", true, "Max number of unique rows, default:50");
+ availableOptions.addOption("d", "nodes", true, "Host nodes (comma separated), default:locahost");
+ availableOptions.addOption("D", "nodesfile", true, "File containing host nodes (one per line)");
+ availableOptions.addOption("s", "stdev", true, "Standard Deviation for gaussian read key generation, default:0.1");
+ availableOptions.addOption("r", "random", false, "Use random key generator for read key generation (STDEV will have no effect), default:false");
+ availableOptions.addOption("f", "file", true, "Write output to given file");
+ availableOptions.addOption("p", "port", true, "Thrift port, default:9160");
+ availableOptions.addOption("o", "operation", true, "Operation to perform (WRITE, READ, READWRITE, RANGE_SLICE, INDEXED_RANGE_SLICE, MULTI_GET, COUNTERWRITE, COUNTER_GET), default:WRITE");
+ availableOptions.addOption("u", "supercolumns", true, "Number of super columns per key, default:1");
+ availableOptions.addOption("y", "family-type", true, "Column Family Type (Super, Standard), default:Standard");
+ availableOptions.addOption("K", "keep-trying", true, "Retry on-going operation N times (in case of failure). positive integer, default:10");
+ availableOptions.addOption("k", "keep-going", false, "Ignore errors inserting or reading (when set, --keep-trying has no effect), default:false");
+ availableOptions.addOption("i", "progress-interval", true, "Progress Report Interval (seconds), default:10");
+ availableOptions.addOption("g", "keys-per-call", true, "Number of keys to get_range_slices or multiget per call, default:1000");
+ availableOptions.addOption("l", "replication-factor", true, "Replication Factor to use when creating needed column families, default:1");
+ availableOptions.addOption("L", "enable-cql", false, "Perform queries using CQL2 (Cassandra Query Language v 2.0.0)");
+ availableOptions.addOption("L3", "enable-cql3", false, "Perform queries using CQL3 (Cassandra Query Language v 3.0.0)");
+ availableOptions.addOption("b", "enable-native-protocol", false, "Use the binary native protocol (only work along with -L3)");
+ availableOptions.addOption("P", "use-prepared-statements", false, "Perform queries using prepared statements (only applicable to CQL).");
+ availableOptions.addOption("e", "consistency-level", true, "Consistency Level to use (ONE, QUORUM, LOCAL_QUORUM, EACH_QUORUM, ALL, ANY), default:ONE");
+ availableOptions.addOption("x", "create-index", true, "Type of index to create on needed column families (KEYS)");
+ availableOptions.addOption("R", "replication-strategy", true, "Replication strategy to use (only on insert if keyspace does not exist), default:org.apache.cassandra.locator.SimpleStrategy");
+ availableOptions.addOption("O", "strategy-properties", true, "Replication strategy properties in the following format <dc_name>:<num>,<dc_name>:<num>,...");
+ availableOptions.addOption("W", "no-replicate-on-write",false, "Set replicate_on_write to false for counters. Only counter add with CL=ONE will work");
+ availableOptions.addOption("V", "average-size-values", false, "Generate column values of average rather than specific size");
+ availableOptions.addOption("T", "send-to", true, "Send this as a request to the stress daemon at specified address.");
+ availableOptions.addOption("I", "compression", true, "Specify the compression to use for sstable, default:no compression");
+ availableOptions.addOption("Q", "query-names", true, "Comma-separated list of column names to retrieve from each row.");
+ availableOptions.addOption("Z", "compaction-strategy", true, "CompactionStrategy to use.");
+ availableOptions.addOption("U", "comparator", true, "Column Comparator to use. Currently supported types are: TimeUUIDType, AsciiType, UTF8Type.");
+ availableOptions.addOption("tf", "transport-factory", true, "Fully-qualified TTransportFactory class name for creating a connection. Note: For Thrift over SSL, use org.apache.cassandra.stress.SSLTransportFactory.");
+ availableOptions.addOption("ns", "no-statistics", false, "Turn off the aggegate statistics that is normally output after completion.");
+ availableOptions.addOption("ts", SSL_TRUSTSTORE, true, "SSL: full path to truststore");
+ availableOptions.addOption("tspw", SSL_TRUSTSTORE_PW, true, "SSL: full path to truststore");
+ availableOptions.addOption("prtcl", SSL_PROTOCOL, true, "SSL: connections protocol to use (default: TLS)");
+ availableOptions.addOption("alg", SSL_ALGORITHM, true, "SSL: algorithm (default: SunX509)");
+ availableOptions.addOption("st", SSL_STORE_TYPE, true, "SSL: type of store");
+ availableOptions.addOption("ciphers", SSL_CIPHER_SUITES, true, "SSL: comma-separated list of encryption suites to use");
+ availableOptions.addOption("th", "throttle", true, "Throttle the total number of operations per second to a maximum amount.");
+ }
+
+ public static StressSettings build(String[] arguments)
+ {
+ CommandLineParser parser = new PosixParser();
+
+ final Converter r = new Converter();
+ try
+ {
+ CommandLine cmd = parser.parse(availableOptions, arguments);
+
+ if (cmd.getArgs().length > 0)
+ {
+ System.err.println("Application does not allow arbitrary arguments: " + Arrays.asList(cmd.getArgList()));
+ System.exit(1);
+ }
+
+ if (cmd.hasOption("h"))
+ printHelpMessage();
+
+ if (cmd.hasOption("C"))
+ System.out.println("Ignoring deprecated option -C");
+
+ if (cmd.hasOption("o"))
+ r.setCommand(cmd.getOptionValue("o").toLowerCase());
+ else
+ r.setCommand("insert");
+
+ if (cmd.hasOption("K"))
+ r.add("command", "tries=" + cmd.getOptionValue("K"));
+
+ if (cmd.hasOption("k"))
+ {
+ if (!cmd.hasOption("K"))
+ r.add("command", "retry=1");
+ r.add("command", "ignore_errors");
+ }
+
+ if (cmd.hasOption("g"))
+ r.add("command", "at-once=" + cmd.getOptionValue("g"));
+
+ if (cmd.hasOption("e"))
+ r.add("command", "cl=" + cmd.getOptionValue("e"));
+
+ String numKeys;
+ if (cmd.hasOption("n"))
+ numKeys = cmd.getOptionValue("n");
+ else
+ numKeys = "1000000";
+ r.add("command", "n=" + numKeys);
+
+ String uniqueKeys;
+ if (cmd.hasOption("F"))
+ uniqueKeys = cmd.getOptionValue("F");
+ else
+ uniqueKeys = numKeys;
+
+ if (r.opts.containsKey("write") || r.opts.containsKey("counterwrite"))
+ {
+ if (!uniqueKeys.equals(numKeys))
+ r.add("-key", "populate=1.." + uniqueKeys);
+ }
+ else if (cmd.hasOption("r"))
+ {
+ r.add("-key", "dist=uniform(1.." + uniqueKeys + ")");
+ }
+ else
+ {
+ if (!cmd.hasOption("s"))
+ r.add("-key", "dist=gauss(1.." + uniqueKeys + ",5)");
+ else
+ r.add("-key", String.format("dist=gauss(1..%s,%.2f)", uniqueKeys,
+ 0.5 / Float.parseFloat(cmd.getOptionValue("s"))));
+ }
+
+ String colCount;
+ if (cmd.hasOption("c"))
+ colCount = cmd.getOptionValue("c");
+ else
+ colCount = "5";
+
+ String colSize;
+ if (cmd.hasOption("S"))
+ colSize = cmd.getOptionValue("S");
+ else
+ colSize = "34";
+
+ r.add("-col", "n=fixed(" + colCount + ")");
+ if (cmd.hasOption("V"))
+ {
+ r.add("-col", "size=uniform(1.." + Integer.parseInt(colSize) * 2 + ")");
+ r.add("-col", "data=rand()");
+ }
+ else
+ {
+ r.add("-col", "size=fixed(" + colSize + ")");
+ r.add("-col", "data=repeat(1)");
+ }
+ if (cmd.hasOption("Q"))
+ r.add("-col", "names=" + cmd.getOptionValue("Q"));
+
+ if (cmd.hasOption("U"))
+ r.add("-col", "comparator=" + cmd.getOptionValue("U"));
+
+ if (cmd.hasOption("y") && cmd.getOptionValue("y").equals("Super"))
+ r.add("-col", "super=" + (cmd.hasOption("u") ? cmd.getOptionValue("u") : "1"));
+
+ if (cmd.hasOption("t"))
+ r.add("-rate", "threads=" + cmd.getOptionValue("t"));
+ else
+ r.add("-rate", "threads=50");
+
+ if (cmd.hasOption("th"))
+ r.add("-rate", "limit=" + cmd.getOptionValue("th") + "/s");
+
+ if (cmd.hasOption("f"))
+ r.add("-log", "file=" + cmd.getOptionValue("f"));
+
+ if (cmd.hasOption("p"))
+ r.add("-port", cmd.getOptionValue("p"));
+
+ if (cmd.hasOption("i"))
+ r.add("-log", "interval=" + cmd.getOptionValue("i"));
+ else
+ r.add("-log", "interval=10");
+
+ if (cmd.hasOption("x"))
+ r.add("-schema", "index=" + cmd.getOptionValue("x"));
+
+ if (cmd.hasOption("R") || cmd.hasOption("l") || cmd.hasOption("O"))
+ {
+ StringBuilder rep = new StringBuilder();
+ if (cmd.hasOption("R"))
+ rep.append("strategy=" + cmd.getOptionValue("R"));
+ if (cmd.hasOption("l"))
+ {
+ if (rep.length() > 0)
+ rep.append(",");
+ rep.append("factor=" + cmd.getOptionValue("l"));
+ }
+ if (cmd.hasOption("O"))
+ {
+ if (rep.length() > 0)
+ rep.append(",");
+ rep.append(cmd.getOptionValue("O").replace(':','='));
+ }
+ r.add("-schema", "replication(" + rep + ")");
+ }
+
+ if (cmd.hasOption("L"))
+ r.add("-mode", cmd.hasOption("P") ? "prepared cql2" : "cql2");
+ else if (cmd.hasOption("L3"))
+ r.add("-mode", (cmd.hasOption("P") ? "prepared" : "") + (cmd.hasOption("b") ? "native" : "") + "cql3");
+ else
+ r.add("-mode", "thrift");
+
+ if (cmd.hasOption("W"))
+ r.add("-schema", "no-replicate-on-write");
+
+ if (cmd.hasOption("I"))
+ r.add("-schema", "compression=" + cmd.getOptionValue("I"));
+
+ if (cmd.hasOption("d"))
+ r.add("-node", cmd.getOptionValue("d"));
+
+ if (cmd.hasOption("D"))
+ r.add("-node", "file=" + cmd.getOptionValue("D"));
+
+
+ if (cmd.hasOption("send-to"))
+ r.add("-send-to", cmd.getOptionValue("send-to"));
+
+ if (cmd.hasOption("Z"))
+ r.add("-schema", "compaction=" + cmd.getOptionValue("Z"));
+
+ if (cmd.hasOption("ns"))
+ r.add("-log", "no-summary");
+
+ if (cmd.hasOption("tf"))
+ r.add("-transport", "factory=" + cmd.getOptionValue("tf"));
+
+ // THESE DON'T SEEM TO AFFECT PROGRAM BEHAVIOUR
+// if(cmd.hasOption(SSL_TRUSTSTORE))
+// encOptions.truststore = cmd.getOptionValue(SSL_TRUSTSTORE);
+//
+// if(cmd.hasOption(SSL_TRUSTSTORE_PW))
+// encOptions.truststore_password = cmd.getOptionValue(SSL_TRUSTSTORE_PW);
+//
+// if(cmd.hasOption(SSL_PROTOCOL))
+// encOptions.protocol = cmd.getOptionValue(SSL_PROTOCOL);
+//
+// if(cmd.hasOption(SSL_ALGORITHM))
+// encOptions.algorithm = cmd.getOptionValue(SSL_ALGORITHM);
+//
+// if(cmd.hasOption(SSL_STORE_TYPE))
+// encOptions.store_type = cmd.getOptionValue(SSL_STORE_TYPE);
+//
+// if(cmd.hasOption(SSL_CIPHER_SUITES))
+// encOptions.cipher_suites = cmd.getOptionValue(SSL_CIPHER_SUITES).split(",");
+
+ }
+ catch (ParseException e)
+ {
+ printHelpMessage();
+ System.exit(1);
+ }
+
+ r.printNewCommand();
+ return r.get();
+ }
+
+ private static final class Converter
+ {
+ private Map<String, List<String>> opts = new LinkedHashMap<>();
+ List<String> command;
+ public void add(String option, String suboption)
+ {
+ if (option.equals("command"))
+ {
+ command.add(suboption);
+ return;
+ }
+ List<String> params = opts.get(option);
+ if (params == null)
+ opts.put(option, params = new ArrayList());
+ params.add(suboption);
+ }
+ StressSettings get(){
+ Map<String, String[]> clArgs = new HashMap<>();
+ for (Map.Entry<String, List<String>> e : opts.entrySet())
+ clArgs .put(e.getKey(), e.getValue().toArray(new String[0]));
+ return StressSettings.get(clArgs);
+ }
+ void setCommand(String command)
+ {
+ command = Command.get(command).toString().toLowerCase();
+ opts.put(command, this.command = new ArrayList<>());
+ }
+ void printNewCommand()
+ {
+ StringBuilder sb = new StringBuilder("stress");
+ for (Map.Entry<String, List<String>> e : opts.entrySet())
+ {
+ sb.append(" ");
+ sb.append(e.getKey());
+ for (String opt : e.getValue())
+ {
+ sb.append(" ");
+ sb.append(opt);
+ }
+ }
+ System.out.println("Running in legacy support mode. Translating command to: ");
+ System.out.println(sb.toString());
+ }
+ }
+
+ public static void printHelpMessage()
+ {
+ System.out.println("Usage: ./bin/cassandra-stress legacy [options]\n\nOptions:");
+ System.out.println("THIS IS A LEGACY SUPPORT MODE");
+
+ for(Object o : availableOptions.getOptions())
+ {
+ Option option = (Option) o;
+ String upperCaseName = option.getLongOpt().toUpperCase();
+ System.out.println(String.format("-%s%s, --%s%s%n\t\t%s%n", option.getOpt(), (option.hasArg()) ? " "+upperCaseName : "",
+ option.getLongOpt(), (option.hasArg()) ? "="+upperCaseName : "", option.getDescription()));
+ }
+ }
+
+ public static Runnable helpPrinter()
+ {
+ return new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ printHelpMessage();
+ }
+ };
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/Option.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/Option.java b/tools/stress/src/org/apache/cassandra/stress/settings/Option.java
new file mode 100644
index 0000000..dfd0857
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/Option.java
@@ -0,0 +1,24 @@
+package org.apache.cassandra.stress.settings;
+
+import java.util.List;
+
+abstract class Option
+{
+
+ abstract boolean accept(String param);
+ abstract boolean happy();
+ abstract String shortDisplay();
+ abstract String longDisplay();
+ abstract List<String> multiLineDisplay();
+
+ public int hashCode()
+ {
+ return getClass().hashCode();
+ }
+
+ public boolean equals(Object that)
+ {
+ return this.getClass() == that.getClass();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/OptionDataGen.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/OptionDataGen.java b/tools/stress/src/org/apache/cassandra/stress/settings/OptionDataGen.java
new file mode 100644
index 0000000..7f4e5ea
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/OptionDataGen.java
@@ -0,0 +1,177 @@
+package org.apache.cassandra.stress.settings;
+
+import java.io.File;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.cassandra.stress.generatedata.*;
+
+/**
+ * For selecting a data generator
+ */
+class OptionDataGen extends Option
+{
+
+ private static final Pattern FULL = Pattern.compile("([A-Z]+)\\(([^)]+)\\)", Pattern.CASE_INSENSITIVE);
+ private static final Pattern ARGS = Pattern.compile("[^,]+");
+
+ final String prefix;
+ private DataGenFactory factory;
+ private final DataGenFactory defaultFactory;
+
+ public OptionDataGen(String prefix, String defaultSpec)
+ {
+ this.prefix = prefix;
+ this.defaultFactory = defaultSpec == null ? null : get(defaultSpec);
+ }
+
+ @Override
+ public boolean accept(String param)
+ {
+ if (!param.toLowerCase().startsWith(prefix))
+ return false;
+ factory = get(param.substring(prefix.length()));
+ return true;
+ }
+
+ private static DataGenFactory get(String spec)
+ {
+ Matcher m = FULL.matcher(spec);
+ if (!m.matches())
+ throw new IllegalArgumentException("Illegal data generator specification: " + spec);
+ String name = m.group(1);
+ Impl impl = LOOKUP.get(name.toLowerCase());
+ if (impl == null)
+ throw new IllegalArgumentException("Illegal data generator type: " + name);
+ List<String> params = new ArrayList<>();
+ m = ARGS.matcher(m.group(2));
+ while (m.find())
+ params.add(m.group());
+ return impl.getFactory(params);
+ }
+
+ public DataGenFactory get()
+ {
+ return factory != null ? factory : defaultFactory;
+ }
+
+ @Override
+ public boolean happy()
+ {
+ return factory != null || defaultFactory != null;
+ }
+
+ @Override
+ public String shortDisplay()
+ {
+ return prefix + "ALG()";
+ }
+
+ public String longDisplay()
+ {
+ return shortDisplay() + ": Specify a data generator from:";
+ }
+
+ @Override
+ public List<String> multiLineDisplay()
+ {
+ return Arrays.asList(
+ GroupedOptions.formatMultiLine("RANDOM()", "Completely random byte generation"),
+ GroupedOptions.formatMultiLine("REPEAT(<freq>)", "An MD5 hash of (opIndex % freq) combined with the column index"),
+ GroupedOptions.formatMultiLine("DICT(<file>)","Random words from a dictionary; the file should be in the format \"<freq> <word>\"")
+ );
+ }
+
+ private static final Map<String, Impl> LOOKUP;
+ static
+ {
+ final Map<String, Impl> lookup = new HashMap<>();
+ lookup.put("random", new RandomImpl());
+ lookup.put("rand", new RandomImpl());
+ lookup.put("rnd", new RandomImpl());
+ lookup.put("repeat", new RepeatImpl());
+ lookup.put("dict", new DictionaryImpl());
+ lookup.put("dictionary", new DictionaryImpl());
+ LOOKUP = lookup;
+ }
+
+ private static interface Impl
+ {
+ public DataGenFactory getFactory(List<String> params);
+ }
+
+ private static final class RandomImpl implements Impl
+ {
+ @Override
+ public DataGenFactory getFactory(List<String> params)
+ {
+ if (params.size() != 0)
+ throw new IllegalArgumentException("Invalid parameter list for random generator: " + params);
+ return new RandomFactory();
+ }
+ }
+
+ private static final class RepeatImpl implements Impl
+ {
+
+ @Override
+ public DataGenFactory getFactory(List<String> params)
+ {
+ if (params.size() != 1)
+ throw new IllegalArgumentException("Invalid parameter list for repeating generator: " + params);
+ try
+ {
+ int repeatFrequency = Integer.parseInt(params.get(0));
+ return new RepeatsFactory(repeatFrequency);
+ } catch (Exception _)
+ {
+ throw new IllegalArgumentException("Invalid parameter list for repeating generator: " + params);
+ }
+ }
+ }
+
+ private static final class DictionaryImpl implements Impl
+ {
+
+ @Override
+ public DataGenFactory getFactory(List<String> params)
+ {
+ if (params.size() != 1)
+ throw new IllegalArgumentException("Invalid parameter list for dictionary generator: " + params);
+ try
+ {
+ final File file = new File(params.get(0));
+ return DataGenStringDictionary.getFactory(file);
+ } catch (Exception e)
+ {
+ throw new IllegalArgumentException("Invalid parameter list for dictionary generator: " + params, e);
+ }
+ }
+ }
+
+ private static final class RandomFactory implements DataGenFactory
+ {
+ @Override
+ public DataGen get()
+ {
+ return new DataGenBytesRandom();
+ }
+ }
+
+ private static final class RepeatsFactory implements DataGenFactory
+ {
+ final int frequency;
+ private RepeatsFactory(int frequency)
+ {
+ this.frequency = frequency;
+ }
+
+ @Override
+ public DataGen get()
+ {
+ return new DataGenStringRepeats(frequency);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/OptionDistribution.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/OptionDistribution.java b/tools/stress/src/org/apache/cassandra/stress/settings/OptionDistribution.java
new file mode 100644
index 0000000..749c797
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/OptionDistribution.java
@@ -0,0 +1,340 @@
+package org.apache.cassandra.stress.settings;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.cassandra.stress.generatedata.*;
+import org.apache.commons.math3.distribution.ExponentialDistribution;
+import org.apache.commons.math3.distribution.NormalDistribution;
+import org.apache.commons.math3.distribution.UniformRealDistribution;
+import org.apache.commons.math3.distribution.WeibullDistribution;
+
+/**
+ * For selecting a mathematical distribution
+ */
+class OptionDistribution extends Option
+{
+
+ private static final Pattern FULL = Pattern.compile("([A-Z]+)\\((.+)\\)", Pattern.CASE_INSENSITIVE);
+ private static final Pattern ARGS = Pattern.compile("[^,]+");
+
+ final String prefix;
+ private String spec;
+ private final String defaultSpec;
+
+ public OptionDistribution(String prefix, String defaultSpec)
+ {
+ this.prefix = prefix;
+ this.defaultSpec = defaultSpec;
+ }
+
+ @Override
+ public boolean accept(String param)
+ {
+ if (!param.toLowerCase().startsWith(prefix))
+ return false;
+ spec = param.substring(prefix.length());
+ return true;
+ }
+
+ private static DistributionFactory get(String spec)
+ {
+ Matcher m = FULL.matcher(spec);
+ if (!m.matches())
+ throw new IllegalArgumentException("Illegal distribution specification: " + spec);
+ String name = m.group(1);
+ Impl impl = LOOKUP.get(name.toLowerCase());
+ if (impl == null)
+ throw new IllegalArgumentException("Illegal distribution type: " + name);
+ List<String> params = new ArrayList<>();
+ m = ARGS.matcher(m.group(2));
+ while (m.find())
+ params.add(m.group());
+ return impl.getFactory(params);
+ }
+
+ public DistributionFactory get()
+ {
+ return spec != null ? get(spec) : get(defaultSpec);
+ }
+
+ @Override
+ public boolean happy()
+ {
+ return spec != null || defaultSpec != null;
+ }
+
+ public String longDisplay()
+ {
+ return shortDisplay() + ": Specify a mathematical distribution";
+ }
+
+ @Override
+ public List<String> multiLineDisplay()
+ {
+ return Arrays.asList(
+ GroupedOptions.formatMultiLine("EXP(min..max)", "An exponential distribution over the range [min..max]"),
+ GroupedOptions.formatMultiLine("EXTREME(min..max,shape)", "An extreme value (Weibull) distribution over the range [min..max]"),
+ GroupedOptions.formatMultiLine("GAUSSIAN(min..max,stdvrng)", "A gaussian/normal distribution, where mean=(min+max)/2, and stdev is (mean-min)/stdvrng"),
+ GroupedOptions.formatMultiLine("GAUSSIAN(min..max,mean,stdev)", "A gaussian/normal distribution, with explicitly defined mean and stdev"),
+ GroupedOptions.formatMultiLine("UNIFORM(min..max)", "A uniform distribution over the range [min, max]"),
+ GroupedOptions.formatMultiLine("FIXED(val)", "A fixed distribution, always returning the same value"),
+ "Aliases: extr, gauss, normal, norm, weibull"
+ );
+ }
+
+ @Override
+ public String shortDisplay()
+ {
+ return prefix + "DIST(?)";
+ }
+
+ private static final Map<String, Impl> LOOKUP;
+ static
+ {
+ final Map<String, Impl> lookup = new HashMap<>();
+ lookup.put("exp", new ExponentialImpl());
+ lookup.put("extr", new ExtremeImpl());
+ lookup.put("extreme", lookup.get("extreme"));
+ lookup.put("weibull", lookup.get("weibull"));
+ lookup.put("gaussian", new GaussianImpl());
+ lookup.put("normal", lookup.get("gaussian"));
+ lookup.put("gauss", lookup.get("gaussian"));
+ lookup.put("norm", lookup.get("gaussian"));
+ lookup.put("uniform", new UniformImpl());
+ lookup.put("fixed", new FixedImpl());
+ LOOKUP = lookup;
+ }
+
+ // factory builders
+
+ private static interface Impl
+ {
+ public DistributionFactory getFactory(List<String> params);
+ }
+
+ private static final class GaussianImpl implements Impl
+ {
+
+ @Override
+ public DistributionFactory getFactory(List<String> params)
+ {
+ if (params.size() > 3 || params.size() < 1)
+ throw new IllegalArgumentException("Invalid parameter list for gaussian distribution: " + params);
+ try
+ {
+ String[] bounds = params.get(0).split("\\.\\.+");
+ final long min = Long.parseLong(bounds[0]);
+ final long max = Long.parseLong(bounds[1]);
+ final double mean, stdev;
+ if (params.size() == 3)
+ {
+ mean = Double.parseDouble(params.get(1));
+ stdev = Double.parseDouble(params.get(2));
+ }
+ else
+ {
+ final double stdevsToEdge = params.size() == 1 ? 3d : Double.parseDouble(params.get(1));
+ mean = (min + max) / 2d;
+ stdev = ((max - min) / 2d) / stdevsToEdge;
+ }
+ return new GaussianFactory(min, max, mean, stdev);
+ } catch (Exception _)
+ {
+ throw new IllegalArgumentException("Invalid parameter list for uniform distribution: " + params);
+ }
+ }
+ }
+
+ private static final class ExponentialImpl implements Impl
+ {
+ @Override
+ public DistributionFactory getFactory(List<String> params)
+ {
+ if (params.size() != 1)
+ throw new IllegalArgumentException("Invalid parameter list for gaussian distribution: " + params);
+ try
+ {
+ String[] bounds = params.get(0).split("\\.\\.+");
+ final long min = Long.parseLong(bounds[0]);
+ final long max = Long.parseLong(bounds[1]);
+ ExponentialDistribution findBounds = new ExponentialDistribution(1d);
+ // max probability should be roughly equal to accuracy of (max-min) to ensure all values are visitable,
+ // over entire range, but this results in overly skewed distribution, so take sqrt
+ final double mean = (max - min) / findBounds.inverseCumulativeProbability(1d - Math.sqrt(1d/(max-min)));
+ return new ExpFactory(min, max, mean);
+ } catch (Exception _)
+ {
+ throw new IllegalArgumentException("Invalid parameter list for uniform distribution: " + params);
+ }
+ }
+ }
+
+ private static final class ExtremeImpl implements Impl
+ {
+ @Override
+ public DistributionFactory getFactory(List<String> params)
+ {
+ if (params.size() != 2)
+ throw new IllegalArgumentException("Invalid parameter list for extreme (Weibull) distribution: " + params);
+ try
+ {
+ String[] bounds = params.get(0).split("\\.\\.+");
+ final long min = Long.parseLong(bounds[0]);
+ final long max = Long.parseLong(bounds[1]);
+ final double shape = Double.parseDouble(params.get(1));
+ WeibullDistribution findBounds = new WeibullDistribution(shape, 1d);
+ // max probability should be roughly equal to accuracy of (max-min) to ensure all values are visitable,
+ // over entire range, but this results in overly skewed distribution, so take sqrt
+ final double scale = (max - min) / findBounds.inverseCumulativeProbability(1d - Math.sqrt(1d/(max-min)));
+ return new ExtremeFactory(min, max, shape, scale);
+ } catch (Exception _)
+ {
+ throw new IllegalArgumentException("Invalid parameter list for extreme (Weibull) distribution: " + params);
+ }
+ }
+ }
+
+ private static final class UniformImpl implements Impl
+ {
+
+ @Override
+ public DistributionFactory getFactory(List<String> params)
+ {
+ if (params.size() != 1)
+ throw new IllegalArgumentException("Invalid parameter list for uniform distribution: " + params);
+ try
+ {
+ String[] bounds = params.get(0).split("\\.\\.+");
+ final long min = Long.parseLong(bounds[0]);
+ final long max = Long.parseLong(bounds[1]);
+ return new UniformFactory(min, max);
+ } catch (Exception _)
+ {
+ throw new IllegalArgumentException("Invalid parameter list for uniform distribution: " + params);
+ }
+ }
+ }
+
+ private static final class FixedImpl implements Impl
+ {
+
+ @Override
+ public DistributionFactory getFactory(List<String> params)
+ {
+ if (params.size() != 1)
+ throw new IllegalArgumentException("Invalid parameter list for uniform distribution: " + params);
+ try
+ {
+ final long key = Long.parseLong(params.get(0));
+ return new FixedFactory(key);
+ } catch (Exception _)
+ {
+ throw new IllegalArgumentException("Invalid parameter list for uniform distribution: " + params);
+ }
+ }
+ }
+
+ // factories
+
+ private static final class ExpFactory implements DistributionFactory
+ {
+ final long min, max;
+ final double mean;
+ private ExpFactory(long min, long max, double mean)
+ {
+ this.min = min;
+ this.max = max;
+ this.mean = mean;
+ }
+
+ @Override
+ public Distribution get()
+ {
+ return new DistributionOffsetApache(new ExponentialDistribution(mean), min, max);
+ }
+ }
+
+ private static final class ExtremeFactory implements DistributionFactory
+ {
+ final long min, max;
+ final double shape, scale;
+ private ExtremeFactory(long min, long max, double shape, double scale)
+ {
+ this.min = min;
+ this.max = max;
+ this.shape = shape;
+ this.scale = scale;
+ }
+
+ @Override
+ public Distribution get()
+ {
+ return new DistributionOffsetApache(new WeibullDistribution(shape, scale), min, max);
+ }
+ }
+
+ private static final class GaussianFactory implements DistributionFactory
+ {
+ final long min, max;
+ final double mean, stdev;
+ private GaussianFactory(long min, long max, double mean, double stdev)
+ {
+ this.min = min;
+ this.max = max;
+ this.stdev = stdev;
+ this.mean = mean;
+ }
+
+ @Override
+ public Distribution get()
+ {
+ return new DistributionBoundApache(new NormalDistribution(mean, stdev), min, max);
+ }
+ }
+
+ private static final class UniformFactory implements DistributionFactory
+ {
+ final long min, max;
+ private UniformFactory(long min, long max)
+ {
+ this.min = min;
+ this.max = max;
+ }
+
+ @Override
+ public Distribution get()
+ {
+ return new DistributionBoundApache(new UniformRealDistribution(min, max), min, max);
+ }
+ }
+
+ private static final class FixedFactory implements DistributionFactory
+ {
+ final long key;
+ private FixedFactory(long key)
+ {
+ this.key = key;
+ }
+
+ @Override
+ public Distribution get()
+ {
+ return new DistributionFixed(key);
+ }
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return prefix.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object that)
+ {
+ return super.equals(that) && ((OptionDistribution) that).prefix.equals(this.prefix);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/OptionMulti.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/OptionMulti.java b/tools/stress/src/org/apache/cassandra/stress/settings/OptionMulti.java
new file mode 100644
index 0000000..9b92462
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/OptionMulti.java
@@ -0,0 +1,107 @@
+package org.apache.cassandra.stress.settings;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * For specifying multiple grouped sub-options in the form: group(arg1=,arg2,arg3) etc.
+ */
+abstract class OptionMulti extends Option
+{
+
+ private static final Pattern ARGS = Pattern.compile("([^,]+)", Pattern.CASE_INSENSITIVE);
+
+ private final class Delegate extends GroupedOptions
+ {
+ @Override
+ public List<? extends Option> options()
+ {
+ return OptionMulti.this.options();
+ }
+ }
+
+ protected abstract List<? extends Option> options();
+
+ private final String name;
+ private final Pattern pattern;
+ private final String description;
+ private final Delegate delegate = new Delegate();
+ public OptionMulti(String name, String description)
+ {
+ this.name = name;
+ pattern = Pattern.compile(name + "\\((.*)\\)", Pattern.CASE_INSENSITIVE);
+ this.description = description;
+ }
+
+ @Override
+ public boolean accept(String param)
+ {
+ Matcher m = pattern.matcher(param);
+ if (!m.matches())
+ return false;
+ m = ARGS.matcher(m.group(1));
+ int last = -1;
+ while (m.find())
+ {
+ if (m.start() != last + 1)
+ throw new IllegalArgumentException("Invalid " + name + " specification: " + param);
+ last = m.end();
+ if (!delegate.accept(m.group()))
+ throw new IllegalArgumentException("Invalid " + name + " specification: " + m.group());
+ }
+ return true;
+ }
+
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(name);
+ sb.append("(");
+ for (Option option : options())
+ {
+ sb.append(option);
+ sb.append(",");
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+
+ @Override
+ public String shortDisplay()
+ {
+ return name + "(?)";
+ }
+
+ @Override
+ public String longDisplay()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(name);
+ sb.append("(");
+ for (Option opt : options())
+ {
+ sb.append(opt.shortDisplay());
+ }
+ sb.append("): ");
+ sb.append(description);
+ return sb.toString();
+ }
+
+ @Override
+ public List<String> multiLineDisplay()
+ {
+ final List<String> r = new ArrayList<>();
+ for (Option option : options())
+ r.add(option.longDisplay());
+ return r;
+ }
+
+ @Override
+ boolean happy()
+ {
+ return delegate.happy();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/OptionReplication.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/OptionReplication.java b/tools/stress/src/org/apache/cassandra/stress/settings/OptionReplication.java
new file mode 100644
index 0000000..b145de4
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/OptionReplication.java
@@ -0,0 +1,114 @@
+package org.apache.cassandra.stress.settings;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.cassandra.locator.AbstractReplicationStrategy;
+
+/**
+ * For specifying replication options
+ */
+class OptionReplication extends Option
+{
+
+ private static final Pattern FULL = Pattern.compile("replication\\((.*)\\)", Pattern.CASE_INSENSITIVE);
+ private static final Pattern OPTION = Pattern.compile("([^,=]+)=([^,]+)", Pattern.CASE_INSENSITIVE);
+
+ private String strategy = "org.apache.cassandra.locator.SimpleStrategy";
+ private Map<String, String> options = new HashMap<>();
+
+ public String getStrategy()
+ {
+ return strategy;
+ }
+
+ public Map<String, String> getOptions()
+ {
+ if (!options.containsKey("replication_factor") && strategy.endsWith("SimpleStrategy"))
+ options.put("replication_factor", "1");
+ return options;
+ }
+
+
+ @Override
+ public boolean accept(String param)
+ {
+ Matcher m = FULL.matcher(param);
+ if (!m.matches())
+ return false;
+ String args = m.group(1);
+ m = OPTION.matcher(args);
+ int last = -1;
+ while (m.find())
+ {
+ if (m.start() != last + 1)
+ throw new IllegalArgumentException("Invalid replication specification: " + param);
+ last = m.end();
+ String key = m.group(1).toLowerCase();
+ sw: switch(key)
+ {
+ case "factor":
+ try
+ {
+ Integer.parseInt(m.group(2));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("Invalid replication factor: " + param);
+ }
+ options.put("replication_factor", m.group(2));
+ break;
+ case "strategy":
+ for (String name : new String[] { m.group(2), "org.apache.cassandra.locator." + m.group(2) })
+ {
+ try
+ {
+ Class<?> clazz = Class.forName(name);
+ if (!AbstractReplicationStrategy.class.isAssignableFrom(clazz))
+ throw new RuntimeException();
+ strategy = name;
+ break sw;
+ } catch (Exception _)
+ {
+ }
+ }
+ throw new IllegalArgumentException("Invalid replication strategy: " + param);
+ default:
+
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean happy()
+ {
+ return true;
+ }
+
+ @Override
+ public String shortDisplay()
+ {
+ return "replication(?)";
+ }
+
+ @Override
+ public String longDisplay()
+ {
+ return "replication(factor=?,strategy=?,<option1>=?,...)";
+ }
+
+ @Override
+ public List<String> multiLineDisplay()
+ {
+ return Arrays.asList(
+ GroupedOptions.formatMultiLine("factor=?","The replication factor to use (default 1)"),
+ GroupedOptions.formatMultiLine("strategy=?","The replication strategy to use (default SimpleStrategy)"),
+ GroupedOptions.formatMultiLine("option=?","Arbitrary replication strategy options")
+ );
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/OptionSimple.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/OptionSimple.java b/tools/stress/src/org/apache/cassandra/stress/settings/OptionSimple.java
new file mode 100644
index 0000000..01e75b5
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/OptionSimple.java
@@ -0,0 +1,131 @@
+package org.apache.cassandra.stress.settings;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * For parsing a simple (sub)option for a command/major option
+ */
+class OptionSimple extends Option
+{
+
+ final String displayPrefix;
+ final Pattern matchPrefix;
+ final String defaultValue;
+ final Pattern pattern;
+ final String description;
+ final boolean required;
+ String value;
+
+ public OptionSimple(String prefix, String valuePattern, String defaultValue, String description, boolean required)
+ {
+ this.displayPrefix = prefix;
+ this.matchPrefix = Pattern.compile(Pattern.quote(prefix), Pattern.CASE_INSENSITIVE);
+ this.pattern = Pattern.compile(valuePattern, Pattern.CASE_INSENSITIVE);
+ this.defaultValue = defaultValue;
+ this.description = description;
+ this.required = required;
+ }
+
+ public OptionSimple(String displayPrefix, Pattern matchPrefix, Pattern valuePattern, String defaultValue, String description, boolean required)
+ {
+ this.displayPrefix = displayPrefix;
+ this.matchPrefix = matchPrefix;
+ this.pattern = valuePattern;
+ this.defaultValue = defaultValue;
+ this.description = description;
+ this.required = required;
+ }
+
+ public boolean setByUser()
+ {
+ return value != null;
+ }
+
+ public boolean present()
+ {
+ return value != null || defaultValue != null;
+ }
+
+ public String value()
+ {
+ return value != null ? value : defaultValue;
+ }
+
+ public boolean accept(String param)
+ {
+ if (matchPrefix.matcher(param).lookingAt())
+ {
+ if (value != null)
+ throw new IllegalArgumentException("Suboption " + displayPrefix + " has been specified more than once");
+ String v = param.substring(displayPrefix.length());
+ if (!pattern.matcher(v).matches())
+ throw new IllegalArgumentException("Invalid option " + param + "; must match pattern " + pattern);
+ value = v;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean happy()
+ {
+ return !required || value != null;
+ }
+
+ public String shortDisplay()
+ {
+ StringBuilder sb = new StringBuilder();
+ if (!required)
+ sb.append("[");
+ sb.append(displayPrefix);
+ if (displayPrefix.endsWith("="))
+ sb.append("?");
+ if (displayPrefix.endsWith("<"))
+ sb.append("?");
+ if (displayPrefix.endsWith(">"))
+ sb.append("?");
+ if (!required)
+ sb.append("]");
+ return sb.toString();
+ }
+
+ public String longDisplay()
+ {
+ if (description.equals("") && defaultValue == null && pattern.pattern().equals(""))
+ return null;
+ StringBuilder sb = new StringBuilder();
+ sb.append(displayPrefix);
+ if (displayPrefix.endsWith("="))
+ sb.append("?");
+ if (displayPrefix.endsWith("<"))
+ sb.append("?");
+ if (displayPrefix.endsWith(">"))
+ sb.append("?");
+ if (defaultValue != null)
+ {
+ sb.append(" (default=");
+ sb.append(defaultValue);
+ sb.append(")");
+ }
+ return GroupedOptions.formatLong(sb.toString(), description);
+ }
+
+ public List<String> multiLineDisplay()
+ {
+ return Collections.emptyList();
+ }
+
+ public int hashCode()
+ {
+ return displayPrefix.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object that)
+ {
+ return that instanceof OptionSimple && ((OptionSimple) that).displayPrefix.equals(this.displayPrefix);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/SettingsColumn.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsColumn.java b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsColumn.java
new file mode 100644
index 0000000..b3cca10
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsColumn.java
@@ -0,0 +1,176 @@
+package org.apache.cassandra.stress.settings;
+
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.stress.generatedata.*;
+
+/**
+ * For parsing column options
+ */
+public class SettingsColumn implements Serializable
+{
+
+ public final int maxColumnsPerKey;
+ public final List<ByteBuffer> names;
+ public final String comparator;
+ public final boolean useTimeUUIDComparator;
+ public final int superColumns;
+ public final boolean useSuperColumns;
+ public final boolean variableColumnCount;
+
+ private final DistributionFactory sizeDistribution;
+ private final DistributionFactory countDistribution;
+ private final DataGenFactory dataGenFactory;
+
+ public SettingsColumn(GroupedOptions options)
+ {
+ this((Options) options,
+ options instanceof NameOptions ? (NameOptions) options : null,
+ options instanceof CountOptions ? (CountOptions) options : null
+ );
+ }
+
+ public SettingsColumn(Options options, NameOptions name, CountOptions count)
+ {
+ sizeDistribution = options.size.get();
+ superColumns = Integer.parseInt(options.superColumns.value());
+ dataGenFactory = options.generator.get();
+ useSuperColumns = superColumns > 0;
+ {
+ comparator = options.comparator.value();
+ AbstractType parsed = null;
+
+ try
+ {
+ parsed = TypeParser.parse(comparator);
+ }
+ catch (Exception e)
+ {
+ System.err.println(e.getMessage());
+ System.exit(1);
+ }
+
+ useTimeUUIDComparator = parsed instanceof TimeUUIDType;
+
+ if (!(parsed instanceof TimeUUIDType || parsed instanceof AsciiType || parsed instanceof UTF8Type))
+ {
+ System.err.println("Currently supported types are: TimeUUIDType, AsciiType, UTF8Type.");
+ System.exit(1);
+ }
+ }
+ if (name != null)
+ {
+ assert count == null;
+
+ AbstractType comparator;
+ try
+ {
+ comparator = TypeParser.parse(this.comparator);
+ } catch (Exception e)
+ {
+ throw new IllegalStateException(e);
+ }
+
+ final String[] names = name.name.value().split(",");
+ this.names = new ArrayList<>(names.length);
+
+ for (String columnName : names)
+ this.names.add(comparator.fromString(columnName));
+
+ final int nameCount = this.names.size();
+ countDistribution = new DistributionFactory()
+ {
+ @Override
+ public Distribution get()
+ {
+ return new DistributionFixed(nameCount);
+ }
+ };
+ }
+ else
+ {
+ this.countDistribution = count.count.get();
+ this.names = null;
+ }
+ maxColumnsPerKey = (int) countDistribution.get().maxValue();
+ variableColumnCount = countDistribution.get().minValue() < maxColumnsPerKey;
+ }
+
+ public RowGen newRowGen()
+ {
+ return new RowGenDistributedSize(dataGenFactory.get(), countDistribution.get(), sizeDistribution.get());
+ }
+
+ // Option Declarations
+
+ private static abstract class Options extends GroupedOptions
+ {
+ final OptionSimple superColumns = new OptionSimple("super=", "[0-9]+", "0", "Number of super columns to use (no super columns used if not specified)", false);
+ final OptionSimple comparator = new OptionSimple("comparator=", "TimeUUIDType|AsciiType|UTF8Type", "AsciiType", "Column Comparator to use", false);
+ final OptionDistribution size = new OptionDistribution("size=", "FIXED(34)");
+ final OptionDataGen generator = new OptionDataGen("data=", "REPEAT(50)");
+ }
+
+ private static final class NameOptions extends Options
+ {
+ final OptionSimple name = new OptionSimple("names=", ".*", null, "Column names", true);
+
+ @Override
+ public List<? extends Option> options()
+ {
+ return Arrays.asList(name, superColumns, comparator, size, generator);
+ }
+ }
+
+ private static final class CountOptions extends Options
+ {
+ final OptionDistribution count = new OptionDistribution("n=", "FIXED(5)");
+
+ @Override
+ public List<? extends Option> options()
+ {
+ return Arrays.asList(count, superColumns, comparator, size, generator);
+ }
+ }
+
+ // CLI Utility Methods
+
+ static SettingsColumn get(Map<String, String[]> clArgs)
+ {
+ String[] params = clArgs.remove("-col");
+ if (params == null)
+ return new SettingsColumn(new CountOptions());
+
+ GroupedOptions options = GroupedOptions.select(params, new NameOptions(), new CountOptions());
+ if (options == null)
+ {
+ printHelp();
+ System.out.println("Invalid -col options provided, see output for valid options");
+ System.exit(1);
+ }
+ return new SettingsColumn(options);
+ }
+
+ static void printHelp()
+ {
+ GroupedOptions.printOptions(System.out, "-col", new NameOptions(), new CountOptions());
+ }
+
+ static Runnable helpPrinter()
+ {
+ return new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ printHelp();
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCommand.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCommand.java b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCommand.java
new file mode 100644
index 0000000..a996988
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCommand.java
@@ -0,0 +1,159 @@
+package org.apache.cassandra.stress.settings;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.thrift.ConsistencyLevel;
+
+// Generic command settings - common to read/write/etc
+public class SettingsCommand implements Serializable
+{
+
+ public final Command type;
+ public final long count;
+ public final int tries;
+ public final boolean ignoreErrors;
+ public final ConsistencyLevel consistencyLevel;
+ public final double targetUncertainty;
+ public final int minimumUncertaintyMeasurements;
+ public final int maximumUncertaintyMeasurements;
+
+ public SettingsCommand(Command type, GroupedOptions options)
+ {
+ this(type, (Options) options,
+ options instanceof Count ? (Count) options : null,
+ options instanceof Uncertainty ? (Uncertainty) options : null
+ );
+ }
+
+ public SettingsCommand(Command type, Options options, Count count, Uncertainty uncertainty)
+ {
+ this.type = type;
+ this.tries = Math.max(1, Integer.parseInt(options.retries.value()) + 1);
+ this.ignoreErrors = options.ignoreErrors.setByUser();
+ this.consistencyLevel = ConsistencyLevel.valueOf(options.consistencyLevel.value().toUpperCase());
+ if (count != null)
+ {
+ this.count = Long.parseLong(count.count.value());
+ this.targetUncertainty = -1;
+ this.minimumUncertaintyMeasurements = -1;
+ this.maximumUncertaintyMeasurements = -1;
+ }
+ else
+ {
+ this.count = -1;
+ this.targetUncertainty = Double.parseDouble(uncertainty.uncertainty.value());
+ this.minimumUncertaintyMeasurements = Integer.parseInt(uncertainty.minMeasurements.value());
+ this.maximumUncertaintyMeasurements = Integer.parseInt(uncertainty.maxMeasurements.value());
+ }
+ }
+
+ // Option Declarations
+
+ static abstract class Options extends GroupedOptions
+ {
+ final OptionSimple retries = new OptionSimple("tries=", "[0-9]+", "9", "Number of tries to perform for each operation before failing", false);
+ final OptionSimple ignoreErrors = new OptionSimple("ignore_errors", "", null, "Do not print/log errors", false);
+ final OptionSimple consistencyLevel = new OptionSimple("cl=", "ONE|QUORUM|LOCAL_QUORUM|EACH_QUORUM|ALL|ANY", "ONE", "Consistency level to use", false);
+ }
+
+ static class Count extends Options
+ {
+
+ final OptionSimple count = new OptionSimple("n=", "[0-9]+", null, "Number of operations to perform", true);
+
+ @Override
+ public List<? extends Option> options()
+ {
+ return Arrays.asList(count, retries, ignoreErrors, consistencyLevel);
+ }
+ }
+
+ static class Uncertainty extends Options
+ {
+
+ final OptionSimple uncertainty = new OptionSimple("err<", "0\\.[0-9]+", "0.02", "Run until the standard error of the mean is below this fraction", false);
+ final OptionSimple minMeasurements = new OptionSimple("n>", "[0-9]+", "30", "Run at least this many iterations before accepting uncertainty convergence", false);
+ final OptionSimple maxMeasurements = new OptionSimple("n<", "[0-9]+", "200", "Run at most this many iterations before accepting uncertainty convergence", false);
+
+ @Override
+ public List<? extends Option> options()
+ {
+ return Arrays.asList(uncertainty, minMeasurements, maxMeasurements, retries, ignoreErrors, consistencyLevel);
+ }
+ }
+
+ // CLI Utility Methods
+
+ static SettingsCommand get(Map<String, String[]> clArgs)
+ {
+ for (Command cmd : Command.values())
+ {
+ if (cmd.category == null)
+ continue;
+ final String[] params = clArgs.remove(cmd.toString().toLowerCase());
+ if (params != null)
+ {
+ switch (cmd.category)
+ {
+ case BASIC:
+ return build(cmd, params);
+ case MULTI:
+ return SettingsCommandMulti.build(cmd, params);
+ case MIXED:
+ return SettingsCommandMixed.build(params);
+ }
+ }
+ }
+ return null;
+ }
+
+ static SettingsCommand build(Command type, String[] params)
+ {
+ GroupedOptions options = GroupedOptions.select(params, new Count(), new Uncertainty());
+ if (options == null)
+ {
+ printHelp(type);
+ System.out.println("Invalid " + type + " options provided, see output for valid options");
+ System.exit(1);
+ }
+ return new SettingsCommand(type, options);
+ }
+
+ static void printHelp(Command type)
+ {
+ printHelp(type.toString().toLowerCase());
+ }
+
+ static void printHelp(String type)
+ {
+ GroupedOptions.printOptions(System.out, type.toString().toLowerCase(), new Uncertainty(), new Count());
+ }
+
+ static Runnable helpPrinter(final String type)
+ {
+ return new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ printHelp(type);
+ }
+ };
+ }
+
+ static Runnable helpPrinter(final Command type)
+ {
+ return new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ printHelp(type);
+ }
+ };
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCommandMixed.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCommandMixed.java b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCommandMixed.java
new file mode 100644
index 0000000..995e7d6
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCommandMixed.java
@@ -0,0 +1,184 @@
+package org.apache.cassandra.stress.settings;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cassandra.stress.generatedata.Distribution;
+import org.apache.cassandra.stress.generatedata.DistributionFactory;
+import org.apache.commons.math3.distribution.EnumeratedDistribution;
+import org.apache.commons.math3.util.Pair;
+
+// Settings unique to the mixed command type
+public class SettingsCommandMixed extends SettingsCommandMulti
+{
+
+ // Ratios for selecting commands - index for each Command, NaN indicates the command is not requested
+ private final List<Pair<Command, Double>> ratios;
+ private final DistributionFactory clustering;
+
+ public SettingsCommandMixed(Options options)
+ {
+ super(Command.MIXED, options.parent);
+
+ OptionSimple[] ratiosIn = options.probabilities.ratios;
+ List<Pair<Command, Double>> ratiosOut = new ArrayList<>();
+ for (int i = 0 ; i < ratiosIn.length ; i++)
+ {
+ if (ratiosIn[i] != null && ratiosIn[i].present())
+ {
+ double d = Double.parseDouble(ratiosIn[i].value());
+ if (d > 0)
+ ratiosOut.add(new Pair<>(Command.values()[i], d));
+ }
+ }
+
+ ratios = ratiosOut;
+ clustering = options.clustering.get();
+
+ if (ratios.size() == 0)
+ throw new IllegalArgumentException("Must specify at least one command with a non-zero ratio");
+ }
+
+ public List<Command> getCommands()
+ {
+ final List<Command> r = new ArrayList<>();
+ for (Pair<Command, Double> p : ratios)
+ r.add(p.getFirst());
+ return r;
+ }
+
+ public CommandSelector selector()
+ {
+ return new CommandSelector(ratios, clustering.get());
+ }
+
+ // Class for randomly selecting the next command type
+
+ public static final class CommandSelector
+ {
+
+ final EnumeratedDistribution<Command> selector;
+ final Distribution count;
+ private Command cur;
+ private long remaining;
+
+ public CommandSelector(List<Pair<Command, Double>> ratios, Distribution count)
+ {
+ selector = new EnumeratedDistribution<>(ratios);
+ this.count = count;
+ }
+
+ public Command next()
+ {
+ while (remaining == 0)
+ {
+ remaining = count.next();
+ cur = selector.sample();
+ }
+ remaining--;
+ return cur;
+ }
+ }
+
+ // Option Declarations
+
+ static final class Probabilities extends OptionMulti
+ {
+ // entry for each in Command.values()
+ final OptionSimple[] ratios;
+ final List<OptionSimple> grouping;
+
+ public Probabilities()
+ {
+ super("ratio", "Specify the ratios for operations to perform; e.g. (reads=2,writes=1) will perform 2 reads for each write");
+ OptionSimple[] ratios = new OptionSimple[Command.values().length];
+ List<OptionSimple> grouping = new ArrayList<>();
+ for (Command command : Command.values())
+ {
+ if (command.category == null)
+ continue;
+ String defaultValue;
+ switch (command)
+ {
+ case MIXED:
+ continue;
+ case READ:
+ case WRITE:
+ defaultValue = "1";
+ break;
+ default:
+ defaultValue = null;
+ }
+ OptionSimple ratio = new OptionSimple(command.toString().toLowerCase() +
+ "=", "[0-9]+(\\.[0-9]+)?", defaultValue, "Performs this many " + command + " operations out of total", false);
+ ratios[command.ordinal()] = ratio;
+ grouping.add(ratio);
+ }
+ this.grouping = grouping;
+ this.ratios = ratios;
+ }
+
+ @Override
+ public List<? extends Option> options()
+ {
+ return grouping;
+ }
+ }
+
+ static final class Options extends GroupedOptions
+ {
+ final SettingsCommandMulti.Options parent;
+ protected Options(SettingsCommandMulti.Options parent)
+ {
+ this.parent = parent;
+ }
+ final OptionDistribution clustering = new OptionDistribution("clustering=", "GAUSSIAN(1..10)");
+ final Probabilities probabilities = new Probabilities();
+
+ @Override
+ public List<? extends Option> options()
+ {
+ final List<Option> options = new ArrayList<>();
+ options.add(clustering);
+ options.add(probabilities);
+ options.addAll(parent.options());
+ return options;
+ }
+
+ }
+
+ // CLI utility methods
+
+ public static SettingsCommandMixed build(String[] params)
+ {
+ GroupedOptions options = GroupedOptions.select(params,
+ new Options(new SettingsCommandMulti.Options(new Uncertainty())),
+ new Options(new SettingsCommandMulti.Options(new Count())));
+ if (options == null)
+ {
+ printHelp();
+ System.out.println("Invalid MIXED options provided, see output for valid options");
+ System.exit(1);
+ }
+ return new SettingsCommandMixed((Options) options);
+ }
+
+ public static void printHelp()
+ {
+ GroupedOptions.printOptions(System.out, "mixed",
+ new Options(new SettingsCommandMulti.Options(new Uncertainty())),
+ new Options(new SettingsCommandMulti.Options(new Count())));
+ }
+
+ public static Runnable helpPrinter()
+ {
+ return new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ printHelp();
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e1e98ad/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCommandMulti.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCommandMulti.java b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCommandMulti.java
new file mode 100644
index 0000000..720b0c6
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCommandMulti.java
@@ -0,0 +1,69 @@
+package org.apache.cassandra.stress.settings;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// Settings common to commands that operate over multiple keys at once
+public class SettingsCommandMulti extends SettingsCommand
+{
+
+ public final int keysAtOnce;
+
+ public SettingsCommandMulti(Command type, Options options)
+ {
+ super(type, options.parent);
+ this.keysAtOnce = Integer.parseInt(options.maxKeys.value());
+ }
+
+ // Option Declarations
+
+ static final class Options extends GroupedOptions
+ {
+ final GroupedOptions parent;
+ Options(GroupedOptions parent)
+ {
+ this.parent = parent;
+ }
+ final OptionSimple maxKeys = new OptionSimple("at-once=", "[0-9]+", "1000", "Number of keys per operation", false);
+
+ @Override
+ public List<? extends Option> options()
+ {
+ final List<Option> options = new ArrayList<>();
+ options.add(maxKeys);
+ options.addAll(parent.options());
+ return options;
+ }
+ }
+
+ // CLI Utility Methods
+
+ public static SettingsCommand build(Command type, String[] params)
+ {
+ GroupedOptions options = GroupedOptions.select(params, new Options(new Uncertainty()), new Options(new Count()));
+ if (options == null)
+ {
+ printHelp(type);
+ System.out.println("Invalid " + type + " options provided, see output for valid options");
+ System.exit(1);
+ }
+ return new SettingsCommandMulti(type, (Options) options);
+ }
+
+ public static void printHelp(Command type)
+ {
+ GroupedOptions.printOptions(System.out, type.toString().toLowerCase(), new Options(new Uncertainty()), new Options(new Count()));
+ }
+
+ public static Runnable helpPrinter(final Command type)
+ {
+ return new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ printHelp(type);
+ }
+ };
+ }
+}