1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase;
20
21 import java.io.DataInput;
22 import java.io.DataOutput;
23 import java.io.IOException;
24 import java.io.PrintStream;
25 import java.math.BigDecimal;
26 import java.math.MathContext;
27 import java.text.DecimalFormat;
28 import java.text.SimpleDateFormat;
29 import java.util.ArrayList;
30 import java.util.Date;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Random;
34 import java.util.TreeMap;
35 import java.util.Arrays;
36 import java.util.regex.Matcher;
37 import java.util.regex.Pattern;
38 import java.lang.reflect.Constructor;
39
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42 import org.apache.hadoop.conf.Configuration;
43 import org.apache.hadoop.fs.FSDataInputStream;
44 import org.apache.hadoop.fs.FileStatus;
45 import org.apache.hadoop.fs.FileSystem;
46 import org.apache.hadoop.fs.Path;
47 import org.apache.hadoop.hbase.client.Get;
48 import org.apache.hadoop.hbase.client.HBaseAdmin;
49 import org.apache.hadoop.hbase.client.HConnection;
50 import org.apache.hadoop.hbase.client.HConnectionManager;
51 import org.apache.hadoop.hbase.client.HTableInterface;
52 import org.apache.hadoop.hbase.client.Put;
53 import org.apache.hadoop.hbase.client.Result;
54 import org.apache.hadoop.hbase.client.ResultScanner;
55 import org.apache.hadoop.hbase.client.Scan;
56 import org.apache.hadoop.hbase.client.Durability;
57 import org.apache.hadoop.hbase.filter.PageFilter;
58 import org.apache.hadoop.hbase.filter.WhileMatchFilter;
59 import org.apache.hadoop.hbase.filter.Filter;
60 import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
61 import org.apache.hadoop.hbase.filter.CompareFilter;
62 import org.apache.hadoop.hbase.filter.BinaryComparator;
63 import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
64 import org.apache.hadoop.hbase.io.compress.Compression;
65 import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
66 import org.apache.hadoop.hbase.util.Bytes;
67 import org.apache.hadoop.hbase.util.Hash;
68 import org.apache.hadoop.hbase.util.MurmurHash;
69 import org.apache.hadoop.hbase.util.Pair;
70 import org.apache.hadoop.conf.Configured;
71 import org.apache.hadoop.io.LongWritable;
72 import org.apache.hadoop.io.NullWritable;
73 import org.apache.hadoop.io.Text;
74 import org.apache.hadoop.io.Writable;
75 import org.apache.hadoop.mapreduce.InputSplit;
76 import org.apache.hadoop.mapreduce.Job;
77 import org.apache.hadoop.mapreduce.JobContext;
78 import org.apache.hadoop.mapreduce.Mapper;
79 import org.apache.hadoop.mapreduce.RecordReader;
80 import org.apache.hadoop.mapreduce.TaskAttemptContext;
81 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
82 import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
83 import org.apache.hadoop.mapreduce.lib.reduce.LongSumReducer;
84 import org.apache.hadoop.util.Tool;
85 import org.apache.hadoop.util.ToolRunner;
86 import org.apache.hadoop.util.LineReader;
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 public class PerformanceEvaluation extends Configured implements Tool {
105 protected static final Log LOG = LogFactory.getLog(PerformanceEvaluation.class.getName());
106
107 public static final TableName TABLE_NAME = TableName.valueOf("TestTable");
108 public static final byte[] FAMILY_NAME = Bytes.toBytes("info");
109 public static final byte[] QUALIFIER_NAME = Bytes.toBytes("data");
110 public static final int VALUE_LENGTH = 1000;
111 public static final int ROW_LENGTH = 26;
112
113 private static final int ONE_GB = 1024 * 1024 * 1000;
114 private static final int ROWS_PER_GB = ONE_GB / VALUE_LENGTH;
115 private static final DecimalFormat FMT = new DecimalFormat("0.##");
116 private static final MathContext CXT = MathContext.DECIMAL64;
117 private static final BigDecimal MS_PER_SEC = BigDecimal.valueOf(1000);
118 private static final BigDecimal BYTES_PER_MB = BigDecimal.valueOf(1024 * 1024);
119
120 protected HTableDescriptor TABLE_DESCRIPTOR;
121 protected Map<String, CmdDescriptor> commands = new TreeMap<String, CmdDescriptor>();
122
123 private boolean nomapred = false;
124 private int N = 1;
125 private int R = ROWS_PER_GB;
126 private float sampleRate = 1.0f;
127 private TableName tableName = TABLE_NAME;
128 private Compression.Algorithm compression = Compression.Algorithm.NONE;
129 private DataBlockEncoding blockEncoding = DataBlockEncoding.NONE;
130 private boolean flushCommits = true;
131 private boolean writeToWAL = true;
132 private boolean inMemoryCF = false;
133 private boolean reportLatency = false;
134 private int presplitRegions = 0;
135 private HConnection connection;
136
137 private static final Path PERF_EVAL_DIR = new Path("performance_evaluation");
138
139
140 public static final Pattern LINE_PATTERN =
141 Pattern.compile("tableName=(\\w+),\\s+" +
142 "startRow=(\\d+),\\s+" +
143 "perClientRunRows=(\\d+),\\s+" +
144 "totalRows=(\\d+),\\s+" +
145 "sampleRate=([-+]?[0-9]*\\.?[0-9]+),\\s+" +
146 "clients=(\\d+),\\s+" +
147 "flushCommits=(\\w+),\\s+" +
148 "writeToWAL=(\\w+),\\s+" +
149 "reportLatency=(\\w+)");
150
151
152
153
154
155 protected static enum Counter {
156
157 ELAPSED_TIME,
158
159 ROWS
160 }
161
162
163
164
165
166 public PerformanceEvaluation(final Configuration conf) {
167 super(conf);
168
169 addCommandDescriptor(RandomReadTest.class, "randomRead",
170 "Run random read test");
171 addCommandDescriptor(RandomSeekScanTest.class, "randomSeekScan",
172 "Run random seek and scan 100 test");
173 addCommandDescriptor(RandomScanWithRange10Test.class, "scanRange10",
174 "Run random seek scan with both start and stop row (max 10 rows)");
175 addCommandDescriptor(RandomScanWithRange100Test.class, "scanRange100",
176 "Run random seek scan with both start and stop row (max 100 rows)");
177 addCommandDescriptor(RandomScanWithRange1000Test.class, "scanRange1000",
178 "Run random seek scan with both start and stop row (max 1000 rows)");
179 addCommandDescriptor(RandomScanWithRange10000Test.class, "scanRange10000",
180 "Run random seek scan with both start and stop row (max 10000 rows)");
181 addCommandDescriptor(RandomWriteTest.class, "randomWrite",
182 "Run random write test");
183 addCommandDescriptor(SequentialReadTest.class, "sequentialRead",
184 "Run sequential read test");
185 addCommandDescriptor(SequentialWriteTest.class, "sequentialWrite",
186 "Run sequential write test");
187 addCommandDescriptor(ScanTest.class, "scan",
188 "Run scan test (read every row)");
189 addCommandDescriptor(FilteredScanTest.class, "filterScan",
190 "Run scan test using a filter to find a specific row based on it's value (make sure to use --rows=20)");
191 }
192
193 protected void addCommandDescriptor(Class<? extends Test> cmdClass,
194 String name, String description) {
195 CmdDescriptor cmdDescriptor =
196 new CmdDescriptor(cmdClass, name, description);
197 commands.put(name, cmdDescriptor);
198 }
199
200
201
202
203 interface Status {
204
205
206
207
208
209 void setStatus(final String msg) throws IOException;
210 }
211
212
213
214
215
216
217
218 public static class PeInputSplit extends InputSplit implements Writable {
219 private TableName tableName = TABLE_NAME;
220 private int startRow = 0;
221 private int rows = 0;
222 private int totalRows = 0;
223 private float sampleRate = 1.0f;
224 private int clients = 0;
225 private boolean flushCommits = false;
226 private boolean writeToWAL = true;
227 private boolean reportLatency = false;
228
229 public PeInputSplit() {}
230
231 public PeInputSplit(TableName tableName, int startRow, int rows, int totalRows,
232 float sampleRate, int clients, boolean flushCommits, boolean writeToWAL,
233 boolean reportLatency) {
234 this.tableName = tableName;
235 this.startRow = startRow;
236 this.rows = rows;
237 this.totalRows = totalRows;
238 this.sampleRate = sampleRate;
239 this.clients = clients;
240 this.flushCommits = flushCommits;
241 this.writeToWAL = writeToWAL;
242 this.reportLatency = reportLatency;
243 }
244
245 @Override
246 public void readFields(DataInput in) throws IOException {
247 int tableNameLen = in.readInt();
248 byte[] name = new byte[tableNameLen];
249 in.readFully(name);
250 this.tableName = TableName.valueOf(name);
251
252 this.startRow = in.readInt();
253 this.rows = in.readInt();
254 this.totalRows = in.readInt();
255 this.sampleRate = in.readFloat();
256 this.clients = in.readInt();
257 this.flushCommits = in.readBoolean();
258 this.writeToWAL = in.readBoolean();
259 this.reportLatency = in.readBoolean();
260 }
261
262 @Override
263 public void write(DataOutput out) throws IOException {
264 byte[] name = this.tableName.toBytes();
265 out.writeInt(name.length);
266 out.write(name);
267 out.writeInt(startRow);
268 out.writeInt(rows);
269 out.writeInt(totalRows);
270 out.writeFloat(sampleRate);
271 out.writeInt(clients);
272 out.writeBoolean(flushCommits);
273 out.writeBoolean(writeToWAL);
274 out.writeBoolean(reportLatency);
275 }
276
277 @Override
278 public long getLength() throws IOException, InterruptedException {
279 return 0;
280 }
281
282 @Override
283 public String[] getLocations() throws IOException, InterruptedException {
284 return new String[0];
285 }
286
287 public TableName getTableName() {
288 return tableName;
289 }
290
291 public int getStartRow() {
292 return startRow;
293 }
294
295 public int getRows() {
296 return rows;
297 }
298
299 public int getTotalRows() {
300 return totalRows;
301 }
302
303 public float getSampleRate() {
304 return sampleRate;
305 }
306
307 public int getClients() {
308 return clients;
309 }
310
311 public boolean isFlushCommits() {
312 return flushCommits;
313 }
314
315 public boolean isWriteToWAL() {
316 return writeToWAL;
317 }
318
319 public boolean isReportLatency() {
320 return reportLatency;
321 }
322 }
323
324
325
326
327
328 public static class PeInputFormat extends FileInputFormat<NullWritable, PeInputSplit> {
329
330 @Override
331 public List<InputSplit> getSplits(JobContext job) throws IOException {
332
333 List<InputSplit> splitList = new ArrayList<InputSplit>();
334
335 for (FileStatus file: listStatus(job)) {
336 if (file.isDir()) {
337 continue;
338 }
339 Path path = file.getPath();
340 FileSystem fs = path.getFileSystem(job.getConfiguration());
341 FSDataInputStream fileIn = fs.open(path);
342 LineReader in = new LineReader(fileIn, job.getConfiguration());
343 int lineLen = 0;
344 while(true) {
345 Text lineText = new Text();
346 lineLen = in.readLine(lineText);
347 if(lineLen <= 0) {
348 break;
349 }
350 Matcher m = LINE_PATTERN.matcher(lineText.toString());
351 if((m != null) && m.matches()) {
352 TableName tableName = TableName.valueOf(m.group(1));
353 int startRow = Integer.parseInt(m.group(2));
354 int rows = Integer.parseInt(m.group(3));
355 int totalRows = Integer.parseInt(m.group(4));
356 float sampleRate = Float.parseFloat(m.group(5));
357 int clients = Integer.parseInt(m.group(6));
358 boolean flushCommits = Boolean.parseBoolean(m.group(7));
359 boolean writeToWAL = Boolean.parseBoolean(m.group(8));
360 boolean reportLatency = Boolean.parseBoolean(m.group(9));
361
362 LOG.debug("tableName=" + tableName +
363 " split["+ splitList.size() + "] " +
364 " startRow=" + startRow +
365 " rows=" + rows +
366 " totalRows=" + totalRows +
367 " sampleRate=" + sampleRate +
368 " clients=" + clients +
369 " flushCommits=" + flushCommits +
370 " writeToWAL=" + writeToWAL +
371 " reportLatency=" + reportLatency);
372
373 PeInputSplit newSplit =
374 new PeInputSplit(tableName, startRow, rows, totalRows, sampleRate, clients,
375 flushCommits, writeToWAL, reportLatency);
376 splitList.add(newSplit);
377 }
378 }
379 in.close();
380 }
381
382 LOG.info("Total # of splits: " + splitList.size());
383 return splitList;
384 }
385
386 @Override
387 public RecordReader<NullWritable, PeInputSplit> createRecordReader(InputSplit split,
388 TaskAttemptContext context) {
389 return new PeRecordReader();
390 }
391
392 public static class PeRecordReader extends RecordReader<NullWritable, PeInputSplit> {
393 private boolean readOver = false;
394 private PeInputSplit split = null;
395 private NullWritable key = null;
396 private PeInputSplit value = null;
397
398 @Override
399 public void initialize(InputSplit split, TaskAttemptContext context)
400 throws IOException, InterruptedException {
401 this.readOver = false;
402 this.split = (PeInputSplit)split;
403 }
404
405 @Override
406 public boolean nextKeyValue() throws IOException, InterruptedException {
407 if(readOver) {
408 return false;
409 }
410
411 key = NullWritable.get();
412 value = split;
413
414 readOver = true;
415 return true;
416 }
417
418 @Override
419 public NullWritable getCurrentKey() throws IOException, InterruptedException {
420 return key;
421 }
422
423 @Override
424 public PeInputSplit getCurrentValue() throws IOException, InterruptedException {
425 return value;
426 }
427
428 @Override
429 public float getProgress() throws IOException, InterruptedException {
430 if(readOver) {
431 return 1.0f;
432 } else {
433 return 0.0f;
434 }
435 }
436
437 @Override
438 public void close() throws IOException {
439
440 }
441 }
442 }
443
444
445
446
447 public static class EvaluationMapTask
448 extends Mapper<NullWritable, PeInputSplit, LongWritable, LongWritable> {
449
450
451 public final static String CMD_KEY = "EvaluationMapTask.command";
452
453 public static final String PE_KEY = "EvaluationMapTask.performanceEvalImpl";
454
455 private Class<? extends Test> cmd;
456 private PerformanceEvaluation pe;
457
458 @Override
459 protected void setup(Context context) throws IOException, InterruptedException {
460 this.cmd = forName(context.getConfiguration().get(CMD_KEY), Test.class);
461
462
463
464 Class<? extends PerformanceEvaluation> peClass =
465 forName(context.getConfiguration().get(PE_KEY), PerformanceEvaluation.class);
466 try {
467 this.pe = peClass.getConstructor(Configuration.class)
468 .newInstance(context.getConfiguration());
469 } catch (Exception e) {
470 throw new IllegalStateException("Could not instantiate PE instance", e);
471 }
472 }
473
474 private <Type> Class<? extends Type> forName(String className, Class<Type> type) {
475 Class<? extends Type> clazz = null;
476 try {
477 clazz = Class.forName(className).asSubclass(type);
478 } catch (ClassNotFoundException e) {
479 throw new IllegalStateException("Could not find class for name: " + className, e);
480 }
481 return clazz;
482 }
483
484 protected void map(NullWritable key, PeInputSplit value, final Context context)
485 throws IOException, InterruptedException {
486
487 Status status = new Status() {
488 public void setStatus(String msg) {
489 context.setStatus(msg);
490 }
491 };
492
493
494 pe.tableName = value.getTableName();
495 long elapsedTime = this.pe.runOneClient(this.cmd, value.getStartRow(),
496 value.getRows(), value.getTotalRows(), value.getSampleRate(),
497 value.isFlushCommits(), value.isWriteToWAL(), value.isReportLatency(),
498 HConnectionManager.createConnection(context.getConfiguration()), status);
499
500
501 context.getCounter(Counter.ELAPSED_TIME).increment(elapsedTime);
502 context.getCounter(Counter.ROWS).increment(value.rows);
503 context.write(new LongWritable(value.startRow), new LongWritable(elapsedTime));
504 context.progress();
505 }
506 }
507
508
509
510
511
512
513
514 private boolean checkTable(HBaseAdmin admin) throws IOException {
515 HTableDescriptor tableDescriptor = getTableDescriptor();
516 if (this.presplitRegions > 0) {
517
518 if (admin.tableExists(tableDescriptor.getTableName())) {
519 admin.disableTable(tableDescriptor.getTableName());
520 admin.deleteTable(tableDescriptor.getTableName());
521 }
522
523 byte[][] splits = getSplits();
524 for (int i=0; i < splits.length; i++) {
525 LOG.debug(" split " + i + ": " + Bytes.toStringBinary(splits[i]));
526 }
527 admin.createTable(tableDescriptor, splits);
528 LOG.info ("Table created with " + this.presplitRegions + " splits");
529 }
530 else {
531 boolean tableExists = admin.tableExists(tableDescriptor.getTableName());
532 if (!tableExists) {
533 admin.createTable(tableDescriptor);
534 LOG.info("Table " + tableDescriptor + " created");
535 }
536 }
537 return admin.tableExists(tableDescriptor.getTableName());
538 }
539
540 protected HTableDescriptor getTableDescriptor() {
541 if (TABLE_DESCRIPTOR == null) {
542 TABLE_DESCRIPTOR = new HTableDescriptor(tableName);
543 HColumnDescriptor family = new HColumnDescriptor(FAMILY_NAME);
544 family.setDataBlockEncoding(blockEncoding);
545 family.setCompressionType(compression);
546 if (inMemoryCF) {
547 family.setInMemory(true);
548 }
549 TABLE_DESCRIPTOR.addFamily(family);
550 }
551 return TABLE_DESCRIPTOR;
552 }
553
554
555
556
557
558
559 protected byte[][] getSplits() {
560 if (this.presplitRegions == 0)
561 return new byte [0][];
562
563 int numSplitPoints = presplitRegions - 1;
564 byte[][] splits = new byte[numSplitPoints][];
565 int jump = this.R / this.presplitRegions;
566 for (int i=0; i < numSplitPoints; i++) {
567 int rowkey = jump * (1 + i);
568 splits[i] = format(rowkey);
569 }
570 return splits;
571 }
572
573
574
575
576
577
578
579 private void runNIsMoreThanOne(final Class<? extends Test> cmd)
580 throws IOException, InterruptedException, ClassNotFoundException {
581 checkTable(new HBaseAdmin(getConf()));
582 if (this.nomapred) {
583 doMultipleClients(cmd);
584 } else {
585 doMapReduce(cmd);
586 }
587 }
588
589
590
591
592
593
594 private void doMultipleClients(final Class<? extends Test> cmd) throws IOException {
595 final List<Thread> threads = new ArrayList<Thread>(this.N);
596 final long[] timings = new long[this.N];
597 final int perClientRows = R/N;
598 final float sampleRate = this.sampleRate;
599 final TableName tableName = this.tableName;
600 final DataBlockEncoding encoding = this.blockEncoding;
601 final boolean flushCommits = this.flushCommits;
602 final Compression.Algorithm compression = this.compression;
603 final boolean writeToWal = this.writeToWAL;
604 final boolean reportLatency = this.reportLatency;
605 final int preSplitRegions = this.presplitRegions;
606 final HConnection connection = HConnectionManager.createConnection(getConf());
607 for (int i = 0; i < this.N; i++) {
608 final int index = i;
609 Thread t = new Thread ("TestClient-" + i) {
610 @Override
611 public void run() {
612 super.run();
613 PerformanceEvaluation pe = new PerformanceEvaluation(getConf());
614 pe.tableName = tableName;
615 pe.blockEncoding = encoding;
616 pe.flushCommits = flushCommits;
617 pe.compression = compression;
618 pe.writeToWAL = writeToWal;
619 pe.presplitRegions = preSplitRegions;
620 pe.N = N;
621 pe.sampleRate = sampleRate;
622 pe.reportLatency = reportLatency;
623 pe.connection = connection;
624 try {
625 long elapsedTime = pe.runOneClient(cmd, index * perClientRows,
626 perClientRows, R, sampleRate,
627 flushCommits, writeToWAL, reportLatency, connection, new Status() {
628 public void setStatus(final String msg) throws IOException {
629 LOG.info("client-" + getName() + " " + msg);
630 }
631 });
632 timings[index] = elapsedTime;
633 LOG.info("Finished " + getName() + " in " + elapsedTime +
634 "ms writing " + perClientRows + " rows");
635 } catch (IOException e) {
636 throw new RuntimeException(e);
637 }
638 }
639 };
640 threads.add(t);
641 }
642 for (Thread t: threads) {
643 t.start();
644 }
645 for (Thread t: threads) {
646 while(t.isAlive()) {
647 try {
648 t.join();
649 } catch (InterruptedException e) {
650 LOG.debug("Interrupted, continuing" + e.toString());
651 }
652 }
653 }
654 final String test = cmd.getSimpleName();
655 LOG.info("[" + test + "] Summary of timings (ms): "
656 + Arrays.toString(timings));
657 Arrays.sort(timings);
658 long total = 0;
659 for (int i = 0; i < this.N; i++) {
660 total += timings[i];
661 }
662 LOG.info("[" + test + "]"
663 + "\tMin: " + timings[0] + "ms"
664 + "\tMax: " + timings[this.N - 1] + "ms"
665 + "\tAvg: " + (total / this.N) + "ms");
666 }
667
668
669
670
671
672
673
674
675 private void doMapReduce(final Class<? extends Test> cmd) throws IOException,
676 InterruptedException, ClassNotFoundException {
677 Configuration conf = getConf();
678 Path inputDir = writeInputFile(conf);
679 conf.set(EvaluationMapTask.CMD_KEY, cmd.getName());
680 conf.set(EvaluationMapTask.PE_KEY, getClass().getName());
681 Job job = new Job(conf);
682 job.setJarByClass(PerformanceEvaluation.class);
683 job.setJobName("HBase Performance Evaluation");
684
685 job.setInputFormatClass(PeInputFormat.class);
686 PeInputFormat.setInputPaths(job, inputDir);
687
688 job.setOutputKeyClass(LongWritable.class);
689 job.setOutputValueClass(LongWritable.class);
690
691 job.setMapperClass(EvaluationMapTask.class);
692 job.setReducerClass(LongSumReducer.class);
693
694 job.setNumReduceTasks(1);
695
696 job.setOutputFormatClass(TextOutputFormat.class);
697 TextOutputFormat.setOutputPath(job, new Path(inputDir.getParent(), "outputs"));
698
699 TableMapReduceUtil.addDependencyJars(job);
700
701 TableMapReduceUtil.addDependencyJars(job.getConfiguration(),
702 org.apache.hadoop.hbase.util.Bytes.class);
703
704 TableMapReduceUtil.initCredentials(job);
705
706 job.waitForCompletion(true);
707 }
708
709
710
711
712
713
714
715 private Path writeInputFile(final Configuration c) throws IOException {
716 SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
717 Path jobdir = new Path(PERF_EVAL_DIR, formatter.format(new Date()));
718 Path inputDir = new Path(jobdir, "inputs");
719
720 FileSystem fs = FileSystem.get(c);
721 fs.mkdirs(inputDir);
722
723 Path inputFile = new Path(inputDir, "input.txt");
724 PrintStream out = new PrintStream(fs.create(inputFile));
725
726 Map<Integer, String> m = new TreeMap<Integer, String>();
727 Hash h = MurmurHash.getInstance();
728 int perClientRows = (this.R / this.N);
729 try {
730 for (int i = 0; i < 10; i++) {
731 for (int j = 0; j < N; j++) {
732 String s = "tableName=" + this.tableName +
733 ", startRow=" + ((j * perClientRows) + (i * (perClientRows/10))) +
734 ", perClientRunRows=" + (perClientRows / 10) +
735 ", totalRows=" + this.R +
736 ", sampleRate=" + this.sampleRate +
737 ", clients=" + this.N +
738 ", flushCommits=" + this.flushCommits +
739 ", writeToWAL=" + this.writeToWAL +
740 ", reportLatency=" + this.reportLatency;
741 int hash = h.hash(Bytes.toBytes(s));
742 m.put(hash, s);
743 }
744 }
745 for (Map.Entry<Integer, String> e: m.entrySet()) {
746 out.println(e.getValue());
747 }
748 } finally {
749 out.close();
750 }
751 return inputDir;
752 }
753
754
755
756
757 static class CmdDescriptor {
758 private Class<? extends Test> cmdClass;
759 private String name;
760 private String description;
761
762 CmdDescriptor(Class<? extends Test> cmdClass, String name, String description) {
763 this.cmdClass = cmdClass;
764 this.name = name;
765 this.description = description;
766 }
767
768 public Class<? extends Test> getCmdClass() {
769 return cmdClass;
770 }
771
772 public String getName() {
773 return name;
774 }
775
776 public String getDescription() {
777 return description;
778 }
779 }
780
781
782
783
784
785 static class TestOptions {
786 private int startRow;
787 private int perClientRunRows;
788 private int totalRows;
789 private float sampleRate;
790 private int numClientThreads;
791 private TableName tableName;
792 private boolean flushCommits;
793 private boolean writeToWAL = true;
794 private boolean reportLatency;
795 private HConnection connection;
796
797 TestOptions(int startRow, int perClientRunRows, int totalRows, float sampleRate,
798 int numClientThreads, TableName tableName, boolean flushCommits, boolean writeToWAL,
799 boolean reportLatency, HConnection connection) {
800 this.startRow = startRow;
801 this.perClientRunRows = perClientRunRows;
802 this.totalRows = totalRows;
803 this.sampleRate = sampleRate;
804 this.numClientThreads = numClientThreads;
805 this.tableName = tableName;
806 this.flushCommits = flushCommits;
807 this.writeToWAL = writeToWAL;
808 this.reportLatency = reportLatency;
809 this.connection = connection;
810 }
811
812 public int getStartRow() {
813 return startRow;
814 }
815
816 public int getPerClientRunRows() {
817 return perClientRunRows;
818 }
819
820 public int getTotalRows() {
821 return totalRows;
822 }
823
824 public float getSampleRate() {
825 return sampleRate;
826 }
827
828 public int getNumClientThreads() {
829 return numClientThreads;
830 }
831
832 public TableName getTableName() {
833 return tableName;
834 }
835
836 public boolean isFlushCommits() {
837 return flushCommits;
838 }
839
840 public boolean isWriteToWAL() {
841 return writeToWAL;
842 }
843
844 public boolean isReportLatency() {
845 return reportLatency;
846 }
847
848 public HConnection getConnection() {
849 return connection;
850 }
851 }
852
853
854
855
856
857 static abstract class Test {
858
859
860 private static final Random randomSeed =
861 new Random(System.currentTimeMillis());
862 private static long nextRandomSeed() {
863 return randomSeed.nextLong();
864 }
865 protected final Random rand = new Random(nextRandomSeed());
866
867 protected final int startRow;
868 protected final int perClientRunRows;
869 protected final int totalRows;
870 protected final float sampleRate;
871 private final Status status;
872 protected TableName tableName;
873 protected HTableInterface table;
874 protected volatile Configuration conf;
875 protected boolean flushCommits;
876 protected boolean writeToWAL;
877 protected boolean reportLatency;
878 protected HConnection connection;
879
880
881
882
883
884 Test(final Configuration conf, final TestOptions options, final Status status) {
885 super();
886 this.startRow = options.getStartRow();
887 this.perClientRunRows = options.getPerClientRunRows();
888 this.totalRows = options.getTotalRows();
889 this.sampleRate = options.getSampleRate();
890 this.status = status;
891 this.tableName = options.getTableName();
892 this.table = null;
893 this.conf = conf;
894 this.flushCommits = options.isFlushCommits();
895 this.writeToWAL = options.isWriteToWAL();
896 this.reportLatency = options.isReportLatency();
897 this.connection = options.getConnection();
898 }
899
900 private String generateStatus(final int sr, final int i, final int lr) {
901 return sr + "/" + i + "/" + lr;
902 }
903
904 protected int getReportingPeriod() {
905 int period = this.perClientRunRows / 10;
906 return period == 0 ? this.perClientRunRows : period;
907 }
908
909 void testSetup() throws IOException {
910 this.table = connection.getTable(tableName);
911 this.table.setAutoFlush(false, true);
912 }
913
914 void testTakedown() throws IOException {
915 if (flushCommits) {
916 this.table.flushCommits();
917 }
918 table.close();
919 }
920
921
922
923
924
925
926 long test() throws IOException {
927 testSetup();
928 LOG.info("Timed test starting in thread " + Thread.currentThread().getName());
929 final long startTime = System.nanoTime();
930 try {
931 testTimed();
932 } finally {
933 testTakedown();
934 }
935 return (System.nanoTime() - startTime) / 1000000;
936 }
937
938
939
940
941 void testTimed() throws IOException {
942 int lastRow = this.startRow + this.perClientRunRows;
943
944 for (int i = this.startRow; i < lastRow; i++) {
945 testRow(i);
946 if (status != null && i > 0 && (i % getReportingPeriod()) == 0) {
947 status.setStatus(generateStatus(this.startRow, i, lastRow));
948 }
949 }
950 }
951
952
953
954
955
956 abstract void testRow(final int i) throws IOException;
957 }
958
959
960 @SuppressWarnings("unused")
961 static class RandomSeekScanTest extends Test {
962 RandomSeekScanTest(Configuration conf, TestOptions options, Status status) {
963 super(conf, options, status);
964 }
965
966 @Override
967 void testRow(final int i) throws IOException {
968 Scan scan = new Scan(getRandomRow(this.rand, this.totalRows));
969 scan.addColumn(FAMILY_NAME, QUALIFIER_NAME);
970 scan.setFilter(new WhileMatchFilter(new PageFilter(120)));
971 ResultScanner s = this.table.getScanner(scan);
972 for (Result rr; (rr = s.next()) != null;) ;
973 s.close();
974 }
975
976 @Override
977 protected int getReportingPeriod() {
978 int period = this.perClientRunRows / 100;
979 return period == 0 ? this.perClientRunRows : period;
980 }
981
982 }
983
984 @SuppressWarnings("unused")
985 static abstract class RandomScanWithRangeTest extends Test {
986 RandomScanWithRangeTest(Configuration conf, TestOptions options, Status status) {
987 super(conf, options, status);
988 }
989
990 @Override
991 void testRow(final int i) throws IOException {
992 Pair<byte[], byte[]> startAndStopRow = getStartAndStopRow();
993 Scan scan = new Scan(startAndStopRow.getFirst(), startAndStopRow.getSecond());
994 scan.addColumn(FAMILY_NAME, QUALIFIER_NAME);
995 ResultScanner s = this.table.getScanner(scan);
996 int count = 0;
997 for (Result rr; (rr = s.next()) != null;) {
998 count++;
999 }
1000
1001 if (i % 100 == 0) {
1002 LOG.info(String.format("Scan for key range %s - %s returned %s rows",
1003 Bytes.toString(startAndStopRow.getFirst()),
1004 Bytes.toString(startAndStopRow.getSecond()), count));
1005 }
1006
1007 s.close();
1008 }
1009
1010 protected abstract Pair<byte[],byte[]> getStartAndStopRow();
1011
1012 protected Pair<byte[], byte[]> generateStartAndStopRows(int maxRange) {
1013 int start = this.rand.nextInt(Integer.MAX_VALUE) % totalRows;
1014 int stop = start + maxRange;
1015 return new Pair<byte[],byte[]>(format(start), format(stop));
1016 }
1017
1018 @Override
1019 protected int getReportingPeriod() {
1020 int period = this.perClientRunRows / 100;
1021 return period == 0? this.perClientRunRows: period;
1022 }
1023 }
1024
1025 static class RandomScanWithRange10Test extends RandomScanWithRangeTest {
1026 RandomScanWithRange10Test(Configuration conf, TestOptions options, Status status) {
1027 super(conf, options, status);
1028 }
1029
1030 @Override
1031 protected Pair<byte[], byte[]> getStartAndStopRow() {
1032 return generateStartAndStopRows(10);
1033 }
1034 }
1035
1036 static class RandomScanWithRange100Test extends RandomScanWithRangeTest {
1037 RandomScanWithRange100Test(Configuration conf, TestOptions options, Status status) {
1038 super(conf, options, status);
1039 }
1040
1041 @Override
1042 protected Pair<byte[], byte[]> getStartAndStopRow() {
1043 return generateStartAndStopRows(100);
1044 }
1045 }
1046
1047 static class RandomScanWithRange1000Test extends RandomScanWithRangeTest {
1048 RandomScanWithRange1000Test(Configuration conf, TestOptions options, Status status) {
1049 super(conf, options, status);
1050 }
1051
1052 @Override
1053 protected Pair<byte[], byte[]> getStartAndStopRow() {
1054 return generateStartAndStopRows(1000);
1055 }
1056 }
1057
1058 static class RandomScanWithRange10000Test extends RandomScanWithRangeTest {
1059 RandomScanWithRange10000Test(Configuration conf, TestOptions options, Status status) {
1060 super(conf, options, status);
1061 }
1062
1063 @Override
1064 protected Pair<byte[], byte[]> getStartAndStopRow() {
1065 return generateStartAndStopRows(10000);
1066 }
1067 }
1068
1069 static class RandomReadTest extends Test {
1070 private final int everyN;
1071 private final boolean reportLatency;
1072 private final float[] times;
1073 int idx = 0;
1074
1075 RandomReadTest(Configuration conf, TestOptions options, Status status) {
1076 super(conf, options, status);
1077 everyN = (int) (this.totalRows / (this.totalRows * this.sampleRate));
1078 LOG.info("Sampling 1 every " + everyN + " out of " + perClientRunRows + " total rows.");
1079 this.reportLatency = options.isReportLatency();
1080 if (this.reportLatency) {
1081 times = new float[(int) Math.ceil(this.perClientRunRows * this.sampleRate)];
1082 } else {
1083 times = null;
1084 }
1085 }
1086
1087 @Override
1088 void testRow(final int i) throws IOException {
1089 if (i % everyN == 0) {
1090 Get get = new Get(getRandomRow(this.rand, this.totalRows));
1091 get.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1092 long start = System.nanoTime();
1093 this.table.get(get);
1094 if (this.reportLatency) {
1095 times[idx++] = (float) ((System.nanoTime() - start) / 1000000.0);
1096 }
1097 }
1098 }
1099
1100 @Override
1101 protected int getReportingPeriod() {
1102 int period = this.perClientRunRows / 100;
1103 return period == 0 ? this.perClientRunRows : period;
1104 }
1105
1106 @Override
1107 protected void testTakedown() throws IOException {
1108 super.testTakedown();
1109 if (this.reportLatency) {
1110 LOG.info("randomRead latency log (ms): " + Arrays.toString(times));
1111 }
1112 }
1113 }
1114
1115 static class RandomWriteTest extends Test {
1116 RandomWriteTest(Configuration conf, TestOptions options, Status status) {
1117 super(conf, options, status);
1118 }
1119
1120 @Override
1121 void testRow(final int i) throws IOException {
1122 byte [] row = getRandomRow(this.rand, this.totalRows);
1123 Put put = new Put(row);
1124 byte[] value = generateValue(this.rand);
1125 put.add(FAMILY_NAME, QUALIFIER_NAME, value);
1126 put.setDurability(writeToWAL ? Durability.SYNC_WAL : Durability.SKIP_WAL);
1127 table.put(put);
1128 }
1129 }
1130
1131
1132 static class ScanTest extends Test {
1133 private ResultScanner testScanner;
1134
1135 ScanTest(Configuration conf, TestOptions options, Status status) {
1136 super(conf, options, status);
1137 }
1138
1139 @Override
1140 void testTakedown() throws IOException {
1141 if (this.testScanner != null) {
1142 this.testScanner.close();
1143 }
1144 super.testTakedown();
1145 }
1146
1147
1148 @Override
1149 void testRow(final int i) throws IOException {
1150 if (this.testScanner == null) {
1151 Scan scan = new Scan(format(this.startRow));
1152 scan.setCaching(30);
1153 scan.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1154 this.testScanner = table.getScanner(scan);
1155 }
1156 testScanner.next();
1157 }
1158
1159 }
1160
1161 static class SequentialReadTest extends Test {
1162 SequentialReadTest(Configuration conf, TestOptions options, Status status) {
1163 super(conf, options, status);
1164 }
1165
1166 @Override
1167 void testRow(final int i) throws IOException {
1168 Get get = new Get(format(i));
1169 get.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1170 table.get(get);
1171 }
1172 }
1173
1174 static class SequentialWriteTest extends Test {
1175 SequentialWriteTest(Configuration conf, TestOptions options, Status status) {
1176 super(conf, options, status);
1177 }
1178
1179 @Override
1180 void testRow(final int i) throws IOException {
1181 Put put = new Put(format(i));
1182 byte[] value = generateValue(this.rand);
1183 put.add(FAMILY_NAME, QUALIFIER_NAME, value);
1184 put.setDurability(writeToWAL ? Durability.SYNC_WAL : Durability.SKIP_WAL);
1185 table.put(put);
1186 }
1187 }
1188
1189 static class FilteredScanTest extends Test {
1190 protected static final Log LOG = LogFactory.getLog(FilteredScanTest.class.getName());
1191
1192 FilteredScanTest(Configuration conf, TestOptions options, Status status) {
1193 super(conf, options, status);
1194 }
1195
1196 @Override
1197 void testRow(int i) throws IOException {
1198 byte[] value = generateValue(this.rand);
1199 Scan scan = constructScan(value);
1200 ResultScanner scanner = null;
1201 try {
1202 scanner = this.table.getScanner(scan);
1203 while (scanner.next() != null) {
1204 }
1205 } finally {
1206 if (scanner != null) scanner.close();
1207 }
1208 }
1209
1210 protected Scan constructScan(byte[] valuePrefix) throws IOException {
1211 Filter filter = new SingleColumnValueFilter(
1212 FAMILY_NAME, QUALIFIER_NAME, CompareFilter.CompareOp.EQUAL,
1213 new BinaryComparator(valuePrefix)
1214 );
1215 Scan scan = new Scan();
1216 scan.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1217 scan.setFilter(filter);
1218 return scan;
1219 }
1220 }
1221
1222
1223
1224
1225
1226
1227
1228 private static String calculateMbps(int rows, long timeMs) {
1229
1230
1231 BigDecimal rowSize =
1232 BigDecimal.valueOf(ROW_LENGTH + VALUE_LENGTH + FAMILY_NAME.length + QUALIFIER_NAME.length);
1233 BigDecimal mbps = BigDecimal.valueOf(rows).multiply(rowSize, CXT)
1234 .divide(BigDecimal.valueOf(timeMs), CXT).multiply(MS_PER_SEC, CXT)
1235 .divide(BYTES_PER_MB, CXT);
1236 return FMT.format(mbps) + " MB/s";
1237 }
1238
1239
1240
1241
1242
1243
1244
1245 public static byte [] format(final int number) {
1246 byte [] b = new byte[ROW_LENGTH];
1247 int d = Math.abs(number);
1248 for (int i = b.length - 1; i >= 0; i--) {
1249 b[i] = (byte)((d % 10) + '0');
1250 d /= 10;
1251 }
1252 return b;
1253 }
1254
1255
1256
1257
1258
1259
1260
1261 public static byte[] generateValue(final Random r) {
1262 byte [] b = new byte [VALUE_LENGTH];
1263 int i = 0;
1264
1265 for(i = 0; i < (VALUE_LENGTH-8); i += 8) {
1266 b[i] = (byte) (65 + r.nextInt(26));
1267 b[i+1] = b[i];
1268 b[i+2] = b[i];
1269 b[i+3] = b[i];
1270 b[i+4] = b[i];
1271 b[i+5] = b[i];
1272 b[i+6] = b[i];
1273 b[i+7] = b[i];
1274 }
1275
1276 byte a = (byte) (65 + r.nextInt(26));
1277 for(; i < VALUE_LENGTH; i++) {
1278 b[i] = a;
1279 }
1280 return b;
1281 }
1282
1283 static byte [] getRandomRow(final Random random, final int totalRows) {
1284 return format(random.nextInt(Integer.MAX_VALUE) % totalRows);
1285 }
1286
1287 long runOneClient(final Class<? extends Test> cmd, final int startRow,
1288 final int perClientRunRows, final int totalRows, final float sampleRate,
1289 boolean flushCommits, boolean writeToWAL, boolean reportLatency,
1290 HConnection connection, final Status status)
1291 throws IOException {
1292 status.setStatus("Start " + cmd + " at offset " + startRow + " for " +
1293 perClientRunRows + " rows");
1294 long totalElapsedTime = 0;
1295
1296 TestOptions options = new TestOptions(startRow, perClientRunRows,
1297 totalRows, sampleRate, N, tableName, flushCommits, writeToWAL,
1298 reportLatency, connection);
1299 final Test t;
1300 try {
1301 Constructor<? extends Test> constructor = cmd.getDeclaredConstructor(
1302 Configuration.class, TestOptions.class, Status.class);
1303 t = constructor.newInstance(getConf(), options, status);
1304 } catch (NoSuchMethodException e) {
1305 throw new IllegalArgumentException("Invalid command class: " +
1306 cmd.getName() + ". It does not provide a constructor as described by" +
1307 "the javadoc comment. Available constructors are: " +
1308 Arrays.toString(cmd.getConstructors()));
1309 } catch (Exception e) {
1310 throw new IllegalStateException("Failed to construct command class", e);
1311 }
1312 totalElapsedTime = t.test();
1313
1314 status.setStatus("Finished " + cmd + " in " + totalElapsedTime +
1315 "ms at offset " + startRow + " for " + perClientRunRows + " rows" +
1316 " (" + calculateMbps((int)(perClientRunRows * sampleRate), totalElapsedTime) + ")");
1317 return totalElapsedTime;
1318 }
1319
1320 private void runNIsOne(final Class<? extends Test> cmd) throws IOException {
1321 Status status = new Status() {
1322 public void setStatus(String msg) throws IOException {
1323 LOG.info(msg);
1324 }
1325 };
1326
1327 HBaseAdmin admin = null;
1328 try {
1329 admin = new HBaseAdmin(getConf());
1330 checkTable(admin);
1331 runOneClient(cmd, 0, this.R, this.R, this.sampleRate, this.flushCommits,
1332 this.writeToWAL, this.reportLatency, this.connection, status);
1333 } catch (Exception e) {
1334 LOG.error("Failed", e);
1335 } finally {
1336 if (admin != null) admin.close();
1337 }
1338 }
1339
1340 private void runTest(final Class<? extends Test> cmd) throws IOException,
1341 InterruptedException, ClassNotFoundException {
1342 if (N == 1) {
1343
1344
1345 runNIsOne(cmd);
1346 } else {
1347
1348 runNIsMoreThanOne(cmd);
1349 }
1350 }
1351
1352 protected void printUsage() {
1353 printUsage(null);
1354 }
1355
1356 protected void printUsage(final String message) {
1357 if (message != null && message.length() > 0) {
1358 System.err.println(message);
1359 }
1360 System.err.println("Usage: java " + this.getClass().getName() + " \\");
1361 System.err.println(" [--nomapred] [--rows=ROWS] [--table=NAME] \\");
1362 System.err.println(" [--compress=TYPE] [--blockEncoding=TYPE] " +
1363 "[-D<property=value>]* <command> <nclients>");
1364 System.err.println();
1365 System.err.println("Options:");
1366 System.err.println(" nomapred Run multiple clients using threads " +
1367 "(rather than use mapreduce)");
1368 System.err.println(" rows Rows each client runs. Default: One million");
1369 System.err.println(" sampleRate Execute test on a sample of total " +
1370 "rows. Only supported by randomRead. Default: 1.0");
1371 System.err.println(" table Alternate table name. Default: 'TestTable'");
1372 System.err.println(" compress Compression type to use (GZ, LZO, ...). Default: 'NONE'");
1373 System.err.println(" flushCommits Used to determine if the test should flush the table. " +
1374 "Default: false");
1375 System.err.println(" writeToWAL Set writeToWAL on puts. Default: True");
1376 System.err.println(" presplit Create presplit table. Recommended for accurate perf " +
1377 "analysis (see guide). Default: disabled");
1378 System.err.println(" inmemory Tries to keep the HFiles of the CF " +
1379 "inmemory as far as possible. Not guaranteed that reads are always served " +
1380 "from memory. Default: false");
1381 System.err.println(" latency Set to report operation latencies. " +
1382 "Currently only supported by randomRead test. Default: False");
1383 System.err.println();
1384 System.err.println(" Note: -D properties will be applied to the conf used. ");
1385 System.err.println(" For example: ");
1386 System.err.println(" -Dmapred.output.compress=true");
1387 System.err.println(" -Dmapreduce.task.timeout=60000");
1388 System.err.println();
1389 System.err.println("Command:");
1390 for (CmdDescriptor command : commands.values()) {
1391 System.err.println(String.format(" %-15s %s", command.getName(), command.getDescription()));
1392 }
1393 System.err.println();
1394 System.err.println("Args:");
1395 System.err.println(" nclients Integer. Required. Total number of " +
1396 "clients (and HRegionServers)");
1397 System.err.println(" running: 1 <= value <= 500");
1398 System.err.println("Examples:");
1399 System.err.println(" To run a single evaluation client:");
1400 System.err.println(" $ bin/hbase " + this.getClass().getName()
1401 + " sequentialWrite 1");
1402 }
1403
1404 private void getArgs(final int start, final String[] args) {
1405 if(start + 1 > args.length) {
1406 throw new IllegalArgumentException("must supply the number of clients");
1407 }
1408 N = Integer.parseInt(args[start]);
1409 if (N < 1) {
1410 throw new IllegalArgumentException("Number of clients must be > 1");
1411 }
1412
1413 this.R = this.R * N;
1414 }
1415
1416 public int run(String[] args) throws Exception {
1417
1418
1419 int errCode = -1;
1420 if (args.length < 1) {
1421 printUsage();
1422 return errCode;
1423 }
1424
1425 try {
1426
1427
1428
1429
1430
1431
1432 for (int i = 0; i < args.length; i++) {
1433 String cmd = args[i];
1434 if (cmd.equals("-h") || cmd.startsWith("--h")) {
1435 printUsage();
1436 errCode = 0;
1437 break;
1438 }
1439
1440 final String nmr = "--nomapred";
1441 if (cmd.startsWith(nmr)) {
1442 this.nomapred = true;
1443 continue;
1444 }
1445
1446 final String rows = "--rows=";
1447 if (cmd.startsWith(rows)) {
1448 this.R = Integer.parseInt(cmd.substring(rows.length()));
1449 continue;
1450 }
1451
1452 final String sampleRate = "--sampleRate=";
1453 if (cmd.startsWith(sampleRate)) {
1454 this.sampleRate = Float.parseFloat(cmd.substring(sampleRate.length()));
1455 continue;
1456 }
1457
1458 final String table = "--table=";
1459 if (cmd.startsWith(table)) {
1460 this.tableName = TableName.valueOf(cmd.substring(table.length()));
1461 continue;
1462 }
1463
1464 final String compress = "--compress=";
1465 if (cmd.startsWith(compress)) {
1466 this.compression = Compression.Algorithm.valueOf(cmd.substring(compress.length()));
1467 continue;
1468 }
1469
1470 final String blockEncoding = "--blockEncoding=";
1471 if (cmd.startsWith(blockEncoding)) {
1472 this.blockEncoding = DataBlockEncoding.valueOf(cmd.substring(blockEncoding.length()));
1473 continue;
1474 }
1475
1476 final String flushCommits = "--flushCommits=";
1477 if (cmd.startsWith(flushCommits)) {
1478 this.flushCommits = Boolean.parseBoolean(cmd.substring(flushCommits.length()));
1479 continue;
1480 }
1481
1482 final String writeToWAL = "--writeToWAL=";
1483 if (cmd.startsWith(writeToWAL)) {
1484 this.writeToWAL = Boolean.parseBoolean(cmd.substring(writeToWAL.length()));
1485 continue;
1486 }
1487
1488 final String presplit = "--presplit=";
1489 if (cmd.startsWith(presplit)) {
1490 this.presplitRegions = Integer.parseInt(cmd.substring(presplit.length()));
1491 continue;
1492 }
1493
1494 final String inMemory = "--inmemory=";
1495 if (cmd.startsWith(inMemory)) {
1496 this.inMemoryCF = Boolean.parseBoolean(cmd.substring(inMemory.length()));
1497 continue;
1498 }
1499
1500 final String latency = "--latency";
1501 if (cmd.startsWith(latency)) {
1502 this.reportLatency = true;
1503 continue;
1504 }
1505
1506 this.connection = HConnectionManager.createConnection(getConf());
1507
1508 Class<? extends Test> cmdClass = determineCommandClass(cmd);
1509 if (cmdClass != null) {
1510 getArgs(i + 1, args);
1511 runTest(cmdClass);
1512 errCode = 0;
1513 break;
1514 }
1515
1516 printUsage();
1517 break;
1518 }
1519 } catch (Exception e) {
1520 e.printStackTrace();
1521 }
1522
1523 return errCode;
1524 }
1525
1526 private Class<? extends Test> determineCommandClass(String cmd) {
1527 CmdDescriptor descriptor = commands.get(cmd);
1528 return descriptor != null ? descriptor.getCmdClass() : null;
1529 }
1530
1531 public static void main(final String[] args) throws Exception {
1532 int res = ToolRunner.run(new PerformanceEvaluation(HBaseConfiguration.create()), args);
1533 System.exit(res);
1534 }
1535 }