voldemort.performance.benchmark.Benchmark.java Source code

Java tutorial

Introduction

Here is the source code for voldemort.performance.benchmark.Benchmark.java

Source

/*
 * Copyright 2008-2013 LinkedIn, Inc
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package voldemort.performance.benchmark;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.text.NumberFormat;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.FileUtils;

import joptsimple.OptionParser;
import joptsimple.OptionSet;
import voldemort.ServerTestUtils;
import voldemort.StaticStoreClientFactory;
import voldemort.TestUtils;
import voldemort.VoldemortException;
import voldemort.client.AbstractStoreClientFactory;
import voldemort.client.ClientConfig;
import voldemort.client.SocketStoreClientFactory;
import voldemort.client.StoreClient;
import voldemort.client.StoreClientFactory;
import voldemort.client.protocol.RequestFormatType;
import voldemort.serialization.IdentitySerializer;
import voldemort.serialization.Serializer;
import voldemort.serialization.SerializerDefinition;
import voldemort.serialization.StringSerializer;
import voldemort.serialization.json.JsonTypeSerializer;
import voldemort.server.VoldemortConfig;
import voldemort.store.StorageConfiguration;
import voldemort.store.StorageEngine;
import voldemort.store.Store;
import voldemort.store.StoreDefinition;
import voldemort.store.bdb.BdbStorageConfiguration;
import voldemort.store.metadata.MetadataStore;
import voldemort.store.serialized.SerializingStore;
import voldemort.store.views.ViewStorageConfiguration;
import voldemort.store.views.ViewStorageEngine;
import voldemort.utils.ByteArray;
import voldemort.utils.CmdUtils;
import voldemort.utils.Props;
import voldemort.utils.ReflectUtils;
import voldemort.utils.Time;
import voldemort.utils.Utils;
import voldemort.xml.StoreDefinitionsMapper;

public class Benchmark {

    private static final int MAX_WORKERS = 8;
    private static final int MAX_CONNECTIONS_PER_NODE = 50;

    /**
     * Constants for the benchmark file
     */
    public static final String PROP_FILE = "prop-file";
    public static final String THREADS = "threads";
    public static final String NUM_CONNECTIONS_PER_NODE = "num-connections-per-node";
    public static final String ITERATIONS = "iterations";
    public static final String STORAGE_CONFIGURATION_CLASS = "storage-configuration-class";
    public static final String INTERVAL = "interval";

    public static final String KEY_TYPE = "keyType";
    public static final String STRING_KEY_TYPE = "string";
    public static final String JSONINT_KEY_TYPE = "json-int";
    public static final String JSONSTRING_KEY_TYPE = "json-string";
    public static final String IDENTITY_KEY_TYPE = "identity";

    public static final String URL = "url";
    public static final String PERCENT_CACHED = "percent-cached";
    public static final String VALUE_SIZE = "value-size";
    public static final String IGNORE_NULLS = "ignore-nulls";
    public static final String REQUEST_FILE = "request-file";
    public static final String START_KEY_INDEX = "start-key-index";

    public static final String READS = "r";
    public static final String WRITES = "w";
    public static final String DELETES = "d";
    public static final String MIXED = "m";

    public static final String RECORD_SELECTION = "record-selection";
    public static final String ZIPFIAN_RECORD_SELECTION = "zipfian";
    public static final String LATEST_RECORD_SELECTION = "latest";
    public static final String FILE_RECORD_SELECTION = "file";
    public static final String UNIFORM_RECORD_SELECTION = "uniform";

    public static final String TARGET_THROUGHPUT = "target-throughput";
    public static final String HELP = "help";
    public static final String STORE_NAME = "store-name";
    public static final String RECORD_COUNT = "record-count";
    public static final String PLUGIN_CLASS = "plugin-class";
    public static final String OPS_COUNT = "ops-count";

    public static final String METRIC_TYPE = "metric-type";
    public static final String HISTOGRAM_METRIC_TYPE = "histogram";
    public static final String SUMMARY_METRIC_TYPE = "summary";

    public static final String VERBOSE = "v";
    public static final String VERIFY = "verify";
    public static final String CLIENT_ZONE_ID = "client-zoneid";
    private static final String DUMMY_DB = "benchmark_db";
    public static final String STORE_TYPE = "view";
    public static final String VIEW_CLASS = "voldemort.store.views.UpperCaseView";
    public static final String HAS_TRANSFORMS = "true";
    public static final String SAMPLE_SIZE = "sample-size";

    public static final String LOCAL_SERVER_PROPERTIES = "local-server-properties";

    public static final String WORKLOAD_TYPE = "workload-type";
    public static final String KEY_VALUE_FILE = "key-value-file";
    public static final String KEY_SEQUENCE_FILE = "key-seq-file";
    public static final String DEFAULT_WORKLOAD_TYPE = "default-workload";
    public static final String TRACE_WORKLOAD_TYPE = "trace-workload";

    private StoreClient<Object, Object> storeClient;
    private StoreClientFactory factory;

    private int numThreads, numConnectionsPerNode, numIterations, targetThroughput, recordCount, opsCount,
            statusIntervalSec;
    private double perThreadThroughputPerMs;
    private BenchmarkWorkload workLoad;
    private String pluginName;
    private boolean storeInitialized = false;
    private boolean warmUpCompleted = false;
    private boolean verbose = false;
    private boolean verifyRead = false;
    private boolean ignoreNulls = false;
    private String keyType;

    private boolean localMode = false;

    class StatusThread extends Thread {

        private Vector<Thread> threads;
        private int intervalSec;
        private long startTime;

        public StatusThread(Vector<Thread> threads, int intervalSec, long startTime) {
            this.threads = threads;
            this.intervalSec = intervalSec;
            this.startTime = startTime;
        }

        @Override
        public void run() {
            boolean testComplete = true;
            int totalOps = 0, prevTotalOps = 0;
            do {
                testComplete = true;
                totalOps = 0;
                for (Thread thread : this.threads) {
                    if (thread.getState() != Thread.State.TERMINATED) {
                        testComplete = false;
                    }
                    totalOps += ((ClientThread) thread).getOpsDone();
                }

                if (totalOps != 0 && totalOps != prevTotalOps) {
                    System.out.println("[status]\tThroughput(ops/sec): "
                            + Time.MS_PER_SECOND
                                    * ((double) totalOps / (double) (System.currentTimeMillis() - startTime))
                            + "\tOperations: " + totalOps);
                    Metrics.getInstance().printReport(System.out);
                }
                prevTotalOps = totalOps;
                try {
                    sleep(intervalSec * Time.MS_PER_SECOND);
                } catch (InterruptedException e) {
                }
            } while (!testComplete);
        }
    }

    class ClientThread extends Thread {

        private VoldemortWrapper db;
        private boolean runBenchmark;
        private boolean isVerbose;
        private BenchmarkWorkload clientWorkLoad;
        private int operationsCount;
        private double targetThroughputPerMs;
        private int opsDone;
        private final WorkloadPlugin plugin;

        public ClientThread(VoldemortWrapper db, boolean runBenchmark, BenchmarkWorkload workLoad,
                int operationsCount, double targetThroughputPerMs, boolean isVerbose, WorkloadPlugin plugin) {
            this.db = db;
            this.runBenchmark = runBenchmark;
            this.clientWorkLoad = workLoad;
            this.operationsCount = operationsCount;
            this.opsDone = 0;
            this.targetThroughputPerMs = targetThroughputPerMs;
            this.isVerbose = isVerbose;
            this.plugin = plugin;
        }

        public int getOpsDone() {
            return this.opsDone;
        }

        @Override
        public void run() {
            long startTime = System.currentTimeMillis();
            while (opsDone < this.operationsCount) {
                try {
                    if (runBenchmark) {
                        if (!clientWorkLoad.doTransaction(this.db, plugin)) {
                            break;
                        }
                    } else {
                        if (!clientWorkLoad.doWrite(this.db, plugin)) {
                            break;
                        }
                    }
                } catch (Exception e) {
                    if (this.isVerbose)
                        e.printStackTrace();
                }
                opsDone++;

                if (targetThroughputPerMs > 0) {
                    double timePerOp = ((double) opsDone) / targetThroughputPerMs;
                    while (System.currentTimeMillis() - startTime < timePerOp) {
                        try {
                            sleep(1);
                        } catch (InterruptedException e) {
                        }
                    }
                }
            }
        }
    }

    private StoreDefinition getStoreDefinition(AbstractStoreClientFactory factory, String storeName) {
        String storesXml = factory.bootstrapMetadataWithRetries(MetadataStore.STORES_KEY);
        StoreDefinitionsMapper storeMapper = new StoreDefinitionsMapper();
        List<StoreDefinition> storeDefinitionList = storeMapper.readStoreList(new StringReader(storesXml));

        StoreDefinition storeDef = null;
        for (StoreDefinition storeDefinition : storeDefinitionList) {
            if (storeName.equals(storeDefinition.getName())) {
                storeDef = storeDefinition;
            }
        }
        return storeDef;
    }

    private BenchmarkWorkload getWorkloadInstance(String workloadType) throws Exception {
        if (TRACE_WORKLOAD_TYPE.equals(workloadType)) {
            return new TraceBasedWorkload();
        } else if (DEFAULT_WORKLOAD_TYPE.equals(workloadType)) {
            return new DefaultWorkload();
        }
        throw new Exception("Can't determine for workloadType = " + workloadType);
    }

    public String findKeyType(StoreDefinition storeDefinition) throws Exception {
        SerializerDefinition serializerDefinition = storeDefinition.getKeySerializer();
        if (serializerDefinition != null) {
            if ("string".equals(serializerDefinition.getName())) {
                return Benchmark.STRING_KEY_TYPE;
            } else if ("json".equals(serializerDefinition.getName())) {
                if (serializerDefinition.getCurrentSchemaInfo().contains("int")) {
                    return Benchmark.JSONINT_KEY_TYPE;
                } else if (serializerDefinition.getCurrentSchemaInfo().contains("string")) {
                    return Benchmark.JSONSTRING_KEY_TYPE;
                }
            } else if ("identity".equals(serializerDefinition.getName())) {
                return Benchmark.IDENTITY_KEY_TYPE;
            }
        }

        throw new Exception("Can't determine key type for key serializer " + storeDefinition.getName());
    }

    public Serializer<?> findKeyType(String keyType) throws Exception {
        if (keyType.compareTo(STRING_KEY_TYPE) == 0) {
            return new StringSerializer();
        } else if (keyType.compareTo(JSONINT_KEY_TYPE) == 0) {
            return new JsonTypeSerializer("\"int32\"");
        } else if (keyType.compareTo(JSONSTRING_KEY_TYPE) == 0) {
            return new JsonTypeSerializer("\"string\"");
        } else if (keyType.compareTo(IDENTITY_KEY_TYPE) == 0) {
            return new IdentitySerializer();
        }
        throw new Exception("Can't determine for keyType = " + keyType);
    }

    public void initializeWorkload(Props workloadProps) throws Exception {

        if (!this.storeInitialized) {
            throw new VoldemortException("Store not initialized correctly");
        }

        // Calculate perThreadThroughputPerMs = default unlimited (-1)
        this.targetThroughput = workloadProps.getInt(TARGET_THROUGHPUT, -1);
        this.perThreadThroughputPerMs = -1;
        if (targetThroughput > 0) {
            double targetPerThread = ((double) targetThroughput) / ((double) numThreads);
            this.perThreadThroughputPerMs = targetPerThread / 1000.0;
        }

        if (workloadProps.containsKey(OPS_COUNT)) {
            this.opsCount = workloadProps.getInt(OPS_COUNT);
        } else {
            throw new VoldemortException("Missing compulsory parameters - " + OPS_COUNT);
        }
        this.recordCount = workloadProps.getInt(RECORD_COUNT, -1);
        this.pluginName = workloadProps.getString(PLUGIN_CLASS, null);

        // Initialize measurement
        Metrics.setProperties(workloadProps);
        Metrics.getInstance().reset();

        // Initialize workload
        String workloadType = workloadProps.getString(WORKLOAD_TYPE, DEFAULT_WORKLOAD_TYPE);
        this.workLoad = getWorkloadInstance(workloadType);
        this.workLoad.init(workloadProps);
        this.workLoad.loadSampleValues(storeClient);
        System.out.println(String.format("INFO: %s is used.", this.workLoad.getClass().getName()));
    }

    @SuppressWarnings("unchecked")
    public void initializeStore(Props benchmarkProps) throws Exception {

        this.numThreads = benchmarkProps.getInt(THREADS, MAX_WORKERS);
        this.numConnectionsPerNode = benchmarkProps.getInt(NUM_CONNECTIONS_PER_NODE, MAX_CONNECTIONS_PER_NODE);
        this.numIterations = benchmarkProps.getInt(ITERATIONS, 1);
        this.statusIntervalSec = benchmarkProps.getInt(INTERVAL, 0);
        this.verbose = benchmarkProps.getBoolean(VERBOSE, false);
        this.verifyRead = benchmarkProps.getBoolean(VERIFY, false);
        this.ignoreNulls = benchmarkProps.getBoolean(IGNORE_NULLS, false);
        int clientZoneId = benchmarkProps.getInt(CLIENT_ZONE_ID, -1);

        if (benchmarkProps.containsKey(URL)) {

            // Remote benchmark
            if (!benchmarkProps.containsKey(STORE_NAME)) {
                throw new VoldemortException("Missing storename");
            }

            String socketUrl = benchmarkProps.getString(URL);
            String storeName = benchmarkProps.getString(STORE_NAME);

            ClientConfig clientConfig = new ClientConfig().setMaxThreads(numThreads)
                    .setMaxTotalConnections(numThreads).setMaxConnectionsPerNode(numConnectionsPerNode)
                    .setRoutingTimeout(1500, TimeUnit.MILLISECONDS).setSocketTimeout(1500, TimeUnit.MILLISECONDS)
                    .setConnectionTimeout(500, TimeUnit.MILLISECONDS)
                    .setRequestFormatType(RequestFormatType.VOLDEMORT_V3).setBootstrapUrls(socketUrl);
            // .enableDefaultClient(true);

            if (clientZoneId >= 0) {
                clientConfig.setClientZoneId(clientZoneId);
            }
            SocketStoreClientFactory socketFactory = new SocketStoreClientFactory(clientConfig);
            this.storeClient = socketFactory.getStoreClient(storeName);
            StoreDefinition storeDef = getStoreDefinition(socketFactory, storeName);
            this.keyType = findKeyType(storeDef);
            benchmarkProps.put(Benchmark.KEY_TYPE, this.keyType);
            this.factory = socketFactory;

        } else {

            // Local benchmark
            localMode = true;
            String storageEngineClass = benchmarkProps.getString(STORAGE_CONFIGURATION_CLASS);
            this.keyType = benchmarkProps.getString(KEY_TYPE, STRING_KEY_TYPE);
            Serializer serializer = findKeyType(this.keyType);
            Store<Object, Object, Object> store = null;

            VoldemortConfig voldemortConfig;
            if (benchmarkProps.containsKey(LOCAL_SERVER_PROPERTIES)) {
                File homeDir = TestUtils.createTempDir();
                File configDir = new File(homeDir, "config");
                configDir.mkdir();
                FileUtils.copyFile(new File(benchmarkProps.get(LOCAL_SERVER_PROPERTIES)),
                        new File(configDir, "server.properties"));
                voldemortConfig = VoldemortConfig.loadFromVoldemortHome(homeDir.getAbsolutePath());
            } else {
                voldemortConfig = ServerTestUtils.getVoldemortConfig();
            }

            StorageConfiguration conf = (StorageConfiguration) ReflectUtils
                    .callConstructor(ReflectUtils.loadClass(storageEngineClass), new Object[] { voldemortConfig });

            StorageEngine<ByteArray, byte[], byte[]> engine = conf.getStore(TestUtils.makeStoreDefinition(DUMMY_DB),
                    TestUtils.makeSingleNodeRoutingStrategy());
            if (conf.getType().compareTo(ViewStorageConfiguration.TYPE_NAME) == 0) {
                engine = new ViewStorageEngine(STORE_NAME, engine, new StringSerializer(), new StringSerializer(),
                        serializer, new StringSerializer(), null,
                        BenchmarkViews.loadTransformation(benchmarkProps.getString(VIEW_CLASS).trim()));
            }
            store = SerializingStore.wrap(engine, serializer, new StringSerializer(), new IdentitySerializer());

            this.factory = new StaticStoreClientFactory(store);
            this.storeClient = factory.getStoreClient(store.getName());
        }

        this.storeInitialized = true;
    }

    public void initialize(Props benchmarkProps) throws Exception {
        if (!this.storeInitialized) {
            initializeStore(benchmarkProps);
        }
        initializeWorkload(benchmarkProps);
    }

    public void warmUpAndRun() throws Exception {
        if (this.recordCount > 0) {
            System.out.println("Running warmup");
            runTests(false);
            this.warmUpCompleted = true;
            Metrics.getInstance().reset();
        }

        for (int index = 0; index < this.numIterations; index++) {
            System.out.println(
                    "======================= iteration = " + index + " ======================================");
            runTests(true);
            Metrics.getInstance().reset();
        }

    }

    @SuppressWarnings("cast")
    public long runTests(boolean runBenchmark) throws Exception {

        int localOpsCounts = 0;
        String label = null;
        if (runBenchmark) {
            localOpsCounts = this.opsCount;
            label = new String("benchmark");
        } else {
            localOpsCounts = this.recordCount;
            label = new String("warmup");
        }
        Vector<Thread> threads = new Vector<Thread>();

        for (int index = 0; index < this.numThreads; index++) {
            VoldemortWrapper db = new VoldemortWrapper(storeClient, this.verifyRead && this.warmUpCompleted,
                    this.ignoreNulls, this.localMode);
            WorkloadPlugin plugin = null;
            if (this.pluginName != null && this.pluginName.length() > 0) {
                Class<?> cls = Class.forName(this.pluginName);
                try {
                    plugin = (WorkloadPlugin) cls.newInstance();
                } catch (IllegalAccessException e) {
                    System.err.println("Class not accessible ");
                    System.exit(1);
                } catch (InstantiationException e) {
                    System.err.println("Class not instantiable.");
                    System.exit(1);
                }
                plugin.setDb(db);
            }

            int opsPerThread = localOpsCounts / this.numThreads;
            // Make the last thread handle the remainder.
            if (index == this.numThreads - 1) {
                opsPerThread += localOpsCounts % this.numThreads;
            }

            threads.add(new ClientThread(db, runBenchmark, this.workLoad, opsPerThread,
                    this.perThreadThroughputPerMs, this.verbose, plugin));
        }

        long startRunBenchmark = System.currentTimeMillis();
        for (Thread currentThread : threads) {
            currentThread.start();
        }

        StatusThread statusThread = null;
        if (this.statusIntervalSec > 0) {
            statusThread = new StatusThread(threads, this.statusIntervalSec, startRunBenchmark);
            statusThread.start();
        }

        for (Thread currentThread : threads) {
            try {
                currentThread.join();
            } catch (InterruptedException e) {
                if (this.verbose)
                    e.printStackTrace();
            }
        }
        long endRunBenchmark = System.currentTimeMillis();

        if (this.statusIntervalSec > 0) {
            statusThread.interrupt();
        }

        // Print the output
        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(4);
        nf.setGroupingUsed(false);

        System.out.println("[" + label + "]\tRunTime(ms): " + nf.format((endRunBenchmark - startRunBenchmark)));
        double throughput = Time.MS_PER_SECOND * ((double) localOpsCounts)
                / ((double) (endRunBenchmark - startRunBenchmark));
        System.out.println("[" + label + "]\tThroughput(ops/sec): " + nf.format(throughput));

        if (runBenchmark) {
            Metrics.getInstance().printReport(System.out);
        }
        return (endRunBenchmark - startRunBenchmark);
    }

    public static void main(String args[]) throws IOException {
        // Logger.getRootLogger().removeAllAppenders();
        OptionParser parser = new OptionParser();
        parser.accepts(READS, "percentage of --ops-count to be reads; valid values [0-100]").withRequiredArg()
                .describedAs("read-percent").ofType(Integer.class);
        parser.accepts(WRITES, "percentage of --ops-count to be writes; valid values [0-100]").withRequiredArg()
                .describedAs("write-percent").ofType(Integer.class);
        parser.accepts(DELETES, "percentage of --ops-count to be deletes; valid values [0-100]").withRequiredArg()
                .describedAs("delete-percent").ofType(Integer.class);
        parser.accepts(MIXED, "percentage of --ops-count to be updates; valid values [0-100]").withRequiredArg()
                .describedAs("update-percent").ofType(Integer.class);
        parser.accepts(SAMPLE_SIZE,
                "number of value samples to be obtained from the store for replay based on keys from request-file; 0 means no sample value replay. Default = 0")
                .withRequiredArg().describedAs("sample-size").ofType(Integer.class);
        parser.accepts(VERBOSE, "verbose");
        parser.accepts(THREADS, "max number concurrent worker threads; Default = " + MAX_WORKERS).withRequiredArg()
                .describedAs("num-threads").ofType(Integer.class);
        parser.accepts(NUM_CONNECTIONS_PER_NODE,
                "max number of connections to any node; Default = " + MAX_CONNECTIONS_PER_NODE).withRequiredArg()
                .describedAs("num-connections-per-node").ofType(Integer.class);
        parser.accepts(ITERATIONS, "number of times to repeat benchmark phase; Default = 1").withRequiredArg()
                .describedAs("num-iter").ofType(Integer.class);
        parser.accepts(VERIFY, "verify values read; runs only if warm-up phase is included");
        parser.accepts(PERCENT_CACHED,
                "percentage of requests to come from previously requested keys; valid values are in range [0..100]; 0 means caching disabled. Default = 0")
                .withRequiredArg().describedAs("percent").ofType(Integer.class);
        parser.accepts(START_KEY_INDEX, "key index to start warm-up phase from; Default = 0").withRequiredArg()
                .describedAs("index").ofType(Integer.class);
        parser.accepts(INTERVAL, "print status at interval seconds; Default = 0").withRequiredArg()
                .describedAs("sec").ofType(Integer.class);
        parser.accepts(IGNORE_NULLS, "ignore null values in results");
        parser.accepts(PROP_FILE,
                "file containing all the properties in key=value format; will override all other command line options specified")
                .withRequiredArg().describedAs("prop-file");
        parser.accepts(STORAGE_CONFIGURATION_CLASS,
                "class of the storage engine configuration to use [e.g. voldemort.store.bdb.BdbStorageConfiguration]")
                .withRequiredArg().describedAs("class-name");
        parser.accepts(KEY_TYPE,
                "for local tests; key type to support; [ " + IDENTITY_KEY_TYPE + " | " + JSONINT_KEY_TYPE + " | "
                        + JSONSTRING_KEY_TYPE + "|" + STRING_KEY_TYPE + " <default> ]")
                .withRequiredArg().describedAs("type");
        parser.accepts(REQUEST_FILE,
                "file with limited list of keys to be used during benchmark phase; Overrides " + RECORD_SELECTION)
                .withRequiredArg();
        parser.accepts(VALUE_SIZE,
                "size in bytes for random value; used during warm-up phase and write operation of benchmark phase; Default = 1024")
                .withRequiredArg().describedAs("bytes").ofType(Integer.class);
        parser.accepts(RECORD_SELECTION, "record selection distribution [ " + ZIPFIAN_RECORD_SELECTION + " | "
                + LATEST_RECORD_SELECTION + " | " + UNIFORM_RECORD_SELECTION + " <default> ]").withRequiredArg();
        parser.accepts(TARGET_THROUGHPUT, "fix throughput").withRequiredArg().describedAs("ops/sec")
                .ofType(Integer.class);
        parser.accepts(RECORD_COUNT, "number of records inserted during warmup phase").withRequiredArg()
                .describedAs("count").ofType(Integer.class);
        parser.accepts(OPS_COUNT, "number of operations to do during benchmark phase").withRequiredArg()
                .describedAs("count").ofType(Integer.class);
        parser.accepts(URL, "for remote tests; url of remote server").withRequiredArg();
        parser.accepts(STORE_NAME, "for remote tests; store name on the remote " + URL).withRequiredArg()
                .describedAs("name");
        parser.accepts(METRIC_TYPE,
                "type of result metric [ " + HISTOGRAM_METRIC_TYPE + " | " + SUMMARY_METRIC_TYPE + " <default> ]")
                .withRequiredArg();
        parser.accepts(PLUGIN_CLASS,
                "classname of implementation of WorkloadPlugin; used to run customized operations ")
                .withRequiredArg().describedAs("class-name");
        parser.accepts(CLIENT_ZONE_ID, "zone id for client; enables zone routing").withRequiredArg()
                .describedAs("zone-id").ofType(Integer.class);
        parser.accepts(LOCAL_SERVER_PROPERTIES, "path to a server properties file (local mode only)")
                .withRequiredArg().describedAs(LOCAL_SERVER_PROPERTIES).ofType(String.class);
        parser.accepts(WORKLOAD_TYPE, "workload type; type to support; [ " + TRACE_WORKLOAD_TYPE + " | "
                + DEFAULT_WORKLOAD_TYPE + " <default> ]").withRequiredArg().describedAs(WORKLOAD_TYPE)
                .ofType(String.class);
        parser.accepts(KEY_VALUE_FILE, "path to a file with keys and value sizes").withRequiredArg()
                .describedAs(KEY_VALUE_FILE).ofType(String.class);
        parser.accepts(KEY_SEQUENCE_FILE, "path to a file with key access sequence").withRequiredArg()
                .describedAs(KEY_SEQUENCE_FILE).ofType(String.class);
        parser.accepts(HELP);

        OptionSet options = parser.parse(args);

        if (options.has(HELP)) {
            parser.printHelpOn(System.out);
            System.exit(0);
        }

        Props mainProps = null;
        if (options.has(PROP_FILE)) {
            String propFileDestination = (String) options.valueOf(PROP_FILE);
            File propertyFile = new File(propFileDestination);
            if (!propertyFile.exists()) {
                printUsage(parser, "Property file does not exist");
            }

            try {
                mainProps = new Props(propertyFile);
            } catch (Exception e) {
                printUsage(parser, "Unable to parse the property file");
            }
        } else {
            mainProps = new Props();

            if (options.has(REQUEST_FILE)) {
                mainProps.put(REQUEST_FILE, (String) options.valueOf(REQUEST_FILE));
                mainProps.put(RECORD_SELECTION, FILE_RECORD_SELECTION);
            } else {
                mainProps.put(RECORD_SELECTION,
                        CmdUtils.valueOf(options, RECORD_SELECTION, UNIFORM_RECORD_SELECTION));
            }

            if (options.has(RECORD_COUNT)) {
                mainProps.put(RECORD_COUNT, (Integer) options.valueOf(RECORD_COUNT));
            } else {
                mainProps.put(RECORD_COUNT, 0);
            }

            if (!options.has(OPS_COUNT)) {
                printUsage(parser, "Missing " + OPS_COUNT);
            }
            mainProps.put(OPS_COUNT, (Integer) options.valueOf(OPS_COUNT));

            if (options.has(URL)) {
                mainProps.put(URL, (String) options.valueOf(URL));
                if (options.has(STORE_NAME)) {
                    mainProps.put(STORE_NAME, (String) options.valueOf(STORE_NAME));
                } else {
                    printUsage(parser, "Missing store name");
                }
            } else {
                mainProps.put(KEY_TYPE, CmdUtils.valueOf(options, KEY_TYPE, STRING_KEY_TYPE));
                mainProps.put(STORAGE_CONFIGURATION_CLASS, CmdUtils.valueOf(options, STORAGE_CONFIGURATION_CLASS,
                        BdbStorageConfiguration.class.getName()));
            }

            if (options.has(LOCAL_SERVER_PROPERTIES)) {
                mainProps.put(LOCAL_SERVER_PROPERTIES, (String) options.valueOf(LOCAL_SERVER_PROPERTIES));
            }

            if (options.has(WORKLOAD_TYPE)) {
                mainProps.put(WORKLOAD_TYPE, (String) options.valueOf(WORKLOAD_TYPE));
                if (((String) options.valueOf(WORKLOAD_TYPE)).equals(TRACE_WORKLOAD_TYPE)) {
                    CmdUtils.croakIfMissing(parser, options, KEY_VALUE_FILE, KEY_SEQUENCE_FILE);
                    mainProps.put(KEY_VALUE_FILE, (String) options.valueOf(KEY_VALUE_FILE));
                    mainProps.put(KEY_SEQUENCE_FILE, (String) options.valueOf(KEY_SEQUENCE_FILE));
                }
            }

            mainProps.put(VERBOSE, getCmdBoolean(options, VERBOSE));
            mainProps.put(VERIFY, getCmdBoolean(options, VERIFY));
            mainProps.put(IGNORE_NULLS, getCmdBoolean(options, IGNORE_NULLS));
            mainProps.put(CLIENT_ZONE_ID, CmdUtils.valueOf(options, CLIENT_ZONE_ID, -1));
            mainProps.put(START_KEY_INDEX, CmdUtils.valueOf(options, START_KEY_INDEX, 0));
            mainProps.put(VALUE_SIZE, CmdUtils.valueOf(options, VALUE_SIZE, 1024));
            mainProps.put(ITERATIONS, CmdUtils.valueOf(options, ITERATIONS, 1));
            mainProps.put(THREADS, CmdUtils.valueOf(options, THREADS, MAX_WORKERS));
            mainProps.put(NUM_CONNECTIONS_PER_NODE,
                    CmdUtils.valueOf(options, NUM_CONNECTIONS_PER_NODE, MAX_CONNECTIONS_PER_NODE));
            mainProps.put(PERCENT_CACHED, CmdUtils.valueOf(options, PERCENT_CACHED, 0));
            mainProps.put(INTERVAL, CmdUtils.valueOf(options, INTERVAL, 0));
            mainProps.put(TARGET_THROUGHPUT, CmdUtils.valueOf(options, TARGET_THROUGHPUT, -1));
            mainProps.put(METRIC_TYPE, CmdUtils.valueOf(options, METRIC_TYPE, SUMMARY_METRIC_TYPE));
            mainProps.put(READS, CmdUtils.valueOf(options, READS, 0));
            mainProps.put(WRITES, CmdUtils.valueOf(options, WRITES, 0));
            mainProps.put(DELETES, CmdUtils.valueOf(options, DELETES, 0));
            mainProps.put(MIXED, CmdUtils.valueOf(options, MIXED, 0));
            mainProps.put(PLUGIN_CLASS, CmdUtils.valueOf(options, PLUGIN_CLASS, ""));
            mainProps.put(SAMPLE_SIZE, CmdUtils.valueOf(options, SAMPLE_SIZE, 0));
        }

        // Start the benchmark
        Benchmark benchmark = null;
        try {
            benchmark = new Benchmark();
            benchmark.initialize(mainProps);
            benchmark.warmUpAndRun();
            benchmark.close();
        } catch (Exception e) {
            if (options.has(VERBOSE)) {
                e.printStackTrace();
            }
            parser.printHelpOn(System.err);
            System.exit(-1);
        }
    }

    public void close() {
        this.factory.close();
    }

    private static void printUsage(OptionParser parser, String errorCommand) throws IOException {
        parser.printHelpOn(System.err);
        Utils.croak("Usage: $VOLDEMORT_HOME/bin/run-class.sh " + Benchmark.class.getName() + " [options]\n "
                + errorCommand);
    }

    private static String getCmdBoolean(OptionSet option, String command) {
        if (option.has(command)) {
            return "true";
        } else {
            return "false";
        }
    }
}