
com.yugabyte.sample.common.CmdLineOpts Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of yb-sample-apps Show documentation
Show all versions of yb-sample-apps Show documentation
Sample applications for benchmarking YugaByte DB functionality on various workload types.
The newest version!
// Copyright (c) YugaByte, 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 com.yugabyte.sample.common;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.log4j.Logger;
import com.google.common.collect.ImmutableList;
// Import * so we can list the sample apps.
import com.yugabyte.sample.apps.*;
/**
* This is a helper class to parse the user specified command-line options if they were specified,
* print help messages when running the app, etc.
*/
public class CmdLineOpts {
private static final Logger LOG = Logger.getLogger(CmdLineOpts.class);
// This is a unique UUID that is created by each instance of the application. This UUID is used in
// various apps to make the keys unique. This allows us to run multiple instances of the app
// safely.
public static UUID loadTesterUUID;
// The various apps present in this sample.
private final static List HELP_WORKLOADS = ImmutableList.of(
CassandraHelloWorld.class,
CassandraKeyValue.class,
CassandraRangeKeyValue.class,
CassandraBatchKeyValue.class,
CassandraBatchTimeseries.class,
CassandraEventData.class,
CassandraTransactionalKeyValue.class,
CassandraTransactionalRestartRead.class,
CassandraStockTicker.class,
CassandraTimeseries.class,
CassandraUserId.class,
CassandraPersonalization.class,
CassandraSecondaryIndex.class,
CassandraUniqueSecondaryIndex.class,
RedisKeyValue.class,
RedisPipelinedKeyValue.class,
RedisHashPipelined.class,
RedisYBClientKeyValue.class,
SqlForeignKeysAndJoins.class,
SqlInserts.class,
SqlSecondaryIndex.class,
SqlSnapshotTxns.class,
SqlUpdates.class
);
// The class type of the app needed to spawn new objects.
private Class extends AppBase> appClass;
// List of database contact points. One contact point could be resolved to multiple nodes IPs.
public List contactPoints = new ArrayList<>();
// The number of reader threads to spawn for OLTP apps.
int numReaderThreads;
// The number of writer threads to spawn for OLTP apps.
int numWriterThreads;
boolean readOnly = false;
boolean localReads = false;
// Random number generator.
Random random = new Random();
// Command line opts parser.
CommandLine commandLine;
public void initialize(CommandLine commandLine) throws ClassNotFoundException {
this.commandLine = commandLine;
if (commandLine.hasOption("uuid")) {
loadTesterUUID = UUID.fromString(commandLine.getOptionValue("uuid"));
LOG.info("Using given UUID : " + loadTesterUUID);
} else if (commandLine.hasOption("nouuid")) {
loadTesterUUID = null;
LOG.info("Using NO UUID");
} else {
loadTesterUUID = UUID.randomUUID();
LOG.info("Using a randomly generated UUID : " + loadTesterUUID);
}
// Get the workload.
String appName = commandLine.getOptionValue("workload");
appClass = getAppClass(appName);
LOG.info("App: " + appClass.getSimpleName());
AppBase.appConfig.appName = appClass.getSimpleName();
if (commandLine.hasOption("skip_workload")) {
AppBase.appConfig.skipWorkload = true;
// For now, drop table is the only op that is permitted without workload.
if (!commandLine.hasOption("drop_table_name")) {
LOG.error("Table name to be dropped is expected when skipping workload run.");
System.exit(1);
}
}
if (commandLine.hasOption("run_time")) {
AppBase.appConfig.runTimeSeconds =
Integer.parseInt(commandLine.getOptionValue("run_time"));
}
LOG.info("Run time (seconds): " + AppBase.appConfig.runTimeSeconds);
// Get the proxy contact points.
List hostPortList = Arrays.asList(commandLine.getOptionValue("nodes").split(","));
for (String hostPort : hostPortList) {
LOG.info("Adding node: " + hostPort);
this.contactPoints.add(ContactPoint.fromHostPort(hostPort));
}
// This check needs to be done before initializeThreadCount is called.
if (commandLine.hasOption("read_only")) {
AppBase.appConfig.readOnly = true;
readOnly = true;
if (!commandLine.hasOption("uuid") && !commandLine.hasOption("nouuid")) {
LOG.error("uuid (or nouuid) needs to be provided when using --read-only");
System.exit(1);
}
}
// Set the number of threads.
initializeThreadCount(commandLine);
// Initialize num keys.
initializeNumKeys(commandLine);
// Initialize table properties.
initializeTableProperties(commandLine);
if (commandLine.hasOption("local_reads")) {
AppBase.appConfig.localReads = true;
localReads = true;
}
LOG.info("Local reads: " + localReads);
LOG.info("Read only load: " + readOnly);
if (appName.equals(CassandraBatchTimeseries.class.getSimpleName())) {
if (commandLine.hasOption("read_batch_size")) {
AppBase.appConfig.cassandraReadBatchSize =
Integer.parseInt(commandLine.getOptionValue("read_batch_size"));
LOG.info("CassandraBatchTimeseries batch size: " +
AppBase.appConfig.cassandraReadBatchSize);
}
if (commandLine.hasOption("read_back_delta_from_now")) {
AppBase.appConfig.readBackDeltaTimeFromNow =
Integer.parseInt(commandLine.getOptionValue("read_back_delta_from_now"));
LOG.info("CassandraBatchTimeseries delta read: " +
AppBase.appConfig.readBackDeltaTimeFromNow);
}
}
if (appName.equals(CassandraEventData.class.getSimpleName())) {
if (commandLine.hasOption("read_batch_size")) {
AppBase.appConfig.cassandraReadBatchSize = Integer
.parseInt(commandLine.getOptionValue("read_batch_size"));
LOG.info("CassandraEventData batch size: " + AppBase.appConfig.cassandraReadBatchSize);
}
if (commandLine.hasOption("num_devices")) {
AppBase.appConfig.num_devices = Integer
.parseInt(commandLine.getOptionValue("num_devices"));
LOG.info("CassandraEventData num_devices: " + AppBase.appConfig.num_devices);
}
if (commandLine.hasOption("num_event_types")) {
AppBase.appConfig.num_event_types = Integer
.parseInt(commandLine.getOptionValue("num_event_types"));
LOG.info("CassandraEventData num_event_types: " + AppBase.appConfig.num_event_types);
}
if (commandLine.hasOption("read_back_delta_from_now")) {
AppBase.appConfig.readBackDeltaTimeFromNow = Integer
.parseInt(commandLine.getOptionValue("read_back_delta_from_now"));
LOG.info("CassandraEventData delta read: " + AppBase.appConfig.readBackDeltaTimeFromNow);
}
}
if (commandLine.hasOption("batch_size")) {
AppBase.appConfig.cassandraBatchSize =
Integer.parseInt(commandLine.getOptionValue("batch_size"));
if (AppBase.appConfig.cassandraBatchSize > AppBase.appConfig.numUniqueKeysToWrite) {
LOG.fatal("The batch size cannot be more than the number of unique keys");
System.exit(-1);
}
LOG.info("Batch size : " + AppBase.appConfig.cassandraBatchSize);
}
if (appName.equals(CassandraPersonalization.class.getSimpleName())) {
if (commandLine.hasOption("num_stores")) {
AppBase.appConfig.numStores = Integer.parseInt(commandLine.getOptionValue("num_stores"));
}
LOG.info("CassandraPersonalization number of stores : " + AppBase.appConfig.numStores);
if (commandLine.hasOption("num_new_coupons_per_customer")) {
AppBase.appConfig.numNewCouponsPerCustomer =
Integer.parseInt(commandLine.getOptionValue("num_new_coupons_per_customer"));
}
LOG.info("CassandraPersonalization number of new coupons per costomer : " +
AppBase.appConfig.numNewCouponsPerCustomer);
if (commandLine.hasOption("max_coupons_per_customer")) {
AppBase.appConfig.maxCouponsPerCustomer =
Integer.parseInt(commandLine.getOptionValue("max_coupons_per_customer"));
}
if (AppBase.appConfig.numNewCouponsPerCustomer >
AppBase.appConfig.maxCouponsPerCustomer) {
LOG.fatal(
"The number of new coupons cannot exceed the maximum number of coupons per customer");
System.exit(-1);
}
LOG.info("CassandraPersonalization maximum number of coupons per costomer : " +
AppBase.appConfig.maxCouponsPerCustomer);
}
if (appName.equals(CassandraSecondaryIndex.class.getSimpleName())) {
if (commandLine.hasOption("non_transactional_index")) {
AppBase.appConfig.nonTransactionalIndex = true;
}
LOG.info("CassandraSecondaryIndex non-transactional index");
if (commandLine.hasOption("batch_write")) {
AppBase.appConfig.batchWrite = true;
}
LOG.info("CassandraSecondaryIndex batch write");
}
if (appName.equals(RedisPipelinedKeyValue.class.getSimpleName()) ||
appName.equals(RedisHashPipelined.class.getSimpleName())) {
if (commandLine.hasOption("pipeline_length")) {
AppBase.appConfig.redisPipelineLength =
Integer.parseInt(commandLine.getOptionValue("pipeline_length"));
if (AppBase.appConfig.redisPipelineLength > AppBase.appConfig.numUniqueKeysToWrite) {
LOG.fatal("The pipeline length cannot be more than the number of unique keys");
System.exit(-1);
}
}
LOG.info("RedisPipelinedKeyValue pipeline length : " + AppBase.appConfig.redisPipelineLength);
}
if (appName.equals(RedisHashPipelined.class.getSimpleName())) {
if (commandLine.hasOption("num_subkeys_per_key")) {
AppBase.appConfig.numSubkeysPerKey =
Integer.parseInt(commandLine.getOptionValue("num_subkeys_per_key"));
if (AppBase.appConfig.redisPipelineLength >
AppBase.appConfig.numUniqueKeysToWrite) {
LOG.fatal("The pipeline length cannot be more than the number of unique keys");
System.exit(-1);
}
}
if (commandLine.hasOption("key_freq_zipf_exponent")) {
AppBase.appConfig.keyUpdateFreqZipfExponent = Double.parseDouble(
commandLine.getOptionValue("key_freq_zipf_exponent"));
}
if (commandLine.hasOption("subkey_freq_zipf_exponent")) {
AppBase.appConfig.subkeyUpdateFreqZipfExponent = Double.parseDouble(
commandLine.getOptionValue("subkey_freq_zipf_exponent"));
}
if (commandLine.hasOption("subkey_value_size_zipf_exponent")) {
AppBase.appConfig.valueSizeZipfExponent = Double.parseDouble(
commandLine.getOptionValue("subkey_value_size_zipf_exponent"));
}
if (commandLine.hasOption("subkey_value_max_size")) {
AppBase.appConfig.maxValueSize = Integer.parseInt(
commandLine.getOptionValue("subkey_value_max_size"));
}
if (commandLine.hasOption("num_subkeys_per_write")) {
AppBase.appConfig.numSubkeysPerWrite = Integer.parseInt(
commandLine.getOptionValue("num_subkeys_per_write"));
if (AppBase.appConfig.numSubkeysPerWrite >
AppBase.appConfig.numSubkeysPerKey) {
LOG.fatal("Writing more subkeys than the number of subkeys per key.");
System.exit(-1);
}
}
if (commandLine.hasOption("num_subkeys_per_read")) {
AppBase.appConfig.numSubkeysPerRead = Integer.parseInt(
commandLine.getOptionValue("num_subkeys_per_read"));
if (AppBase.appConfig.numSubkeysPerRead >
AppBase.appConfig.numSubkeysPerKey) {
LOG.fatal("Writing more subkeys than the number of subkeys per key.");
System.exit(-1);
}
}
}
if (commandLine.hasOption("with_local_dc")) {
if (AppBase.appConfig.disableYBLoadBalancingPolicy == true) {
LOG.error("--disable_yb_load_balancing_policy cannot be used with --with_local_dc");
System.exit(1);
}
AppBase.appConfig.localDc = commandLine.getOptionValue("with_local_dc");
}
if (commandLine.hasOption("use_redis_cluster")) {
AppBase.appConfig.useRedisCluster = true;
}
if (commandLine.hasOption("username")) {
AppBase.appConfig.dbUsername = commandLine.getOptionValue("username");
}
if (commandLine.hasOption("password")) {
if (!commandLine.hasOption("username")) {
LOG.error("--password requires --username to be set");
System.exit(1);
}
AppBase.appConfig.dbPassword = commandLine.getOptionValue("password");
}
if (commandLine.hasOption("concurrent_clients")) {
AppBase.appConfig.concurrentClients = Integer.parseInt(
commandLine.getOptionValue("concurrent_clients"));
}
if (commandLine.hasOption("ssl_cert")) {
AppBase.appConfig.sslCert = commandLine.getOptionValue("ssl_cert");
}
}
/**
* Creates new instance of the app.
* @return the app instance.
*/
public AppBase createAppInstance() {
return createAppInstance(true /* enableMetrics */);
}
/**
* Creates new instance of the app.
* @param enableMetrics Should metrics tracker be enabled.
* @return the app instance.
*/
public AppBase createAppInstance(boolean enableMetrics) {
AppBase workload = null;
try {
// Create a new workload object.
workload = appClass.newInstance();
// Initialize the workload.
workload.workloadInit(this, enableMetrics);
} catch (Exception e) {
LOG.error("Could not create instance of " + appClass.getName(), e);
}
return workload;
}
public CommandLine getCommandLine() {
return commandLine;
}
public List getContactPoints() {
return contactPoints;
}
public ContactPoint getRandomContactPoint() {
int contactPointId = random.nextInt(contactPoints.size());
LOG.debug("Returning random contact point id " + contactPointId);
return contactPoints.get(contactPointId);
}
public int getNumReaderThreads() {
return numReaderThreads;
}
public int getNumWriterThreads() {
return numWriterThreads;
}
public boolean getReadOnly() {
return readOnly;
}
public boolean doErrorChecking() {
return AppBase.appConfig.sanityCheckAtEnd;
}
public boolean shouldDropTable() {
return AppBase.appConfig.shouldDropTable;
}
public boolean skipWorkload() {
return AppBase.appConfig.skipWorkload;
}
public String appName() {
return AppBase.appConfig.appName;
}
private static Class extends AppBase> getAppClass(String workloadType)
throws ClassNotFoundException {
// Get the workload class.
return Class.forName("com.yugabyte.sample.apps." + workloadType)
.asSubclass(AppBase.class);
}
private void initializeThreadCount(CommandLine cmd) {
// Check if there are a fixed number of threads or variable.
String numThreadsStr = cmd.getOptionValue("num_threads");
if (readOnly) {
numReaderThreads = AppBase.appConfig.numReaderThreads;
numWriterThreads = 0;
} else if (AppBase.appConfig.readIOPSPercentage == -1) {
numReaderThreads = AppBase.appConfig.numReaderThreads;
numWriterThreads = AppBase.appConfig.numWriterThreads;
} else {
int numThreads = 0;
if (numThreadsStr != null) {
numThreads = Integer.parseInt(numThreadsStr);
} else {
// Default to 8 * num-cores
numThreads = 8 * Runtime.getRuntime().availableProcessors();
}
numReaderThreads =
(int) Math.round(1.0 * numThreads * AppBase.appConfig.readIOPSPercentage / 100);
numWriterThreads = numThreads - numReaderThreads;
}
// If number of read and write threads are specified on the command line, that overrides all
// the other values.
if (cmd.hasOption("num_threads_read")) {
numReaderThreads = Integer.parseInt(cmd.getOptionValue("num_threads_read"));
}
if (cmd.hasOption("num_threads_write")) {
if (readOnly) {
LOG.warn("Ignoring num_threads_write option. It shouldn't be used with read_only.");
} else {
numWriterThreads = Integer.parseInt(cmd.getOptionValue("num_threads_write"));
}
}
LOG.info("Num reader threads: " + numReaderThreads +
", num writer threads: " + numWriterThreads);
}
private void initializeNumKeys(CommandLine cmd) {
if (cmd.hasOption("num_writes")) {
AppBase.appConfig.numKeysToWrite = Long.parseLong(cmd.getOptionValue("num_writes"));
}
if (cmd.hasOption("num_reads")) {
AppBase.appConfig.numKeysToRead = Long.parseLong(cmd.getOptionValue("num_reads"));
}
if (cmd.hasOption("num_unique_keys")) {
AppBase.appConfig.numUniqueKeysToWrite =
Long.parseLong(cmd.getOptionValue("num_unique_keys"));
}
AppBase.appConfig.maxWrittenKey = Long.parseLong(cmd.getOptionValue("max_written_key",
String.valueOf(AppBase.appConfig.maxWrittenKey)));
if (cmd.hasOption("value_size")) {
AppBase.appConfig.valueSize = Integer.parseInt(cmd.getOptionValue("value_size"));
}
if (cmd.hasOption("sleep_time")) {
AppBase.appConfig.sleepTime =
Integer.parseInt(cmd.getOptionValue("sleep_time"));
}
if (cmd.hasOption("socket_timeout")) {
AppBase.appConfig.jedisSocketTimeout =
Integer.parseInt(cmd.getOptionValue("socket_timeout"));
}
if (cmd.hasOption("use_ascii_values")) {
AppBase.appConfig.restrictValuesToAscii = true;
}
if (cmd.hasOption("sanity_check_at_end")) {
AppBase.appConfig.sanityCheckAtEnd = true;
}
if (cmd.hasOption("disable_yb_load_balancing_policy")) {
AppBase.appConfig.disableYBLoadBalancingPolicy = true;
}
if (cmd.hasOption("print_all_exceptions")) {
AppBase.appConfig.printAllExceptions = true;
}
if (cmd.hasOption("create_table_name") && cmd.hasOption("drop_table_name")) {
LOG.error("Both create and drop table options cannot be provided together.");
System.exit(1);
}
if (cmd.hasOption("create_table_name")) {
AppBase.appConfig.tableName = cmd.getOptionValue("create_table_name");
LOG.info("Create table name: " + AppBase.appConfig.tableName);
}
if (cmd.hasOption("default_postgres_database")) {
AppBase.appConfig.defaultPostgresDatabase = cmd.getOptionValue("default_postgres_database");
LOG.info("Default postgres database: " + AppBase.appConfig.defaultPostgresDatabase);
}
if (cmd.hasOption("drop_table_name")) {
AppBase.appConfig.tableName = cmd.getOptionValue("drop_table_name");
LOG.info("Drop table name: " + AppBase.appConfig.tableName);
AppBase.appConfig.shouldDropTable = true;
}
LOG.info("Num unique keys to insert: " + AppBase.appConfig.numUniqueKeysToWrite);
LOG.info("Num keys to update: " +
(AppBase.appConfig.numKeysToWrite - AppBase.appConfig.numUniqueKeysToWrite));
LOG.info("Num keys to read: " + AppBase.appConfig.numKeysToRead);
LOG.info("Value size: " + AppBase.appConfig.valueSize);
LOG.info("Restrict values to ASCII strings: " + AppBase.appConfig.restrictValuesToAscii);
LOG.info("Perform sanity check at end of app run: " + AppBase.appConfig.sanityCheckAtEnd);
}
private void initializeTableProperties(CommandLine cmd) {
// Initialize the TTL.
if (cmd.hasOption("table_ttl_seconds")) {
AppBase.appConfig.tableTTLSeconds =
Long.parseLong(cmd.getOptionValue("table_ttl_seconds"));
}
LOG.info("Table TTL (secs): " + AppBase.appConfig.tableTTLSeconds);
}
/**
* Creates a command line opts object from the arguments specified on the command line.
* @param args command line args.
* @return a CmdLineOpts object.
* @throws java.lang.Exception exceptions during parsing and preparing of options.
*/
public static CmdLineOpts createFromArgs(String[] args) throws Exception {
Options options = new Options();
Option appType = OptionBuilder.create("workload");
appType.setDescription("The workload to run.");
appType.setRequired(true);
appType.setLongOpt("workload");
appType.setArgs(1);
options.addOption(appType);
Option proxyAddrs = OptionBuilder.create("nodes");
proxyAddrs.setDescription("Comma separated proxies, host1:port1,....,hostN:portN");
proxyAddrs.setRequired(true);
proxyAddrs.setLongOpt("nodes");
proxyAddrs.setArgs(1);
options.addOption(proxyAddrs);
options.addOption("help", false, "Show help message.");
options.addOption("verbose", false, "Enable debug level logging.");
options.addOption("uuid", true, "The UUID to use for this loadtester.");
options.addOption("nouuid", false,
"Do not use a UUID. Keys will be key:1, key:2, key:3, "
+ "instead of :1, :2, :3 etc.");
options.addOption("create_table_name", true, "The name of the CQL table to create.");
options.addOption("default_postgres_database", true, "The name of the default postgres db.");
options.addOption("drop_table_name", true, "The name of the CQL table to drop.");
options.addOption("read_only", false, "Read-only workload. " +
"Values must have been written previously and uuid must be provided. " +
"num_threads_write will be ignored.");
options.addOption("local_reads", false, "Use consistency ONE for reads.");
options.addOption("num_threads", true, "The total number of threads.");
options.addOption("num_threads_read", true, "The number of threads that perform reads.");
options.addOption("num_threads_write", true, "The number of threads that perform writes.");
options.addOption("num_writes", true, "The total number of writes to perform.");
options.addOption("num_reads", true, "The total number of reads to perform.");
options.addOption(
"sleep_time", true,
"How long (in ms) to sleep between multiple pipeline batches.");
options.addOption("socket_timeout", true,
"How long (in ms) to wait for a response from jedis.");
options.addOption("value_size", true, "Size in bytes of the value. " +
"The bytes are random. Value size should be more than 5 (9) bytes for binary (ascii) " +
"values in order to have checksum for read verification. First byte is used as a " +
"binary/ascii marker. If value size is more than 16 bytes, random content is prepended " +
"with \"val: $key\" string, so we can check if value matches the key during read.");
options.addOption("use_ascii_values", false, "[RedisKeyValue] If " +
"specified, values are restricted to ASCII strings.");
options.addOption("table_ttl_seconds", true, "The TTL in seconds to create the table with.");
options.addOption("sanity_check_at_end", false,
"Add FATAL logs to ensure no failures before terminating the app.");
options.addOption("disable_yb_load_balancing_policy", false,
"Disable Yugabyte load-balancing policy.");
options.addOption("print_all_exceptions", false,
"Print all exceptions encountered on the client, instead of sampling.");
options.addOption("skip_workload", false, "Skip running workload.");
options.addOption("run_time", true,
"Run time for workload. Negative value means forever (default).");
options.addOption("use_redis_cluster", false, "Use redis cluster client.");
options.addOption("username", true,
"User name to connect to the database using. ");
options.addOption("password", true,
"The password to use when connecting to the database. " +
"If this option is set, the --username option is required.");
options.addOption("concurrent_clients", true,
"The number of client connections to establish to each host in the YugaByte DB cluster.");
options.addOption("ssl_cert", true,
"Use an SSL connection while connecting to YugaByte.");
// Options for CassandraTimeseries workload.
options.addOption("num_users", true, "[CassandraTimeseries] The total number of users.");
options.addOption("min_nodes_per_user", true,
"[CassandraTimeseries] The min number of nodes per user.");
options.addOption("min_nodes_per_user", true,
"[CassandraTimeseries] The min number of nodes per user.");
options.addOption("max_nodes_per_user", true,
"[CassandraTimeseries] The max number of nodes per user.");
options.addOption("min_metrics_count", true,
"[CassandraTimeseries] The min number of metrics for all nodes of user.");
options.addOption("max_metrics_count", true,
"[CassandraTimeseries] The max number of metrics for all nodes of user.");
options.addOption("data_emit_rate_millis", true,
"[CassandraTimeseries] The rate at which each node emits all metrics.");
// Options for CassandraStockTicker workload.
options.addOption("num_ticker_symbols", true,
"[CassandraStockTicker] The total number of stock ticker symbols.");
// Options for the key-value workloads.
options.addOption("num_unique_keys", true,
"[KV workloads only] Number of unique keys to write into the DB.");
options.addOption("max_written_key", true,
"[KV workloads only, reusing existing table] Max written key number.");
// Options for CassandraBatchKeyValue app.
options.addOption("batch_size", true,
"[CassandraBatchKeyValue] Number of keys to write in a batch.");
// Options for CassandraBatchTimeseries app.
options.addOption("read_batch_size", true,
"[CassandraBatchTimeseries] Number of keys to read in a batch.");
options.addOption("read_back_delta_from_now", true,
"[CassandraBatchTimeseries] Time unit delta back from current time unit.");
options.addOption("with_local_dc", true, "Local DC name.");
// Options for CassandraEventData app.
options.addOption("read_batch_size", true, "[CassandraEventData] Number of keys to read in a batch.");
options.addOption("num_devices", true, "[CassandraEventData] Number of devices to generate data");
options.addOption("num_event_types", true, "[CassandraEventData] Number of event yypes to generate per device");
options.addOption("read_batch_size", true, "[CassandraEventData] Number of keys to read in a batch.");
options.addOption("read_back_delta_from_now", true,
"[CassandraEventData] Time unit delta back from current time unit.");
// Options for CassandraPersonalization app.
options.addOption("num_stores", true,
"[CassandraPersonalization] Number of stores.");
options.addOption("num_new_coupons_per_customer", true,
"[CassandraPersonalization] Number of new coupons per customer.");
options.addOption("max_coupons_per_customer", true,
"[CassandraPersonalization] Maximum number of coupons per customer.");
// Options for CassandraSecondaryIndex app.
options.addOption("non_transactional_index", false,
"[CassandraSecondaryIndex] Create secondary index without transactions " +
"enabled.");
options.addOption("batch_write", false,
"[CassandraSecondaryIndex] Enable batch write of key values.");
// Options for Redis Pipelined Key Value
options.addOption(
"pipeline_length", true,
"[RedisPipelinedKeyValue/RedisHashPipelined] Number of commands to be sent out"
+ " in a redis pipelined sync.");
options.addOption(
"num_subkeys_per_key", true,
"[RedisHashPipelined] Number of subkeys in each key for the RedisHashPipelined workload.");
options.addOption(
"num_subkeys_per_write", true,
"[RedisHashPipelined] Number of subkeys updated in each HMSet "
+ "for the RedisHashPipelined workload.");
options.addOption(
"num_subkeys_per_read", true,
"[RedisHashPipelined] Number of subkeys read in each HMGet "
+ "for the RedisHashPipelined workload.");
options.addOption(
"key_freq_zipf_exponent", true,
"[RedisHashPipelined] The zipf distribution exponent, if keys " +
"should be picked using a Zipf distribution. If <= 0, we use " +
"a uniform distribution");
options.addOption(
"subkey_freq_zipf_exponent", true,
"[RedisHashPipelined] The zipf distribution exponent, if subkeys " +
"should be picked using a Zipf distribution. If <= 0, we use " +
"a uniform distribution");
options.addOption(
"subkey_value_size_zipf_exponent", true,
"[RedisHashPipelined] The zipf distribution exponent, if the value " +
"sizes should be picked using a Zipf distribution. Value sizes are " +
"chosen such that the expected mean is the value specified by --value_size. " +
"If <= 0, all subkeys will have the value specified by --value_size");
options.addOption(
"subkey_value_max_size", true,
"[RedisHashPipelined] If using zipf distribution to choose value sizes, " +
"specifies an upper bound on the value sizes.");
// First check if a "--help" argument is passed with a simple parser. Note that if we add
// required args, then the help string would not work. See:
// https://stackoverflow.com/questions/36720946/apache-cli-required-options-contradicts-with-help-option
// The first function check if help was called with an app name to print details. The second
// function just check if help was called without any args to print the overview.
parseHelpDetailed(args, options);
parseHelpOverview(args, options);
// Do the actual arg parsing.
CommandLineParser parser = new BasicParser();
CommandLine commandLine = null;
try {
commandLine = parser.parse(options, args);
} catch (ParseException e) {
LOG.error("Error in args, use the --help option to see usage. Exception:", e);
System.exit(0);
}
CmdLineOpts configuration = new CmdLineOpts();
configuration.initialize(commandLine);
return configuration;
}
private static void parseHelpOverview(String[] args, Options options) throws Exception {
Options helpOptions = new Options();
helpOptions.addOption("help", false, "Print help.");
CommandLineParser helpParser = new BasicParser();
CommandLine helpCommandLine = null;
try {
helpCommandLine = helpParser.parse(helpOptions, args);
} catch (ParseException e) {
return;
}
if (helpCommandLine.hasOption("help")) {
printUsage(options, "Usage:");
System.exit(0);
}
}
private static void parseHelpDetailed(String[] args, Options options) throws Exception {
Options helpOptions = new Options();
helpOptions.addOption("help", true, "Print help.");
CommandLineParser helpParser = new BasicParser();
CommandLine helpCommandLine = null;
try {
helpCommandLine = helpParser.parse(helpOptions, args);
} catch (org.apache.commons.cli.MissingArgumentException e1) {
// This is ok since there was no help argument passed.
return;
} catch (ParseException e) {
return;
}
if (helpCommandLine.hasOption("help")) {
printUsageDetails(options, "Usage:", helpCommandLine.getOptionValue("help"));
System.exit(0);
}
}
private static int getAppPort(String appName) {
if (appName.startsWith("Cassandra")) return 9042;
else if (appName.startsWith("Redis")) return 6379;
else if (appName.startsWith("Sql")) return 5433;
return 0;
}
private static void printUsage(Options options, String header) throws Exception {
StringBuilder footer = new StringBuilder();
footer.append("****************************************************************************\n");
footer.append("* *\n");
footer.append("* YugaByte DB Sample Apps *\n");
footer.append("* *\n");
footer.append("****************************************************************************\n");
footer.append("\n");
footer.append("Use this sample app to try out a variety of workloads against YugaByte DB.\n");
footer.append(" Use the --help option to get more details on how to run it.\n");
String optsPrefix = "\t\t\t";
String optsSuffix = " \\\n";
for (Class cls: HELP_WORKLOADS) {
String workloadType = cls.getSimpleName();
AppBase workload = getAppClass(workloadType).newInstance();
String formattedName = String.format("%-35s: ", workloadType);
footer.append("\n * " + formattedName);
List description = workload.getWorkloadDescription();
if (!description.isEmpty()) {
footer.append(description.get(0));
}
}
footer.append("\n");
System.out.println(footer.toString());
System.exit(0);
}
private static void printUsageDetails(Options options, String header, String appName) throws Exception {
StringBuilder footer = new StringBuilder();
footer.append("Usage and options for workload " + appName + " in YugaByte DB Sample Apps.\n");
String optsPrefix = "\t\t\t";
String optsSuffix = " \\\n";
int port = getAppPort(appName);
AppBase workload = getAppClass(appName).newInstance();
footer.append("\n - " + appName + " :\n");
footer.append(" ");
for (int idx = 0; idx < appName.length(); idx++) {
footer.append("-");
}
footer.append("\n");
List description = workload.getWorkloadDescription();
if (!description.isEmpty()) {
for (String line : description) {
footer.append("\t\t");
footer.append(line);
footer.append("\n");
}
footer.append("\n");
}
footer.append("\t\tUsage:\n");
footer.append(optsPrefix);
footer.append("java -jar yb-sample-apps.jar");
footer.append(optsSuffix);
footer.append(optsPrefix + "--workload " + appName + optsSuffix);
footer.append(optsPrefix + "--nodes 127.0.0.1:" + port);
List requiredArgs = workload.getWorkloadRequiredArguments();
for (String line : requiredArgs) {
footer.append(optsSuffix).append(optsPrefix).append(line);
}
List optionalArgs = workload.getWorkloadOptionalArguments();
if (!optionalArgs.isEmpty()) {
footer.append("\n\n\t\tOther options (with default values):\n");
for (String line : optionalArgs) {
footer.append(optsPrefix + "[ ");
footer.append(line);
footer.append(" ]\n");
}
}
footer.append("\n");
System.out.println(footer.toString());
System.exit(0);
}
/**
* One contact point could be resolved to multiple nodes IPs. For example for Kubernetes
* yb-tservers.default.svc.cluster.local contact point is resolved to all tservers IPs.
*/
public static class ContactPoint {
String host;
int port;
public ContactPoint(String host, int port) {
this.host = host;
this.port = port;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public static ContactPoint fromHostPort(String hostPort) {
String[] parts = hostPort.split(":");
String host = parts[0];
int port = Integer.parseInt(parts[1]);
return new ContactPoint(host, port);
}
public String ToString() { return host + ":" + port; }
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy