You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by mb...@apache.org on 2012/10/09 14:52:14 UTC

svn commit: r1396006 - in /hbase/branches/0.89-fb: conf/ src/main/java/org/apache/hadoop/hbase/util/ src/test/java/org/apache/hadoop/hbase/benchmarks/ src/test/java/org/apache/hadoop/hbase/util/

Author: mbautin
Date: Tue Oct  9 12:52:13 2012
New Revision: 1396006

URL: http://svn.apache.org/viewvc?rev=1396006&view=rev
Log:
[HBASE-6923] Create scanner benchmark

Author: kranganathan

Summary: This diff creates a generic benchmarking framework and implements a scan benchmark from memory for HBase

Test Plan:
Tested by running benchmark by running:
bin/hbase org.apache.hadoop.hbase.benchmarks.ScanBenchmark

Reviewers: kannan, aaiyer, liyintang

Reviewed By: kannan

CC: hbase-eng@

Differential Revision: https://phabricator.fb.com/D594846

Added:
    hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/
    hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/Benchmark.java
    hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/BenchmarkResults.java
    hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/ScanBenchmark.java
Modified:
    hbase/branches/0.89-fb/conf/hbase-env.sh
    hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/AbstractHBaseTool.java
    hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/LoadTestKVGenerator.java
    hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/LoadTestTool.java

Modified: hbase/branches/0.89-fb/conf/hbase-env.sh
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/conf/hbase-env.sh?rev=1396006&r1=1396005&r2=1396006&view=diff
==============================================================================
--- hbase/branches/0.89-fb/conf/hbase-env.sh (original)
+++ hbase/branches/0.89-fb/conf/hbase-env.sh Tue Oct  9 12:52:13 2012
@@ -23,6 +23,7 @@
 
 # The java implementation to use.  Java 1.6 required.
 # export JAVA_HOME=/usr/java/jdk1.6.0/
+export JAVA_HOME=/usr/local/jdk-6u14-64
 
 # Extra Java CLASSPATH elements.  Optional.
 # export HBASE_CLASSPATH=

Modified: hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/AbstractHBaseTool.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/AbstractHBaseTool.java?rev=1396006&r1=1396005&r2=1396006&view=diff
==============================================================================
--- hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/AbstractHBaseTool.java (original)
+++ hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/AbstractHBaseTool.java Tue Oct  9 12:52:13 2012
@@ -167,7 +167,7 @@ public abstract class AbstractHBaseTool 
   }
 
   /** Call this from the concrete tool class's main function. */
-  protected void doStaticMain(String args[]) {
+  protected int doStaticMain(String args[]) {
     int ret;
     try {
       ret = ToolRunner.run(HBaseConfiguration.create(), this, args);
@@ -175,7 +175,7 @@ public abstract class AbstractHBaseTool 
       LOG.error("Error running command-line tool", ex);
       ret = EXIT_FAILURE;
     }
-    System.exit(ret);
+    return ret;
   }
 
 }

Added: hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/Benchmark.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/Benchmark.java?rev=1396006&view=auto
==============================================================================
--- hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/Benchmark.java (added)
+++ hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/Benchmark.java Tue Oct  9 12:52:13 2012
@@ -0,0 +1,127 @@
+package org.apache.hadoop.hbase.benchmarks;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.loadtest.ColumnFamilyProperties;
+import org.apache.hadoop.hbase.loadtest.HBaseUtils;
+import org.apache.hadoop.hbase.util.LoadTestTool;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+public abstract class Benchmark {
+  public static final Log LOG = LogFactory.getLog(Benchmark.class);
+  public static final String ARG_ZOOKEEPER = "--zookeeper";
+  // use local zookeeper by default
+  public String zkNodeName = null;
+  // cached config object
+  public Configuration conf;
+  // benchmark results abstraction
+  public BenchmarkResults benchmarkResults;
+
+  /**
+   * Initialize the benchmark results structure "benchmarkResults"
+   */
+  public abstract void initBenchmarkResults();
+  
+  /**
+   * Run the actual benchmark
+   * @throws Throwable
+   */
+  public abstract void runBenchmark() throws Throwable;
+  
+  /**
+   * Parse the args to the benchmark
+   * @param args
+   */
+  public void parseArgs(String[] args) {
+    for (int i = 0; i < args.length; i++) {
+      if (args[i].equals(ARG_ZOOKEEPER)) {
+        zkNodeName = args[i+1];
+        i++;
+      }
+    }
+  }
+  
+  public void initialize() {
+    conf = HBaseConfiguration.create();
+    if (zkNodeName != null) {
+      conf.set("hbase.zookeeper.quorum", zkNodeName);
+      conf.setInt("hbase.zookeeper.property.clientPort", 2181);
+    }
+  }
+  
+  public void printBenchmarkResults() {
+    System.out.println("Benchmark results");
+    benchmarkResults.prettyPrint();
+  }
+
+  /**
+   * Helper function to create a table and load the requested number of KVs 
+   * into the table - if the table does not exist. If the table exists, then 
+   * nothing is done.
+   * @throws IOException 
+   */
+  public HTable createTableAndLoadData(byte[] tableName, int kvSize, 
+      long numKVs) throws IOException {
+    HTable htable = null;
+    try {
+      htable = new HTable(conf, tableName);
+    } catch (IOException e) {
+      LOG.info("Table " + new String(tableName) + " already exists.");
+    }
+    
+    if (htable != null) return htable;
+
+    ColumnFamilyProperties familyProperty = new ColumnFamilyProperties();
+    familyProperty.familyName = "cf1";
+    familyProperty.minColsPerKey = 1;
+    familyProperty.maxColsPerKey = 1;    
+    familyProperty.minColDataSize = kvSize;
+    familyProperty.maxColDataSize = kvSize;
+    familyProperty.maxVersions = 1;
+    familyProperty.compressionType = "none";
+    
+    ColumnFamilyProperties[] familyProperties = new ColumnFamilyProperties[1];
+    familyProperties[0] = familyProperty;
+    
+    // create the table
+    HBaseUtils.createTableIfNotExists(conf, tableName, familyProperties, 1);
+    // write data if the table was created
+    LOG.info("Loading data for the table");
+    String[] loadTestToolArgs = {
+      "-zk", "localhost", 
+      "-tn", "bench.ScanFromMemoryPerf",
+      "-cf", familyProperty.familyName,
+      "-write", "1:50", 
+      "-num_keys", "" + numKVs, 
+      "-multiput",
+      "-compression", "NONE",
+    };
+    LoadTestTool.doMain(loadTestToolArgs);
+    LOG.info("Done loading data");
+    
+    htable = new HTable(conf, tableName);
+    return htable;
+  }
+  
+  public static void benchmarkRunner(
+      Class<? extends Benchmark> benchmarkClass, String[] args) 
+  throws Throwable {
+    // suppress unnecessary logging
+    Logger.getLogger("org.apache.zookeeper").setLevel(Level.ERROR);
+    Logger.getLogger("org.apache.hadoop.hbase.client").setLevel(Level.ERROR);
+    Logger.getLogger("org.apache.hadoop.hbase.zookeeper").setLevel(Level.ERROR);
+    
+    Benchmark benchmark = benchmarkClass.newInstance();    
+    benchmark.parseArgs(args);
+    benchmark.initialize();
+    benchmark.initBenchmarkResults();
+    benchmark.runBenchmark();
+    benchmark.printBenchmarkResults();    
+  }
+}

Added: hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/BenchmarkResults.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/BenchmarkResults.java?rev=1396006&view=auto
==============================================================================
--- hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/BenchmarkResults.java (added)
+++ hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/BenchmarkResults.java Tue Oct  9 12:52:13 2012
@@ -0,0 +1,71 @@
+package org.apache.hadoop.hbase.benchmarks;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * General purpose utility class that tracks the results of a benchmark 
+ * experiment. Abstracts the X-axis and Y-axis of the table, and the 
+ * result object.
+ * 
+ * @param <DimensionX> X axis of the result
+ * @param <DimensionY> Y axis of the result
+ * @param <Result> Result object
+ */
+public class BenchmarkResults<DimensionX, DimensionY, Result> {
+  String xFormat;
+  String resultFormat;
+  DimensionX[] xValues;
+  DimensionY[] yValues;
+  List<String> headerEntries;
+  private Map<DimensionX, Map<DimensionY, Result>> results = 
+    new HashMap<DimensionX, Map<DimensionY, Result>>();
+  
+  public BenchmarkResults(DimensionX[] xValues, DimensionY[] yValues, 
+      String xFormat, String resultFormat, List<String> headerEntries) {
+    this.xValues = xValues;
+    this.yValues = yValues;
+    this.xFormat = xFormat;
+    this.resultFormat = resultFormat;
+    this.headerEntries = headerEntries;
+  }
+  
+  /**
+   * Add a single result entry
+   * @param xValue
+   * @param yValue
+   * @param result
+   */
+  public void addResult(DimensionX xValue, DimensionY yValue, Result result) {
+    Map<DimensionY, Result> yValue_to_result_map = results.get(xValue);
+    if (yValue_to_result_map == null) {
+      yValue_to_result_map = new HashMap<DimensionY, Result>();
+    }
+    yValue_to_result_map.put(yValue, result);
+    results.put(xValue, yValue_to_result_map);
+  }
+  
+  /**
+   * Pretty print the results to STDOUT
+   */
+  public void prettyPrint() {
+    // print the headers
+    for (String s : headerEntries) {
+      System.out.printf("%s\t", s);
+    }
+    System.out.println("");
+    // print all the values in the results
+    for (DimensionX x : xValues) {
+      System.out.printf(xFormat, x);
+      Map<DimensionY, Result> yValue_to_result_map = results.get(x);
+      for (DimensionY y : yValues) {
+        System.out.printf("\t" + resultFormat, yValue_to_result_map.get(y));
+      }
+      // row done
+      System.out.println("");
+    }
+    // done with the results
+    System.out.println("\n");
+  }
+}

Added: hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/ScanBenchmark.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/ScanBenchmark.java?rev=1396006&view=auto
==============================================================================
--- hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/ScanBenchmark.java (added)
+++ hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/benchmarks/ScanBenchmark.java Tue Oct  9 12:52:13 2012
@@ -0,0 +1,128 @@
+package org.apache.hadoop.hbase.benchmarks;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.TableExistsException;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.ResultScanner;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.loadtest.ColumnFamilyProperties;
+import org.apache.hadoop.hbase.loadtest.HBaseUtils;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.LoadTestTool;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+/**
+ * This test compares the performance of scan when all the data is in memory 
+ * (in the block cache). The setCaching parameter is varied and the read 
+ * throughput is printed for various values of setCaching.
+ */
+public class ScanBenchmark extends Benchmark {
+  public static final Log LOG = LogFactory.getLog(ScanBenchmark.class);
+  private static final long PRINT_INTERVAL_KVS = 1000000;
+  private byte[] tableName = Bytes.toBytes("bench.ScanFromMemoryPerf");
+  private static Integer[] SET_CACHING_VALUES = { 
+//    1000,  2000,  3000,  4000,
+//    5000,  6000,  7000,  8000, 
+    9000,  10000, 11000, 12000, 
+    13000, 14000, 
+  };
+  private static Integer[] SET_PREFETCH_VALUES = { 0 };  
+  
+  public void initBenchmarkResults() {
+    List<String> header = new ArrayList<String>();
+    header.add("caching");
+    for (int i = 0; i < SET_PREFETCH_VALUES.length; i++) {
+      header.add("prefetch=" +  SET_PREFETCH_VALUES[i]);
+    }
+    benchmarkResults = new BenchmarkResults<Integer, Integer, Double>(
+        SET_CACHING_VALUES, SET_PREFETCH_VALUES, "  %5d", "   %3.2f", header);
+  }
+  
+  public void runBenchmark() throws Throwable {
+    // populate the table
+    createTableAndLoadData(tableName, 50, 1000000);
+    // warm block cache 
+    runExperiment(false, 10000, 0);  
+    for (int caching : SET_CACHING_VALUES) {  
+      for (int prefetch : SET_PREFETCH_VALUES) {
+        try { 
+          runExperiment(true, caching, prefetch); 
+        } catch (IOException e) { 
+          e.printStackTrace();  
+        }
+      }
+    }
+  }
+
+  public void runExperiment(boolean printStats, int caching, int prefetch) throws IOException {
+    Configuration conf = HBaseConfiguration.create();
+    HTable htable = HBaseUtils.getHTable(conf, tableName);
+
+    // create the scan object with the right params
+    Scan scan = new Scan();
+    scan.setMaxVersions(1);
+    // set caching
+    scan.setCaching(caching);
+    
+    long numKVs = 0;
+    long numBytes = 0;
+    Result kv;
+    long printAfterNumKVs = PRINT_INTERVAL_KVS;
+    long startTime = System.currentTimeMillis();
+    
+    // read all the KV's
+    ResultScanner scanner = htable.getScanner(scan);
+    while ( (kv = scanner.next()) != null) {
+      numKVs += kv.size();
+      if (kv.raw() != null) {
+        for (KeyValue k : kv.raw())
+          numBytes += k.getLength();
+      }
+
+      if (numKVs > printAfterNumKVs) {
+        printAfterNumKVs += PRINT_INTERVAL_KVS;
+        if (printStats) printStats(numKVs, numBytes, startTime, caching, prefetch);
+      }
+    }
+
+    if (printStats) printStats(numKVs, numBytes, startTime, caching, prefetch);
+    scanner.close();
+  }
+
+  private void printStats(long numKVs, long numBytes, long startTime, 
+      int caching, int prefetch) {
+    long t2 = System.currentTimeMillis();
+    double numBytesInMB = numBytes * 1.0 / (1024 * 1024);
+    double rate = numBytesInMB * (1000 * 1.0 / (t2 - startTime));
+    System.out.println("Scan: caching = " + caching +
+                     ", prefetch = " + prefetch +
+                     ", kvs = " + numKVs +
+                     ", bytes = " + String.format("%1$,.2f", numBytesInMB) + " MB" +
+                     ", time = " + (t2 - startTime) + " ms" +
+                     ", rate = " + String.format("%1$,.2f", rate) + "MB/s"
+                     );
+    benchmarkResults.addResult(caching, prefetch, rate);
+  }
+  
+  public static void main(String[] args) throws Throwable {
+    String className = 
+      Thread.currentThread().getStackTrace()[1].getClassName();
+    System.out.println("Running benchmark " + className);
+    @SuppressWarnings("unchecked")
+    Class<? extends Benchmark> benchmarkClass = 
+      (Class<? extends Benchmark>)Class.forName(className);
+    Benchmark.benchmarkRunner(benchmarkClass, args);
+  }
+}
\ No newline at end of file

Modified: hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/LoadTestKVGenerator.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/LoadTestKVGenerator.java?rev=1396006&r1=1396005&r2=1396006&view=diff
==============================================================================
--- hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/LoadTestKVGenerator.java (original)
+++ hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/LoadTestKVGenerator.java Tue Oct  9 12:52:13 2012
@@ -65,10 +65,11 @@ public class LoadTestKVGenerator {
    */
   public static String md5PrefixedKey(long key) {
     String stringKey = Long.toString(key);
-    String md5hash = MD5Hash.getMD5AsHex(Bytes.toBytes(stringKey));
-
+    // use a 4 byte md5 hash prefix (gives 65K regions)
+    String md5hash = 
+      MD5Hash.getMD5AsHex(Bytes.toBytes(stringKey)).substring(0, 4);
     // flip the key to randomize
-    return md5hash + "-" + stringKey;
+    return md5hash + ":" + stringKey;
   }
 
   /**
@@ -80,8 +81,11 @@ public class LoadTestKVGenerator {
    */
   public byte[] generateRandomSizeValue(long key, String qual) {
     String rowKey = md5PrefixedKey(key);
-    int dataSize = minValueSize + randomForValueSize.nextInt(
+    int dataSize = minValueSize;
+    if (minValueSize != maxValueSize) {
+      dataSize += randomForValueSize.nextInt(
         Math.abs(maxValueSize - minValueSize));
+    }
     return getValueForRowColumn(rowKey, qual, dataSize);
   }
 

Modified: hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/LoadTestTool.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/LoadTestTool.java?rev=1396006&r1=1396005&r2=1396006&view=diff
==============================================================================
--- hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/LoadTestTool.java (original)
+++ hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/util/LoadTestTool.java Tue Oct  9 12:52:13 2012
@@ -43,15 +43,15 @@ public class LoadTestTool extends Abstra
 
   /** Table name for the test */
   private byte[] tableName;
+  
+  /** cf name for the test */
+  private byte[] columnFamily;
 
   /** Table name to use of not overridden on the command line */
   private static final String DEFAULT_TABLE_NAME = "cluster_test";
 
-  /** Column family used by the test */
-  static byte[] COLUMN_FAMILY = Bytes.toBytes("test_cf");
-
-  /** Column families used by the test */
-  static final byte[][] COLUMN_FAMILIES = { COLUMN_FAMILY };
+  /** Default column family used by the test */
+  private static String DEFAULT_CF_NAME = "test_cf";
 
   /** The number of reader/writer threads if not specified */
   private static final int DEFAULT_NUM_THREADS = 20;
@@ -96,12 +96,16 @@ public class LoadTestTool extends Abstra
   private static final String OPT_READ = "read";
   private static final String OPT_START_KEY = "start_key";
   private static final String OPT_TABLE_NAME = "tn";
+  private static final String OPT_CF_NAME = "cf";
   private static final String OPT_ZK_QUORUM = "zk";
   private static final String OPT_PROFILING = "profiling";
   private static final String OPT_RPC_COMPRESSION = "rpc_compression";
 
   private static final long DEFAULT_START_KEY = 0;
 
+  /** Column families used by the test */
+  private byte[][] COLUMN_FAMILIES;
+
   /** This will be removed as we factor out the dependency on command line */
   private CommandLine cmd;
 
@@ -134,7 +138,12 @@ public class LoadTestTool extends Abstra
   private int maxReadErrors = MultiThreadedReader.DEFAULT_MAX_ERRORS;
   private int verifyPercent;
   private int profilePercent = 0;
-
+  
+  public LoadTestTool() {
+    if (columnFamily == null) columnFamily = Bytes.toBytes(DEFAULT_CF_NAME);
+    COLUMN_FAMILIES = new byte[][] { columnFamily };
+  }
+  
   private boolean isBatched;
 
   private int batchSize;
@@ -202,6 +211,7 @@ public class LoadTestTool extends Abstra
     addOptWithArg(OPT_ZK_QUORUM, "ZK quorum as comma-separated host names " +
         "without port numbers");
     addOptWithArg(OPT_TABLE_NAME, "The name of the table to read or write");
+    addOptWithArg(OPT_CF_NAME, "The column family to read or write");
     addOptWithArg(OPT_WRITE, OPT_USAGE_LOAD);
     addOptWithArg(OPT_BATCHED_WRITES, "Use batched writes (with WAL)");
     addOptWithArg(OPT_BATCHED_WRITES_CNT, "Size of a batch (if using batched writes)");
@@ -236,6 +246,8 @@ public class LoadTestTool extends Abstra
 
     tableName = Bytes.toBytes(cmd.getOptionValue(OPT_TABLE_NAME,
         DEFAULT_TABLE_NAME));
+    columnFamily = Bytes.toBytes(cmd.getOptionValue(OPT_CF_NAME,
+        DEFAULT_CF_NAME));
     startKey = parseLong(cmd.getOptionValue(OPT_START_KEY,
         String.valueOf(DEFAULT_START_KEY)), 0, Long.MAX_VALUE);
     long numKeys = parseLong(cmd.getOptionValue(OPT_NUM_KEYS), 1,
@@ -353,11 +365,11 @@ public class LoadTestTool extends Abstra
     }
 
     HBaseTestingUtility.createPreSplitLoadTestTable(conf, tableName,
-        COLUMN_FAMILY, compressAlgo, dataBlockEncodingAlgo);
+        columnFamily, compressAlgo, dataBlockEncodingAlgo);
     applyColumnFamilyOptions(tableName, COLUMN_FAMILIES);
 
     if (isWrite) {
-      writerThreads = new MultiThreadedWriter(conf, tableName, COLUMN_FAMILY, 
+      writerThreads = new MultiThreadedWriter(conf, tableName, columnFamily, 
           profilePercent, this.txCompression, this.rxCompression);
       writerThreads.setMultiPut(isMultiPut);
       writerThreads.setBatching(isBatched);
@@ -367,7 +379,7 @@ public class LoadTestTool extends Abstra
     }
 
     if (isRead) {
-      readerThreads = new MultiThreadedReader(conf, tableName, COLUMN_FAMILY,
+      readerThreads = new MultiThreadedReader(conf, tableName, columnFamily,
           verifyPercent, profilePercent, this.txCompression, this.rxCompression);
       readerThreads.setMaxErrors(maxReadErrors);
       readerThreads.setKeyWindow(keyWindow);
@@ -397,9 +409,14 @@ public class LoadTestTool extends Abstra
       readerThreads.waitForFinish();
     }
   }
+  
+  public static int doMain(String[] args) {
+    return new LoadTestTool().doStaticMain(args);
+  }
 
   public static void main(String[] args) {
-    new LoadTestTool().doStaticMain(args);
+    int ret = doMain(args);
+    System.exit(ret);
   }
 
 }