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 static org.codehaus.jackson.map.SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY;
22
23 import java.io.IOException;
24 import java.io.PrintStream;
25 import java.lang.reflect.Constructor;
26 import java.math.BigDecimal;
27 import java.math.MathContext;
28 import java.text.DecimalFormat;
29 import java.text.SimpleDateFormat;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Date;
33 import java.util.LinkedList;
34 import java.util.Map;
35 import java.util.Queue;
36 import java.util.Random;
37 import java.util.TreeMap;
38 import java.util.concurrent.Callable;
39 import java.util.concurrent.ExecutionException;
40 import java.util.concurrent.ExecutorService;
41 import java.util.concurrent.Executors;
42 import java.util.concurrent.Future;
43
44 import com.google.common.base.Objects;
45 import com.google.common.util.concurrent.ThreadFactoryBuilder;
46
47 import org.apache.commons.logging.Log;
48 import org.apache.commons.logging.LogFactory;
49 import org.apache.hadoop.conf.Configuration;
50 import org.apache.hadoop.conf.Configured;
51 import org.apache.hadoop.fs.FileSystem;
52 import org.apache.hadoop.fs.Path;
53 import org.apache.hadoop.hbase.client.Append;
54 import org.apache.hadoop.hbase.client.Delete;
55 import org.apache.hadoop.hbase.client.Durability;
56 import org.apache.hadoop.hbase.client.Get;
57 import org.apache.hadoop.hbase.client.HBaseAdmin;
58 import org.apache.hadoop.hbase.client.HConnection;
59 import org.apache.hadoop.hbase.client.HConnectionManager;
60 import org.apache.hadoop.hbase.client.HTableInterface;
61 import org.apache.hadoop.hbase.client.Increment;
62 import org.apache.hadoop.hbase.client.Put;
63 import org.apache.hadoop.hbase.client.Result;
64 import org.apache.hadoop.hbase.client.ResultScanner;
65 import org.apache.hadoop.hbase.client.RowMutations;
66 import org.apache.hadoop.hbase.client.Scan;
67 import org.apache.hadoop.hbase.filter.BinaryComparator;
68 import org.apache.hadoop.hbase.filter.CompareFilter;
69 import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
70 import org.apache.hadoop.hbase.filter.Filter;
71 import org.apache.hadoop.hbase.filter.FilterAllFilter;
72 import org.apache.hadoop.hbase.filter.FilterList;
73 import org.apache.hadoop.hbase.filter.PageFilter;
74 import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
75 import org.apache.hadoop.hbase.filter.WhileMatchFilter;
76 import org.apache.hadoop.hbase.io.compress.Compression;
77 import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
78 import org.apache.hadoop.hbase.io.hfile.RandomDistribution;
79 import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
80 import org.apache.hadoop.hbase.regionserver.BloomType;
81 import org.apache.hadoop.hbase.trace.SpanReceiverHost;
82 import org.apache.hadoop.hbase.util.*;
83 import org.apache.hadoop.io.LongWritable;
84 import org.apache.hadoop.io.Text;
85 import org.apache.hadoop.mapreduce.Job;
86 import org.apache.hadoop.mapreduce.Mapper;
87 import org.apache.hadoop.mapreduce.lib.input.NLineInputFormat;
88 import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
89 import org.apache.hadoop.mapreduce.lib.reduce.LongSumReducer;
90 import org.apache.hadoop.util.Tool;
91 import org.apache.hadoop.util.ToolRunner;
92 import org.cloudera.htrace.Sampler;
93 import org.cloudera.htrace.Trace;
94 import org.cloudera.htrace.TraceScope;
95 import org.cloudera.htrace.impl.ProbabilitySampler;
96 import org.codehaus.jackson.map.ObjectMapper;
97
98 import com.yammer.metrics.core.Histogram;
99 import com.yammer.metrics.stats.UniformSample;
100 import com.yammer.metrics.stats.Snapshot;
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119 public class PerformanceEvaluation extends Configured implements Tool {
120 private static final Log LOG = LogFactory.getLog(PerformanceEvaluation.class.getName());
121 private static final ObjectMapper MAPPER = new ObjectMapper();
122 static {
123 MAPPER.configure(SORT_PROPERTIES_ALPHABETICALLY, true);
124 }
125
126 public static final String TABLE_NAME = "TestTable";
127 public static final byte[] FAMILY_NAME = Bytes.toBytes("info");
128 public static final byte [] COLUMN_ZERO = Bytes.toBytes("" + 0);
129 public static final byte [] QUALIFIER_NAME = COLUMN_ZERO;
130 public static final int DEFAULT_VALUE_LENGTH = 1000;
131 public static final int ROW_LENGTH = 26;
132
133 private static final int ONE_GB = 1024 * 1024 * 1000;
134 private static final int DEFAULT_ROWS_PER_GB = ONE_GB / DEFAULT_VALUE_LENGTH;
135
136 private static final int TAG_LENGTH = 256;
137 private static final DecimalFormat FMT = new DecimalFormat("0.##");
138 private static final MathContext CXT = MathContext.DECIMAL64;
139 private static final BigDecimal MS_PER_SEC = BigDecimal.valueOf(1000);
140 private static final BigDecimal BYTES_PER_MB = BigDecimal.valueOf(1024 * 1024);
141 private static final TestOptions DEFAULT_OPTS = new TestOptions();
142
143 private static Map<String, CmdDescriptor> COMMANDS = new TreeMap<String, CmdDescriptor>();
144 private static final Path PERF_EVAL_DIR = new Path("performance_evaluation");
145
146 static {
147 addCommandDescriptor(RandomReadTest.class, "randomRead",
148 "Run random read test");
149 addCommandDescriptor(RandomSeekScanTest.class, "randomSeekScan",
150 "Run random seek and scan 100 test");
151 addCommandDescriptor(RandomScanWithRange10Test.class, "scanRange10",
152 "Run random seek scan with both start and stop row (max 10 rows)");
153 addCommandDescriptor(RandomScanWithRange100Test.class, "scanRange100",
154 "Run random seek scan with both start and stop row (max 100 rows)");
155 addCommandDescriptor(RandomScanWithRange1000Test.class, "scanRange1000",
156 "Run random seek scan with both start and stop row (max 1000 rows)");
157 addCommandDescriptor(RandomScanWithRange10000Test.class, "scanRange10000",
158 "Run random seek scan with both start and stop row (max 10000 rows)");
159 addCommandDescriptor(RandomWriteTest.class, "randomWrite",
160 "Run random write test");
161 addCommandDescriptor(SequentialReadTest.class, "sequentialRead",
162 "Run sequential read test");
163 addCommandDescriptor(SequentialWriteTest.class, "sequentialWrite",
164 "Run sequential write test");
165 addCommandDescriptor(ScanTest.class, "scan",
166 "Run scan test (read every row)");
167 addCommandDescriptor(FilteredScanTest.class, "filterScan",
168 "Run scan test using a filter to find a specific row based on it's value " +
169 "(make sure to use --rows=20)");
170 addCommandDescriptor(IncrementTest.class, "increment",
171 "Increment on each row; clients overlap on keyspace so some concurrent operations");
172 addCommandDescriptor(AppendTest.class, "append",
173 "Append on each row; clients overlap on keyspace so some concurrent operations");
174 addCommandDescriptor(CheckAndMutateTest.class, "checkAndMutate",
175 "CheckAndMutate on each row; clients overlap on keyspace so some concurrent operations");
176 addCommandDescriptor(CheckAndPutTest.class, "checkAndPut",
177 "CheckAndPut on each row; clients overlap on keyspace so some concurrent operations");
178 addCommandDescriptor(CheckAndDeleteTest.class, "checkAndDelete",
179 "CheckAndDelete on each row; clients overlap on keyspace so some concurrent operations");
180 }
181
182
183
184
185
186 protected static enum Counter {
187
188 ELAPSED_TIME,
189
190 ROWS
191 }
192
193 protected static class RunResult implements Comparable<RunResult> {
194 public RunResult(long duration, Histogram hist) {
195 this.duration = duration;
196 this.hist = hist;
197 }
198
199 public final long duration;
200 public final Histogram hist;
201
202 @Override
203 public String toString() {
204 return Long.toString(duration);
205 }
206
207 @Override public int compareTo(RunResult o) {
208 if (this.duration == o.duration) {
209 return 0;
210 }
211 return this.duration > o.duration ? 1 : -1;
212 }
213 }
214
215
216
217
218
219 public PerformanceEvaluation(final Configuration conf) {
220 super(conf);
221 }
222
223 protected static void addCommandDescriptor(Class<? extends Test> cmdClass,
224 String name, String description) {
225 CmdDescriptor cmdDescriptor = new CmdDescriptor(cmdClass, name, description);
226 COMMANDS.put(name, cmdDescriptor);
227 }
228
229
230
231
232 interface Status {
233
234
235
236
237
238 void setStatus(final String msg) throws IOException;
239 }
240
241
242
243
244 public static class EvaluationMapTask
245 extends Mapper<LongWritable, Text, LongWritable, LongWritable> {
246
247
248 public final static String CMD_KEY = "EvaluationMapTask.command";
249
250 public static final String PE_KEY = "EvaluationMapTask.performanceEvalImpl";
251
252 private Class<? extends Test> cmd;
253
254 @Override
255 protected void setup(Context context) throws IOException, InterruptedException {
256 this.cmd = forName(context.getConfiguration().get(CMD_KEY), Test.class);
257
258
259
260 Class<? extends PerformanceEvaluation> peClass =
261 forName(context.getConfiguration().get(PE_KEY), PerformanceEvaluation.class);
262 try {
263 peClass.getConstructor(Configuration.class).newInstance(context.getConfiguration());
264 } catch (Exception e) {
265 throw new IllegalStateException("Could not instantiate PE instance", e);
266 }
267 }
268
269 private <Type> Class<? extends Type> forName(String className, Class<Type> type) {
270 try {
271 return Class.forName(className).asSubclass(type);
272 } catch (ClassNotFoundException e) {
273 throw new IllegalStateException("Could not find class for name: " + className, e);
274 }
275 }
276
277 @Override
278 protected void map(LongWritable key, Text value, final Context context)
279 throws IOException, InterruptedException {
280
281 Status status = new Status() {
282 @Override
283 public void setStatus(String msg) {
284 context.setStatus(msg);
285 }
286 };
287
288 ObjectMapper mapper = new ObjectMapper();
289 TestOptions opts = mapper.readValue(value.toString(), TestOptions.class);
290 Configuration conf = HBaseConfiguration.create(context.getConfiguration());
291 final HConnection con = HConnectionManager.createConnection(conf);
292
293
294 RunResult result = PerformanceEvaluation.runOneClient(this.cmd, conf, con, opts, status);
295
296
297 context.getCounter(Counter.ELAPSED_TIME).increment(result.duration);
298 context.getCounter(Counter.ROWS).increment(opts.perClientRunRows);
299 context.write(new LongWritable(opts.startRow), new LongWritable(result.duration));
300 context.progress();
301 }
302 }
303
304
305
306
307
308 static boolean checkTable(HBaseAdmin admin, TestOptions opts) throws IOException {
309 TableName tableName = TableName.valueOf(opts.tableName);
310 boolean needsDelete = false, exists = admin.tableExists(tableName);
311 boolean isReadCmd = opts.cmdName.toLowerCase().contains("read")
312 || opts.cmdName.toLowerCase().contains("scan");
313 if (!exists && isReadCmd) {
314 throw new IllegalStateException(
315 "Must specify an existing table for read commands. Run a write command first.");
316 }
317 HTableDescriptor desc =
318 exists ? admin.getTableDescriptor(TableName.valueOf(opts.tableName)) : null;
319 byte[][] splits = getSplits(opts);
320
321
322
323 if ((exists && opts.presplitRegions != DEFAULT_OPTS.presplitRegions)
324 || (!isReadCmd && desc != null && desc.getRegionSplitPolicyClassName() != opts.splitPolicy)) {
325 needsDelete = true;
326
327 LOG.debug(Objects.toStringHelper("needsDelete")
328 .add("needsDelete", needsDelete)
329 .add("isReadCmd", isReadCmd)
330 .add("exists", exists)
331 .add("desc", desc)
332 .add("presplit", opts.presplitRegions)
333 .add("splitPolicy", opts.splitPolicy));
334 }
335
336
337 if (needsDelete) {
338 if (admin.isTableEnabled(tableName)) {
339 admin.disableTable(tableName);
340 }
341 admin.deleteTable(tableName);
342 }
343
344
345 if (!exists || needsDelete) {
346 desc = getTableDescriptor(opts);
347 if (splits != null) {
348 if (LOG.isDebugEnabled()) {
349 for (int i = 0; i < splits.length; i++) {
350 LOG.debug(" split " + i + ": " + Bytes.toStringBinary(splits[i]));
351 }
352 }
353 }
354 admin.createTable(desc, splits);
355 LOG.info("Table " + desc + " created");
356 }
357 return admin.tableExists(tableName);
358 }
359
360
361
362
363 protected static HTableDescriptor getTableDescriptor(TestOptions opts) {
364 HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(opts.tableName));
365 HColumnDescriptor family = new HColumnDescriptor(FAMILY_NAME);
366 family.setDataBlockEncoding(opts.blockEncoding);
367 family.setCompressionType(opts.compression);
368 family.setBloomFilterType(opts.bloomType);
369 if (opts.inMemoryCF) {
370 family.setInMemory(true);
371 }
372 desc.addFamily(family);
373 if (opts.splitPolicy != DEFAULT_OPTS.splitPolicy) {
374 desc.setRegionSplitPolicyClassName(opts.splitPolicy);
375 }
376 return desc;
377 }
378
379
380
381
382 protected static byte[][] getSplits(TestOptions opts) {
383 if (opts.presplitRegions == DEFAULT_OPTS.presplitRegions)
384 return null;
385
386 int numSplitPoints = opts.presplitRegions - 1;
387 byte[][] splits = new byte[numSplitPoints][];
388 int jump = opts.totalRows / opts.presplitRegions;
389 for (int i = 0; i < numSplitPoints; i++) {
390 int rowkey = jump * (1 + i);
391 splits[i] = format(rowkey);
392 }
393 return splits;
394 }
395
396
397
398
399 static RunResult[] doLocalClients(final TestOptions opts, final Configuration conf)
400 throws IOException, InterruptedException {
401 final Class<? extends Test> cmd = determineCommandClass(opts.cmdName);
402 assert cmd != null;
403 @SuppressWarnings("unchecked")
404 Future<RunResult>[] threads = new Future[opts.numClientThreads];
405 RunResult[] results = new RunResult[opts.numClientThreads];
406 ExecutorService pool = Executors.newFixedThreadPool(opts.numClientThreads,
407 new ThreadFactoryBuilder().setNameFormat("TestClient-%s").build());
408 final HConnection con = HConnectionManager.createConnection(conf);
409 for (int i = 0; i < threads.length; i++) {
410 final int index = i;
411 threads[i] = pool.submit(new Callable<RunResult>() {
412 @Override
413 public RunResult call() throws Exception {
414 TestOptions threadOpts = new TestOptions(opts);
415 if (threadOpts.startRow == 0) threadOpts.startRow = index * threadOpts.perClientRunRows;
416 RunResult run = runOneClient(cmd, conf, con, threadOpts, new Status() {
417 @Override
418 public void setStatus(final String msg) throws IOException {
419 LOG.info(msg);
420 }
421 });
422 LOG.info("Finished " + Thread.currentThread().getName() + " in " + run.duration +
423 "ms over " + threadOpts.perClientRunRows + " rows");
424 return run;
425 }
426 });
427 }
428 pool.shutdown();
429
430 for (int i = 0; i < threads.length; i++) {
431 try {
432 results[i] = threads[i].get();
433 } catch (ExecutionException e) {
434 throw new IOException(e.getCause());
435 }
436 }
437 final String test = cmd.getSimpleName();
438 LOG.info("[" + test + "] Summary of timings (ms): "
439 + Arrays.toString(results));
440 Arrays.sort(results);
441 long total = 0;
442 for (RunResult result : results) {
443 total += result.duration;
444 }
445 LOG.info("[" + test + "]"
446 + "\tMin: " + results[0] + "ms"
447 + "\tMax: " + results[results.length - 1] + "ms"
448 + "\tAvg: " + (total / results.length) + "ms");
449
450 con.close();
451
452 return results;
453 }
454
455
456
457
458
459
460
461
462 static Job doMapReduce(TestOptions opts, final Configuration conf)
463 throws IOException, InterruptedException, ClassNotFoundException {
464 final Class<? extends Test> cmd = determineCommandClass(opts.cmdName);
465 assert cmd != null;
466 Path inputDir = writeInputFile(conf, opts);
467 conf.set(EvaluationMapTask.CMD_KEY, cmd.getName());
468 conf.set(EvaluationMapTask.PE_KEY, PerformanceEvaluation.class.getName());
469 Job job = Job.getInstance(conf);
470 job.setJarByClass(PerformanceEvaluation.class);
471 job.setJobName("HBase Performance Evaluation - " + opts.cmdName);
472
473 job.setInputFormatClass(NLineInputFormat.class);
474 NLineInputFormat.setInputPaths(job, inputDir);
475
476 NLineInputFormat.setNumLinesPerSplit(job, 1);
477
478 job.setOutputKeyClass(LongWritable.class);
479 job.setOutputValueClass(LongWritable.class);
480
481 job.setMapperClass(EvaluationMapTask.class);
482 job.setReducerClass(LongSumReducer.class);
483
484 job.setNumReduceTasks(1);
485
486 job.setOutputFormatClass(TextOutputFormat.class);
487 TextOutputFormat.setOutputPath(job, new Path(inputDir.getParent(), "outputs"));
488
489 TableMapReduceUtil.addDependencyJars(job);
490 TableMapReduceUtil.addDependencyJars(job.getConfiguration(),
491 Histogram.class,
492 ObjectMapper.class);
493
494 TableMapReduceUtil.initCredentials(job);
495
496 job.waitForCompletion(true);
497 return job;
498 }
499
500
501
502
503
504
505
506 private static Path writeInputFile(final Configuration c, final TestOptions opts) throws IOException {
507 SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
508 Path jobdir = new Path(PERF_EVAL_DIR, formatter.format(new Date()));
509 Path inputDir = new Path(jobdir, "inputs");
510
511 FileSystem fs = FileSystem.get(c);
512 fs.mkdirs(inputDir);
513
514 Path inputFile = new Path(inputDir, "input.txt");
515 PrintStream out = new PrintStream(fs.create(inputFile));
516
517 Map<Integer, String> m = new TreeMap<Integer, String>();
518 Hash h = MurmurHash.getInstance();
519 int perClientRows = (opts.totalRows / opts.numClientThreads);
520 try {
521 for (int i = 0; i < 10; i++) {
522 for (int j = 0; j < opts.numClientThreads; j++) {
523 TestOptions next = new TestOptions(opts);
524 next.startRow = (j * perClientRows) + (i * (perClientRows/10));
525 next.perClientRunRows = perClientRows / 10;
526 String s = MAPPER.writeValueAsString(next);
527 LOG.info("maptask input=" + s);
528 int hash = h.hash(Bytes.toBytes(s));
529 m.put(hash, s);
530 }
531 }
532 for (Map.Entry<Integer, String> e: m.entrySet()) {
533 out.println(e.getValue());
534 }
535 } finally {
536 out.close();
537 }
538 return inputDir;
539 }
540
541
542
543
544 static class CmdDescriptor {
545 private Class<? extends Test> cmdClass;
546 private String name;
547 private String description;
548
549 CmdDescriptor(Class<? extends Test> cmdClass, String name, String description) {
550 this.cmdClass = cmdClass;
551 this.name = name;
552 this.description = description;
553 }
554
555 public Class<? extends Test> getCmdClass() {
556 return cmdClass;
557 }
558
559 public String getName() {
560 return name;
561 }
562
563 public String getDescription() {
564 return description;
565 }
566 }
567
568
569
570
571
572
573
574
575 static class TestOptions {
576 String cmdName = null;
577 boolean nomapred = false;
578 boolean filterAll = false;
579 int startRow = 0;
580 float size = 1.0f;
581 int perClientRunRows = DEFAULT_ROWS_PER_GB;
582 int numClientThreads = 1;
583 int totalRows = DEFAULT_ROWS_PER_GB;
584 float sampleRate = 1.0f;
585 double traceRate = 0.0;
586 String tableName = TABLE_NAME;
587 boolean flushCommits = true;
588 boolean writeToWAL = true;
589 boolean autoFlush = false;
590 boolean oneCon = false;
591 boolean useTags = false;
592 int noOfTags = 1;
593 boolean reportLatency = false;
594 int multiGet = 0;
595 int randomSleep = 0;
596 boolean inMemoryCF = false;
597 int presplitRegions = 0;
598 String splitPolicy = null;
599 Compression.Algorithm compression = Compression.Algorithm.NONE;
600 BloomType bloomType = BloomType.ROW;
601 DataBlockEncoding blockEncoding = DataBlockEncoding.NONE;
602 boolean valueRandom = false;
603 boolean valueZipf = false;
604 int valueSize = DEFAULT_VALUE_LENGTH;
605 int period = (this.perClientRunRows / 10) == 0? perClientRunRows: perClientRunRows / 10;
606 int columns = 1;
607 int caching = 30;
608 boolean addColumns = true;
609
610 public TestOptions() {}
611
612
613
614
615
616 public TestOptions(TestOptions that) {
617 this.cmdName = that.cmdName;
618 this.nomapred = that.nomapred;
619 this.startRow = that.startRow;
620 this.size = that.size;
621 this.perClientRunRows = that.perClientRunRows;
622 this.numClientThreads = that.numClientThreads;
623 this.totalRows = that.totalRows;
624 this.sampleRate = that.sampleRate;
625 this.traceRate = that.traceRate;
626 this.tableName = that.tableName;
627 this.flushCommits = that.flushCommits;
628 this.writeToWAL = that.writeToWAL;
629 this.autoFlush = that.autoFlush;
630 this.oneCon = that.oneCon;
631 this.useTags = that.useTags;
632 this.noOfTags = that.noOfTags;
633 this.reportLatency = that.reportLatency;
634 this.multiGet = that.multiGet;
635 this.inMemoryCF = that.inMemoryCF;
636 this.presplitRegions = that.presplitRegions;
637 this.splitPolicy = that.splitPolicy;
638 this.compression = that.compression;
639 this.blockEncoding = that.blockEncoding;
640 this.filterAll = that.filterAll;
641 this.bloomType = that.bloomType;
642 this.valueRandom = that.valueRandom;
643 this.valueZipf = that.valueZipf;
644 this.valueSize = that.valueSize;
645 this.period = that.period;
646 this.randomSleep = that.randomSleep;
647 this.addColumns = that.addColumns;
648 this.columns = that.columns;
649 this.caching = that.caching;
650 }
651
652 public int getCaching() {
653 return this.caching;
654 }
655
656 public void setCaching(final int caching) {
657 this.caching = caching;
658 }
659
660 public int getColumns() {
661 return this.columns;
662 }
663
664 public void setColumns(final int columns) {
665 this.columns = columns;
666 }
667
668 public boolean isValueZipf() {
669 return valueZipf;
670 }
671
672 public void setValueZipf(boolean valueZipf) {
673 this.valueZipf = valueZipf;
674 }
675
676 public String getCmdName() {
677 return cmdName;
678 }
679
680 public void setCmdName(String cmdName) {
681 this.cmdName = cmdName;
682 }
683
684 public int getRandomSleep() {
685 return randomSleep;
686 }
687
688 public void setRandomSleep(int randomSleep) {
689 this.randomSleep = randomSleep;
690 }
691
692 public String getSplitPolicy() {
693 return splitPolicy;
694 }
695
696 public void setSplitPolicy(String splitPolicy) {
697 this.splitPolicy = splitPolicy;
698 }
699
700 public void setNomapred(boolean nomapred) {
701 this.nomapred = nomapred;
702 }
703
704 public void setFilterAll(boolean filterAll) {
705 this.filterAll = filterAll;
706 }
707
708 public void setStartRow(int startRow) {
709 this.startRow = startRow;
710 }
711
712 public void setSize(float size) {
713 this.size = size;
714 }
715
716 public void setPerClientRunRows(int perClientRunRows) {
717 this.perClientRunRows = perClientRunRows;
718 }
719
720 public void setNumClientThreads(int numClientThreads) {
721 this.numClientThreads = numClientThreads;
722 }
723
724 public void setTotalRows(int totalRows) {
725 this.totalRows = totalRows;
726 }
727
728 public void setSampleRate(float sampleRate) {
729 this.sampleRate = sampleRate;
730 }
731
732 public void setTraceRate(double traceRate) {
733 this.traceRate = traceRate;
734 }
735
736 public void setTableName(String tableName) {
737 this.tableName = tableName;
738 }
739
740 public void setFlushCommits(boolean flushCommits) {
741 this.flushCommits = flushCommits;
742 }
743
744 public void setWriteToWAL(boolean writeToWAL) {
745 this.writeToWAL = writeToWAL;
746 }
747
748 public void setAutoFlush(boolean autoFlush) {
749 this.autoFlush = autoFlush;
750 }
751
752 public void setOneCon(boolean oneCon) {
753 this.oneCon = oneCon;
754 }
755
756 public void setUseTags(boolean useTags) {
757 this.useTags = useTags;
758 }
759
760 public void setNoOfTags(int noOfTags) {
761 this.noOfTags = noOfTags;
762 }
763
764 public void setReportLatency(boolean reportLatency) {
765 this.reportLatency = reportLatency;
766 }
767
768 public void setMultiGet(int multiGet) {
769 this.multiGet = multiGet;
770 }
771
772 public void setInMemoryCF(boolean inMemoryCF) {
773 this.inMemoryCF = inMemoryCF;
774 }
775
776 public void setPresplitRegions(int presplitRegions) {
777 this.presplitRegions = presplitRegions;
778 }
779
780 public void setCompression(Compression.Algorithm compression) {
781 this.compression = compression;
782 }
783
784 public void setBloomType(BloomType bloomType) {
785 this.bloomType = bloomType;
786 }
787
788 public void setBlockEncoding(DataBlockEncoding blockEncoding) {
789 this.blockEncoding = blockEncoding;
790 }
791
792 public void setValueRandom(boolean valueRandom) {
793 this.valueRandom = valueRandom;
794 }
795
796 public void setValueSize(int valueSize) {
797 this.valueSize = valueSize;
798 }
799
800 public void setPeriod(int period) {
801 this.period = period;
802 }
803
804 public boolean isNomapred() {
805 return nomapred;
806 }
807
808 public boolean isFilterAll() {
809 return filterAll;
810 }
811
812 public int getStartRow() {
813 return startRow;
814 }
815
816 public float getSize() {
817 return size;
818 }
819
820 public int getPerClientRunRows() {
821 return perClientRunRows;
822 }
823
824 public int getNumClientThreads() {
825 return numClientThreads;
826 }
827
828 public int getTotalRows() {
829 return totalRows;
830 }
831
832 public float getSampleRate() {
833 return sampleRate;
834 }
835
836 public double getTraceRate() {
837 return traceRate;
838 }
839
840 public String getTableName() {
841 return tableName;
842 }
843
844 public boolean isFlushCommits() {
845 return flushCommits;
846 }
847
848 public boolean isWriteToWAL() {
849 return writeToWAL;
850 }
851
852 public boolean isAutoFlush() {
853 return autoFlush;
854 }
855
856 public boolean isUseTags() {
857 return useTags;
858 }
859
860 public int getNoOfTags() {
861 return noOfTags;
862 }
863
864 public boolean isReportLatency() {
865 return reportLatency;
866 }
867
868 public int getMultiGet() {
869 return multiGet;
870 }
871
872 public boolean isInMemoryCF() {
873 return inMemoryCF;
874 }
875
876 public int getPresplitRegions() {
877 return presplitRegions;
878 }
879
880 public Compression.Algorithm getCompression() {
881 return compression;
882 }
883
884 public DataBlockEncoding getBlockEncoding() {
885 return blockEncoding;
886 }
887
888 public boolean isValueRandom() {
889 return valueRandom;
890 }
891
892 public int getValueSize() {
893 return valueSize;
894 }
895
896 public int getPeriod() {
897 return period;
898 }
899
900 public BloomType getBloomType() {
901 return bloomType;
902 }
903
904 public boolean isOneCon() {
905 return oneCon;
906 }
907
908 public boolean getAddColumns() {
909 return addColumns;
910 }
911
912 public void setAddColumns(boolean addColumns) {
913 this.addColumns = addColumns;
914 }
915 }
916
917
918
919
920
921 static abstract class Test {
922
923
924 private static final Random randomSeed = new Random(System.currentTimeMillis());
925
926 private static long nextRandomSeed() {
927 return randomSeed.nextLong();
928 }
929 private final int everyN;
930
931 protected final Random rand = new Random(nextRandomSeed());
932 protected final Configuration conf;
933 protected final TestOptions opts;
934
935 private final Status status;
936 private final Sampler<?> traceSampler;
937 private final SpanReceiverHost receiverHost;
938 protected HConnection connection;
939
940 private String testName;
941 private Histogram latency;
942 private Histogram valueSize;
943 private RandomDistribution.Zipf zipf;
944
945
946
947
948
949 Test(final HConnection con, final TestOptions options, final Status status) {
950 this.connection = con;
951 this.conf = con == null ? HBaseConfiguration.create() : this.connection.getConfiguration();
952 this.opts = options;
953 this.status = status;
954 this.testName = this.getClass().getSimpleName();
955 receiverHost = SpanReceiverHost.getInstance(conf);
956 if (options.traceRate >= 1.0) {
957 this.traceSampler = Sampler.ALWAYS;
958 } else if (options.traceRate > 0.0) {
959 this.traceSampler = new ProbabilitySampler(options.traceRate);
960 } else {
961 this.traceSampler = Sampler.NEVER;
962 }
963 everyN = (int) (opts.totalRows / (opts.totalRows * opts.sampleRate));
964 if (options.isValueZipf()) {
965 this.zipf = new RandomDistribution.Zipf(this.rand, 1, options.getValueSize(), 1.1);
966 }
967 LOG.info("Sampling 1 every " + everyN + " out of " + opts.perClientRunRows + " total rows.");
968 }
969
970 int getValueLength(final Random r) {
971 if (this.opts.isValueRandom()) return Math.abs(r.nextInt() % opts.valueSize);
972 else if (this.opts.isValueZipf()) return Math.abs(this.zipf.nextInt());
973 else return opts.valueSize;
974 }
975
976 void updateValueSize(final Result [] rs) throws IOException {
977 if (rs == null || !isRandomValueSize()) return;
978 for (Result r: rs) updateValueSize(r);
979 }
980
981 void updateValueSize(final Result r) throws IOException {
982 if (r == null || !isRandomValueSize()) return;
983 int size = 0;
984 for (CellScanner scanner = r.cellScanner(); scanner.advance();) {
985 size += scanner.current().getValueLength();
986 }
987 updateValueSize(size);
988 }
989
990 void updateValueSize(final int valueSize) {
991 if (!isRandomValueSize()) return;
992 this.valueSize.update(valueSize);
993 }
994
995 String generateStatus(final int sr, final int i, final int lr) {
996 return sr + "/" + i + "/" + lr + ", latency " + getShortLatencyReport() +
997 (!isRandomValueSize()? "": ", value size " + getShortValueSizeReport());
998 }
999
1000 boolean isRandomValueSize() {
1001 return opts.valueRandom;
1002 }
1003
1004 protected int getReportingPeriod() {
1005 return opts.period;
1006 }
1007
1008
1009
1010
1011 public Histogram getLatency() {
1012 return latency;
1013 }
1014
1015 void testSetup() throws IOException {
1016 if (!opts.oneCon) {
1017 this.connection = HConnectionManager.createConnection(conf);
1018 }
1019 onStartup();
1020 latency = YammerHistogramUtils.newHistogram(new UniformSample(1024 * 500));
1021 valueSize = YammerHistogramUtils.newHistogram(new UniformSample(1024 * 500));
1022 }
1023
1024 abstract void onStartup() throws IOException;
1025
1026 void testTakedown() throws IOException {
1027 reportLatency();
1028 reportValueSize();
1029 onTakedown();
1030 if (!opts.oneCon) {
1031 connection.close();
1032 }
1033 receiverHost.closeReceivers();
1034 }
1035
1036 abstract void onTakedown() throws IOException;
1037
1038
1039
1040
1041
1042
1043 long test() throws IOException, InterruptedException {
1044 testSetup();
1045 LOG.info("Timed test starting in thread " + Thread.currentThread().getName());
1046 final long startTime = System.nanoTime();
1047 try {
1048 testTimed();
1049 } finally {
1050 testTakedown();
1051 }
1052 return (System.nanoTime() - startTime) / 1000000;
1053 }
1054
1055 int getStartRow() {
1056 return opts.startRow;
1057 }
1058
1059 int getLastRow() {
1060 return getStartRow() + opts.perClientRunRows;
1061 }
1062
1063
1064
1065
1066 void testTimed() throws IOException, InterruptedException {
1067 int startRow = getStartRow();
1068 int lastRow = getLastRow();
1069
1070 for (int i = startRow; i < lastRow; i++) {
1071 if (i % everyN != 0) continue;
1072 long startTime = System.nanoTime();
1073 TraceScope scope = Trace.startSpan("test row", traceSampler);
1074 try {
1075 testRow(i);
1076 } finally {
1077 scope.close();
1078 }
1079 latency.update((System.nanoTime() - startTime) / 1000);
1080 if (status != null && i > 0 && (i % getReportingPeriod()) == 0) {
1081 status.setStatus(generateStatus(startRow, i, lastRow));
1082 }
1083 }
1084 }
1085
1086
1087
1088
1089
1090 private void reportLatency() throws IOException {
1091 status.setStatus(testName + " latency log (microseconds), on " +
1092 latency.count() + " measures");
1093 reportHistogram(this.latency);
1094 }
1095
1096 private void reportValueSize() throws IOException {
1097 status.setStatus(testName + " valueSize after " +
1098 valueSize.count() + " measures");
1099 reportHistogram(this.valueSize);
1100 }
1101
1102 private void reportHistogram(final Histogram h) throws IOException {
1103 Snapshot sn = h.getSnapshot();
1104 status.setStatus(testName + " Min = " + h.min());
1105 status.setStatus(testName + " Avg = " + h.mean());
1106 status.setStatus(testName + " StdDev = " + h.stdDev());
1107 status.setStatus(testName + " 50th = " + sn.getMedian());
1108 status.setStatus(testName + " 75th = " + sn.get75thPercentile());
1109 status.setStatus(testName + " 95th = " + sn.get95thPercentile());
1110 status.setStatus(testName + " 99th = " + sn.get99thPercentile());
1111 status.setStatus(testName + " 99.9th = " + sn.get999thPercentile());
1112 status.setStatus(testName + " 99.99th = " + sn.getValue(0.9999));
1113 status.setStatus(testName + " 99.999th = " + sn.getValue(0.99999));
1114 status.setStatus(testName + " Max = " + h.max());
1115 }
1116
1117
1118
1119
1120 public String getShortLatencyReport() {
1121 return YammerHistogramUtils.getShortHistogramReport(this.latency);
1122 }
1123
1124
1125
1126
1127 public String getShortValueSizeReport() {
1128 return YammerHistogramUtils.getShortHistogramReport(this.valueSize);
1129 }
1130
1131
1132
1133
1134
1135 abstract void testRow(final int i) throws IOException, InterruptedException;
1136 }
1137
1138 static abstract class TableTest extends Test {
1139 protected HTableInterface table;
1140
1141 TableTest(HConnection con, TestOptions options, Status status) {
1142 super(con, options, status);
1143 }
1144
1145 @Override
1146 void onStartup() throws IOException {
1147 this.table = connection.getTable(TableName.valueOf(opts.tableName));
1148 }
1149
1150 @Override
1151 void onTakedown() throws IOException {
1152 table.close();
1153 }
1154 }
1155
1156 static class RandomSeekScanTest extends TableTest {
1157 RandomSeekScanTest(HConnection con, TestOptions options, Status status) {
1158 super(con, options, status);
1159 }
1160
1161 @Override
1162 void testRow(final int i) throws IOException {
1163 Scan scan = new Scan(getRandomRow(this.rand, opts.totalRows));
1164 scan.setCaching(opts.caching);
1165 FilterList list = new FilterList();
1166 if (opts.addColumns) {
1167 scan.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1168 } else {
1169 scan.addFamily(FAMILY_NAME);
1170 }
1171 if (opts.filterAll) {
1172 list.addFilter(new FilterAllFilter());
1173 }
1174 list.addFilter(new WhileMatchFilter(new PageFilter(120)));
1175 scan.setFilter(list);
1176 ResultScanner s = this.table.getScanner(scan);
1177 for (Result rr; (rr = s.next()) != null;) {
1178 updateValueSize(rr);
1179 }
1180 s.close();
1181 }
1182
1183 @Override
1184 protected int getReportingPeriod() {
1185 int period = opts.perClientRunRows / 100;
1186 return period == 0 ? opts.perClientRunRows : period;
1187 }
1188
1189 }
1190
1191 static abstract class RandomScanWithRangeTest extends TableTest {
1192 RandomScanWithRangeTest(HConnection con, TestOptions options, Status status) {
1193 super(con, options, status);
1194 }
1195
1196 @Override
1197 void testRow(final int i) throws IOException {
1198 Pair<byte[], byte[]> startAndStopRow = getStartAndStopRow();
1199 Scan scan = new Scan(startAndStopRow.getFirst(), startAndStopRow.getSecond());
1200 scan.setCaching(opts.caching);
1201 if (opts.filterAll) {
1202 scan.setFilter(new FilterAllFilter());
1203 }
1204 if (opts.addColumns) {
1205 scan.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1206 } else {
1207 scan.addFamily(FAMILY_NAME);
1208 }
1209 Result r = null;
1210 int count = 0;
1211 ResultScanner s = this.table.getScanner(scan);
1212 for (; (r = s.next()) != null;) {
1213 updateValueSize(r);
1214 count++;
1215 }
1216 if (i % 100 == 0) {
1217 LOG.info(String.format("Scan for key range %s - %s returned %s rows",
1218 Bytes.toString(startAndStopRow.getFirst()),
1219 Bytes.toString(startAndStopRow.getSecond()), count));
1220 }
1221
1222 s.close();
1223 }
1224
1225 protected abstract Pair<byte[],byte[]> getStartAndStopRow();
1226
1227 protected Pair<byte[], byte[]> generateStartAndStopRows(int maxRange) {
1228 int start = this.rand.nextInt(Integer.MAX_VALUE) % opts.totalRows;
1229 int stop = start + maxRange;
1230 return new Pair<byte[],byte[]>(format(start), format(stop));
1231 }
1232
1233 @Override
1234 protected int getReportingPeriod() {
1235 int period = opts.perClientRunRows / 100;
1236 return period == 0? opts.perClientRunRows: period;
1237 }
1238 }
1239
1240 static class RandomScanWithRange10Test extends RandomScanWithRangeTest {
1241 RandomScanWithRange10Test(HConnection con, TestOptions options, Status status) {
1242 super(con, options, status);
1243 }
1244
1245 @Override
1246 protected Pair<byte[], byte[]> getStartAndStopRow() {
1247 return generateStartAndStopRows(10);
1248 }
1249 }
1250
1251 static class RandomScanWithRange100Test extends RandomScanWithRangeTest {
1252 RandomScanWithRange100Test(HConnection con, TestOptions options, Status status) {
1253 super(con, options, status);
1254 }
1255
1256 @Override
1257 protected Pair<byte[], byte[]> getStartAndStopRow() {
1258 return generateStartAndStopRows(100);
1259 }
1260 }
1261
1262 static class RandomScanWithRange1000Test extends RandomScanWithRangeTest {
1263 RandomScanWithRange1000Test(HConnection con, TestOptions options, Status status) {
1264 super(con, options, status);
1265 }
1266
1267 @Override
1268 protected Pair<byte[], byte[]> getStartAndStopRow() {
1269 return generateStartAndStopRows(1000);
1270 }
1271 }
1272
1273 static class RandomScanWithRange10000Test extends RandomScanWithRangeTest {
1274 RandomScanWithRange10000Test(HConnection con, TestOptions options, Status status) {
1275 super(con, options, status);
1276 }
1277
1278 @Override
1279 protected Pair<byte[], byte[]> getStartAndStopRow() {
1280 return generateStartAndStopRows(10000);
1281 }
1282 }
1283
1284 static class RandomReadTest extends TableTest {
1285 private ArrayList<Get> gets;
1286 private Random rd = new Random();
1287
1288 RandomReadTest(HConnection con, TestOptions options, Status status) {
1289 super(con, options, status);
1290 if (opts.multiGet > 0) {
1291 LOG.info("MultiGet enabled. Sending GETs in batches of " + opts.multiGet + ".");
1292 this.gets = new ArrayList<Get>(opts.multiGet);
1293 }
1294 }
1295
1296 @Override
1297 void testRow(final int i) throws IOException, InterruptedException {
1298 if (opts.randomSleep > 0) {
1299 Thread.sleep(rd.nextInt(opts.randomSleep));
1300 }
1301 Get get = new Get(getRandomRow(this.rand, opts.totalRows));
1302 if (opts.addColumns) {
1303 get.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1304 } else {
1305 get.addFamily(FAMILY_NAME);
1306 }
1307 if (opts.filterAll) {
1308 get.setFilter(new FilterAllFilter());
1309 }
1310 if (LOG.isTraceEnabled()) LOG.trace(get.toString());
1311 if (opts.multiGet > 0) {
1312 this.gets.add(get);
1313 if (this.gets.size() == opts.multiGet) {
1314 Result [] rs = this.table.get(this.gets);
1315 updateValueSize(rs);
1316 this.gets.clear();
1317 }
1318 } else {
1319 updateValueSize(this.table.get(get));
1320 }
1321 }
1322
1323 @Override
1324 protected int getReportingPeriod() {
1325 int period = opts.perClientRunRows / 10;
1326 return period == 0 ? opts.perClientRunRows : period;
1327 }
1328
1329 @Override
1330 protected void testTakedown() throws IOException {
1331 if (this.gets != null && this.gets.size() > 0) {
1332 this.table.get(gets);
1333 this.gets.clear();
1334 }
1335 super.testTakedown();
1336 }
1337 }
1338
1339 static class RandomWriteTest extends TableTest {
1340 RandomWriteTest(HConnection con, TestOptions options, Status status) {
1341 super(con, options, status);
1342 }
1343
1344 @Override
1345 void testRow(final int i) throws IOException {
1346 byte[] row = getRandomRow(this.rand, opts.totalRows);
1347 Put put = new Put(row);
1348 for (int column = 0; column < opts.columns; column++) {
1349 byte [] qualifier = column == 0? COLUMN_ZERO: Bytes.toBytes("" + column);
1350 byte[] value = generateData(this.rand, getValueLength(this.rand));
1351 if (opts.useTags) {
1352 byte[] tag = generateData(this.rand, TAG_LENGTH);
1353 Tag[] tags = new Tag[opts.noOfTags];
1354 for (int n = 0; n < opts.noOfTags; n++) {
1355 Tag t = new Tag((byte) n, tag);
1356 tags[n] = t;
1357 }
1358 KeyValue kv = new KeyValue(row, FAMILY_NAME, qualifier, HConstants.LATEST_TIMESTAMP,
1359 value, tags);
1360 put.add(kv);
1361 updateValueSize(kv.getValueLength());
1362 } else {
1363 put.add(FAMILY_NAME, qualifier, value);
1364 updateValueSize(value.length);
1365 }
1366 }
1367 put.setDurability(opts.writeToWAL ? Durability.SYNC_WAL : Durability.SKIP_WAL);
1368 table.put(put);
1369 }
1370 }
1371
1372 static class ScanTest extends TableTest {
1373 private ResultScanner testScanner;
1374
1375 ScanTest(HConnection con, TestOptions options, Status status) {
1376 super(con, options, status);
1377 }
1378
1379 @Override
1380 void testTakedown() throws IOException {
1381 if (this.testScanner != null) {
1382 this.testScanner.close();
1383 }
1384 super.testTakedown();
1385 }
1386
1387
1388 @Override
1389 void testRow(final int i) throws IOException {
1390 if (this.testScanner == null) {
1391 Scan scan = new Scan(format(opts.startRow));
1392 scan.setCaching(opts.caching);
1393 if (opts.addColumns) {
1394 scan.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1395 } else {
1396 scan.addFamily(FAMILY_NAME);
1397 }
1398 if (opts.filterAll) {
1399 scan.setFilter(new FilterAllFilter());
1400 }
1401 this.testScanner = table.getScanner(scan);
1402 }
1403 Result r = testScanner.next();
1404 updateValueSize(r);
1405 }
1406 }
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416 static abstract class CASTableTest extends TableTest {
1417 private final byte [] qualifier;
1418 CASTableTest(HConnection con, TestOptions options, Status status) {
1419 super(con, options, status);
1420 qualifier = Bytes.toBytes(this.getClass().getSimpleName());
1421 }
1422
1423 byte [] getQualifier() {
1424 return this.qualifier;
1425 }
1426
1427 @Override
1428 int getStartRow() {
1429 return 0;
1430 }
1431
1432 @Override
1433 int getLastRow() {
1434 return opts.perClientRunRows;
1435 }
1436 }
1437
1438 static class IncrementTest extends CASTableTest {
1439 IncrementTest(HConnection con, TestOptions options, Status status) {
1440 super(con, options, status);
1441 }
1442
1443 @Override
1444 void testRow(final int i) throws IOException {
1445 Increment increment = new Increment(format(i));
1446 increment.addColumn(FAMILY_NAME, getQualifier(), 1l);
1447 updateValueSize(this.table.increment(increment));
1448 }
1449 }
1450
1451 static class AppendTest extends CASTableTest {
1452 AppendTest(HConnection con, TestOptions options, Status status) {
1453 super(con, options, status);
1454 }
1455
1456 @Override
1457 void testRow(final int i) throws IOException {
1458 byte [] bytes = format(i);
1459 Append append = new Append(bytes);
1460 append.add(FAMILY_NAME, getQualifier(), bytes);
1461 updateValueSize(this.table.append(append));
1462 }
1463 }
1464
1465 static class CheckAndMutateTest extends CASTableTest {
1466 CheckAndMutateTest(HConnection con, TestOptions options, Status status) {
1467 super(con, options, status);
1468 }
1469
1470 @Override
1471 void testRow(final int i) throws IOException {
1472 byte [] bytes = format(i);
1473
1474 Put put = new Put(bytes);
1475 put.add(FAMILY_NAME, getQualifier(), bytes);
1476 this.table.put(put);
1477 RowMutations mutations = new RowMutations(bytes);
1478 mutations.add(put);
1479 this.table.checkAndMutate(bytes, FAMILY_NAME, getQualifier(), CompareOp.EQUAL, bytes,
1480 mutations);
1481 }
1482 }
1483
1484 static class CheckAndPutTest extends CASTableTest {
1485 CheckAndPutTest(HConnection con, TestOptions options, Status status) {
1486 super(con, options, status);
1487 }
1488
1489 @Override
1490 void testRow(final int i) throws IOException {
1491 byte [] bytes = format(i);
1492
1493 Put put = new Put(bytes);
1494 put.add(FAMILY_NAME, getQualifier(), bytes);
1495 this.table.put(put);
1496 this.table.checkAndPut(bytes, FAMILY_NAME, getQualifier(), bytes, put);
1497 }
1498 }
1499
1500 static class CheckAndDeleteTest extends CASTableTest {
1501 CheckAndDeleteTest(HConnection con, TestOptions options, Status status) {
1502 super(con, options, status);
1503 }
1504
1505 @Override
1506 void testRow(final int i) throws IOException {
1507 byte [] bytes = format(i);
1508
1509 Put put = new Put(bytes);
1510 put.add(FAMILY_NAME, getQualifier(), bytes);
1511 this.table.put(put);
1512 Delete delete = new Delete(put.getRow());
1513 delete.deleteColumn(FAMILY_NAME, getQualifier());
1514 this.table.checkAndDelete(bytes, FAMILY_NAME, getQualifier(), bytes, delete);
1515 }
1516 }
1517
1518 static class SequentialReadTest extends TableTest {
1519 SequentialReadTest(HConnection con, TestOptions options, Status status) {
1520 super(con, options, status);
1521 }
1522
1523 @Override
1524 void testRow(final int i) throws IOException {
1525 Get get = new Get(format(i));
1526 if (opts.addColumns) {
1527 get.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1528 }
1529 if (opts.filterAll) {
1530 get.setFilter(new FilterAllFilter());
1531 }
1532 updateValueSize(table.get(get));
1533 }
1534 }
1535
1536 static class SequentialWriteTest extends TableTest {
1537 SequentialWriteTest(HConnection con, TestOptions options, Status status) {
1538 super(con, options, status);
1539 }
1540
1541 @Override
1542 void testRow(final int i) throws IOException {
1543 byte[] row = format(i);
1544 Put put = new Put(row);
1545 for (int column = 0; column < opts.columns; column++) {
1546 byte [] qualifier = column == 0? COLUMN_ZERO: Bytes.toBytes("" + column);
1547 byte[] value = generateData(this.rand, getValueLength(this.rand));
1548 if (opts.useTags) {
1549 byte[] tag = generateData(this.rand, TAG_LENGTH);
1550 Tag[] tags = new Tag[opts.noOfTags];
1551 for (int n = 0; n < opts.noOfTags; n++) {
1552 Tag t = new Tag((byte) n, tag);
1553 tags[n] = t;
1554 }
1555 KeyValue kv = new KeyValue(row, FAMILY_NAME, qualifier, HConstants.LATEST_TIMESTAMP,
1556 value, tags);
1557 put.add(kv);
1558 updateValueSize(kv.getValueLength());
1559 } else {
1560 put.add(FAMILY_NAME, qualifier, value);
1561 updateValueSize(value.length);
1562 }
1563 }
1564 put.setDurability(opts.writeToWAL ? Durability.SYNC_WAL : Durability.SKIP_WAL);
1565 table.put(put);
1566 }
1567 }
1568
1569 static class FilteredScanTest extends TableTest {
1570 protected static final Log LOG = LogFactory.getLog(FilteredScanTest.class.getName());
1571
1572 FilteredScanTest(HConnection con, TestOptions options, Status status) {
1573 super(con, options, status);
1574 }
1575
1576 @Override
1577 void testRow(int i) throws IOException {
1578 byte[] value = generateData(this.rand, getValueLength(this.rand));
1579 Scan scan = constructScan(value);
1580 ResultScanner scanner = null;
1581 try {
1582 scanner = this.table.getScanner(scan);
1583 for (Result r = null; (r = scanner.next()) != null;) {
1584 updateValueSize(r);
1585 }
1586 } finally {
1587 if (scanner != null) scanner.close();
1588 }
1589 }
1590
1591 protected Scan constructScan(byte[] valuePrefix) throws IOException {
1592 FilterList list = new FilterList();
1593 Filter filter = new SingleColumnValueFilter(
1594 FAMILY_NAME, COLUMN_ZERO, CompareFilter.CompareOp.EQUAL,
1595 new BinaryComparator(valuePrefix)
1596 );
1597 list.addFilter(filter);
1598 if(opts.filterAll) {
1599 list.addFilter(new FilterAllFilter());
1600 }
1601 Scan scan = new Scan();
1602 scan.setCaching(opts.caching);
1603 if (opts.addColumns) {
1604 scan.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1605 } else {
1606 scan.addFamily(FAMILY_NAME);
1607 }
1608 scan.setFilter(list);
1609 return scan;
1610 }
1611 }
1612
1613
1614
1615
1616
1617
1618
1619 private static String calculateMbps(int rows, long timeMs, final int valueSize, int columns) {
1620 BigDecimal rowSize = BigDecimal.valueOf(ROW_LENGTH +
1621 ((valueSize + FAMILY_NAME.length + COLUMN_ZERO.length) * columns));
1622 BigDecimal mbps = BigDecimal.valueOf(rows).multiply(rowSize, CXT)
1623 .divide(BigDecimal.valueOf(timeMs), CXT).multiply(MS_PER_SEC, CXT)
1624 .divide(BYTES_PER_MB, CXT);
1625 return FMT.format(mbps) + " MB/s";
1626 }
1627
1628
1629
1630
1631
1632
1633
1634 public static byte [] format(final int number) {
1635 byte [] b = new byte[ROW_LENGTH];
1636 int d = Math.abs(number);
1637 for (int i = b.length - 1; i >= 0; i--) {
1638 b[i] = (byte)((d % 10) + '0');
1639 d /= 10;
1640 }
1641 return b;
1642 }
1643
1644
1645
1646
1647
1648
1649
1650 public static byte[] generateData(final Random r, int length) {
1651 byte [] b = new byte [length];
1652 int i;
1653
1654 for(i = 0; i < (length-8); i += 8) {
1655 b[i] = (byte) (65 + r.nextInt(26));
1656 b[i+1] = b[i];
1657 b[i+2] = b[i];
1658 b[i+3] = b[i];
1659 b[i+4] = b[i];
1660 b[i+5] = b[i];
1661 b[i+6] = b[i];
1662 b[i+7] = b[i];
1663 }
1664
1665 byte a = (byte) (65 + r.nextInt(26));
1666 for(; i < length; i++) {
1667 b[i] = a;
1668 }
1669 return b;
1670 }
1671
1672
1673
1674
1675
1676 @Deprecated
1677 public static byte[] generateValue(final Random r) {
1678 return generateData(r, DEFAULT_VALUE_LENGTH);
1679 }
1680
1681 static byte [] getRandomRow(final Random random, final int totalRows) {
1682 return format(random.nextInt(Integer.MAX_VALUE) % totalRows);
1683 }
1684
1685 static RunResult runOneClient(final Class<? extends Test> cmd, Configuration conf, HConnection con,
1686 TestOptions opts, final Status status)
1687 throws IOException, InterruptedException {
1688 status.setStatus("Start " + cmd + " at offset " + opts.startRow + " for " +
1689 opts.perClientRunRows + " rows");
1690 long totalElapsedTime;
1691
1692 final Test t;
1693 try {
1694 Constructor<? extends Test> constructor =
1695 cmd.getDeclaredConstructor(HConnection.class, TestOptions.class, Status.class);
1696 t = constructor.newInstance(con, opts, status);
1697 } catch (NoSuchMethodException e) {
1698 throw new IllegalArgumentException("Invalid command class: " +
1699 cmd.getName() + ". It does not provide a constructor as described by " +
1700 "the javadoc comment. Available constructors are: " +
1701 Arrays.toString(cmd.getConstructors()));
1702 } catch (Exception e) {
1703 throw new IllegalStateException("Failed to construct command class", e);
1704 }
1705 totalElapsedTime = t.test();
1706
1707 status.setStatus("Finished " + cmd + " in " + totalElapsedTime +
1708 "ms at offset " + opts.startRow + " for " + opts.perClientRunRows + " rows" +
1709 " (" + calculateMbps((int)(opts.perClientRunRows * opts.sampleRate), totalElapsedTime,
1710 getAverageValueLength(opts), opts.columns) + ")");
1711
1712 return new RunResult(totalElapsedTime, t.getLatency());
1713 }
1714
1715 private static int getAverageValueLength(final TestOptions opts) {
1716 return opts.valueRandom? opts.valueSize/2: opts.valueSize;
1717 }
1718
1719 private void runTest(final Class<? extends Test> cmd, TestOptions opts) throws IOException,
1720 InterruptedException, ClassNotFoundException {
1721
1722
1723 LOG.info(cmd.getSimpleName() + " test run options=" + MAPPER.writeValueAsString(opts));
1724 HBaseAdmin admin = new HBaseAdmin(getConf());
1725 try {
1726 checkTable(admin, opts);
1727 } finally {
1728 admin.close();
1729 }
1730
1731 if (opts.nomapred) {
1732 doLocalClients(opts, getConf());
1733 } else {
1734 doMapReduce(opts, getConf());
1735 }
1736 }
1737
1738 protected void printUsage() {
1739 printUsage(this.getClass().getName(), null);
1740 }
1741
1742 protected static void printUsage(final String message) {
1743 printUsage(PerformanceEvaluation.class.getName(), message);
1744 }
1745
1746 protected static void printUsageAndExit(final String message, final int exitCode) {
1747 printUsage(message);
1748 System.exit(exitCode);
1749 }
1750
1751 protected static void printUsage(final String className, final String message) {
1752 if (message != null && message.length() > 0) {
1753 System.err.println(message);
1754 }
1755 System.err.println("Usage: java " + className + " \\");
1756 System.err.println(" <OPTIONS> [-D<property=value>]* <command> <nclients>");
1757 System.err.println();
1758 System.err.println("Options:");
1759 System.err.println(" nomapred Run multiple clients using threads " +
1760 "(rather than use mapreduce)");
1761 System.err.println(" rows Rows each client runs. Default: One million");
1762 System.err.println(" size Total size in GiB. Mutually exclusive with --rows. " +
1763 "Default: 1.0.");
1764 System.err.println(" sampleRate Execute test on a sample of total " +
1765 "rows. Only supported by randomRead. Default: 1.0");
1766 System.err.println(" traceRate Enable HTrace spans. Initiate tracing every N rows. " +
1767 "Default: 0");
1768 System.err.println(" table Alternate table name. Default: 'TestTable'");
1769 System.err.println(" multiGet If >0, when doing RandomRead, perform multiple gets " +
1770 "instead of single gets. Default: 0");
1771 System.err.println(" compress Compression type to use (GZ, LZO, ...). Default: 'NONE'");
1772 System.err.println(" flushCommits Used to determine if the test should flush the table. " +
1773 "Default: false");
1774 System.err.println(" writeToWAL Set writeToWAL on puts. Default: True");
1775 System.err.println(" autoFlush Set autoFlush on htable. Default: False");
1776 System.err.println(" oneCon all the threads share the same connection. Default: False");
1777 System.err.println(" presplit Create presplit table. Recommended for accurate perf " +
1778 "analysis (see guide). Default: disabled");
1779 System.err.println(" inmemory Tries to keep the HFiles of the CF " +
1780 "inmemory as far as possible. Not guaranteed that reads are always served " +
1781 "from memory. Default: false");
1782 System.err.println(" usetags Writes tags along with KVs. Use with HFile V3. " +
1783 "Default: false");
1784 System.err.println(" numoftags Specify the no of tags that would be needed. " +
1785 "This works only if usetags is true.");
1786 System.err.println(" filterAll Helps to filter out all the rows on the server side"
1787 + " there by not returning any thing back to the client. Helps to check the server side"
1788 + " performance. Uses FilterAllFilter internally. ");
1789 System.err.println(" latency Set to report operation latencies. Default: False");
1790 System.err.println(" bloomFilter Bloom filter type, one of " + Arrays.toString(BloomType.values()));
1791 System.err.println(" valueSize Pass value size to use: Default: 1024");
1792 System.err.println(" valueRandom Set if we should vary value size between 0 and " +
1793 "'valueSize'; set on read for stats on size: Default: Not set.");
1794 System.err.println(" valueZipf Set if we should vary value size between 0 and " +
1795 "'valueSize' in zipf form: Default: Not set.");
1796 System.err.println(" period Report every 'period' rows: " +
1797 "Default: opts.perClientRunRows / 10");
1798 System.err.println(" multiGet Batch gets together into groups of N. Only supported " +
1799 "by randomRead. Default: disabled");
1800 System.err.println(" addColumns Adds columns to scans/gets explicitly. Default: true");
1801 System.err.println(" splitPolicy Specify a custom RegionSplitPolicy for the table.");
1802 System.err.println(" randomSleep Do a random sleep before each get between 0 and entered value. Defaults: 0");
1803 System.err.println(" columns Columns to write per row. Default: 1");
1804 System.err.println(" caching Scan caching to use. Default: 30");
1805 System.err.println();
1806 System.err.println(" Note: -D properties will be applied to the conf used. ");
1807 System.err.println(" For example: ");
1808 System.err.println(" -Dmapreduce.output.fileoutputformat.compress=true");
1809 System.err.println(" -Dmapreduce.task.timeout=60000");
1810 System.err.println();
1811 System.err.println("Command:");
1812 for (CmdDescriptor command : COMMANDS.values()) {
1813 System.err.println(String.format(" %-15s %s", command.getName(), command.getDescription()));
1814 }
1815 System.err.println();
1816 System.err.println("Args:");
1817 System.err.println(" nclients Integer. Required. Total number of " +
1818 "clients (and HRegionServers)");
1819 System.err.println(" running: 1 <= value <= 500");
1820 System.err.println("Examples:");
1821 System.err.println(" To run a single client doing the default 1M sequentialWrites:");
1822 System.err.println(" $ bin/hbase " + className + " sequentialWrite 1");
1823 System.err.println(" To run 10 clients doing increments over ten rows:");
1824 System.err.println(" $ bin/hbase " + className + " --rows=10 --nomapred increment 10");
1825 }
1826
1827
1828
1829
1830
1831
1832
1833 static TestOptions parseOpts(Queue<String> args) {
1834 TestOptions opts = new TestOptions();
1835
1836 String cmd = null;
1837 while ((cmd = args.poll()) != null) {
1838 if (cmd.equals("-h") || cmd.startsWith("--h")) {
1839
1840 args.add(cmd);
1841 break;
1842 }
1843
1844 final String nmr = "--nomapred";
1845 if (cmd.startsWith(nmr)) {
1846 opts.nomapred = true;
1847 continue;
1848 }
1849
1850 final String rows = "--rows=";
1851 if (cmd.startsWith(rows)) {
1852 opts.perClientRunRows = Integer.parseInt(cmd.substring(rows.length()));
1853 continue;
1854 }
1855
1856 final String sampleRate = "--sampleRate=";
1857 if (cmd.startsWith(sampleRate)) {
1858 opts.sampleRate = Float.parseFloat(cmd.substring(sampleRate.length()));
1859 continue;
1860 }
1861
1862 final String table = "--table=";
1863 if (cmd.startsWith(table)) {
1864 opts.tableName = cmd.substring(table.length());
1865 continue;
1866 }
1867
1868 final String startRow = "--startRow=";
1869 if (cmd.startsWith(startRow)) {
1870 opts.startRow = Integer.parseInt(cmd.substring(startRow.length()));
1871 continue;
1872 }
1873
1874 final String compress = "--compress=";
1875 if (cmd.startsWith(compress)) {
1876 opts.compression = Compression.Algorithm.valueOf(cmd.substring(compress.length()));
1877 continue;
1878 }
1879
1880 final String traceRate = "--traceRate=";
1881 if (cmd.startsWith(traceRate)) {
1882 opts.traceRate = Double.parseDouble(cmd.substring(traceRate.length()));
1883 continue;
1884 }
1885
1886 final String blockEncoding = "--blockEncoding=";
1887 if (cmd.startsWith(blockEncoding)) {
1888 opts.blockEncoding = DataBlockEncoding.valueOf(cmd.substring(blockEncoding.length()));
1889 continue;
1890 }
1891
1892 final String flushCommits = "--flushCommits=";
1893 if (cmd.startsWith(flushCommits)) {
1894 opts.flushCommits = Boolean.parseBoolean(cmd.substring(flushCommits.length()));
1895 continue;
1896 }
1897
1898 final String writeToWAL = "--writeToWAL=";
1899 if (cmd.startsWith(writeToWAL)) {
1900 opts.writeToWAL = Boolean.parseBoolean(cmd.substring(writeToWAL.length()));
1901 continue;
1902 }
1903
1904 final String presplit = "--presplit=";
1905 if (cmd.startsWith(presplit)) {
1906 opts.presplitRegions = Integer.parseInt(cmd.substring(presplit.length()));
1907 continue;
1908 }
1909
1910 final String inMemory = "--inmemory=";
1911 if (cmd.startsWith(inMemory)) {
1912 opts.inMemoryCF = Boolean.parseBoolean(cmd.substring(inMemory.length()));
1913 continue;
1914 }
1915
1916 final String autoFlush = "--autoFlush=";
1917 if (cmd.startsWith(autoFlush)) {
1918 opts.autoFlush = Boolean.parseBoolean(cmd.substring(autoFlush.length()));
1919 continue;
1920 }
1921
1922 final String onceCon = "--oneCon=";
1923 if (cmd.startsWith(onceCon)) {
1924 opts.oneCon = Boolean.parseBoolean(cmd.substring(onceCon.length()));
1925 continue;
1926 }
1927
1928 final String latency = "--latency";
1929 if (cmd.startsWith(latency)) {
1930 opts.reportLatency = true;
1931 continue;
1932 }
1933
1934 final String multiGet = "--multiGet=";
1935 if (cmd.startsWith(multiGet)) {
1936 opts.multiGet = Integer.parseInt(cmd.substring(multiGet.length()));
1937 continue;
1938 }
1939
1940 final String useTags = "--usetags=";
1941 if (cmd.startsWith(useTags)) {
1942 opts.useTags = Boolean.parseBoolean(cmd.substring(useTags.length()));
1943 continue;
1944 }
1945
1946 final String noOfTags = "--numoftags=";
1947 if (cmd.startsWith(noOfTags)) {
1948 opts.noOfTags = Integer.parseInt(cmd.substring(noOfTags.length()));
1949 continue;
1950 }
1951
1952 final String filterOutAll = "--filterAll";
1953 if (cmd.startsWith(filterOutAll)) {
1954 opts.filterAll = true;
1955 continue;
1956 }
1957
1958 final String size = "--size=";
1959 if (cmd.startsWith(size)) {
1960 opts.size = Float.parseFloat(cmd.substring(size.length()));
1961 continue;
1962 }
1963
1964 final String splitPolicy = "--splitPolicy=";
1965 if (cmd.startsWith(splitPolicy)) {
1966 opts.splitPolicy = cmd.substring(splitPolicy.length());
1967 continue;
1968 }
1969
1970 final String randomSleep = "--randomSleep=";
1971 if (cmd.startsWith(randomSleep)) {
1972 opts.randomSleep = Integer.parseInt(cmd.substring(randomSleep.length()));
1973 continue;
1974 }
1975
1976 final String bloomFilter = "--bloomFilter=";
1977 if (cmd.startsWith(bloomFilter)) {
1978 opts.bloomType = BloomType.valueOf(cmd.substring(bloomFilter.length()));
1979 continue;
1980 }
1981
1982 final String valueSize = "--valueSize=";
1983 if (cmd.startsWith(valueSize)) {
1984 opts.valueSize = Integer.parseInt(cmd.substring(valueSize.length()));
1985 continue;
1986 }
1987
1988 final String valueRandom = "--valueRandom";
1989 if (cmd.startsWith(valueRandom)) {
1990 opts.valueRandom = true;
1991 if (opts.valueZipf) {
1992 throw new IllegalStateException("Either valueZipf or valueRandom but not both");
1993 }
1994 continue;
1995 }
1996
1997 final String valueZipf = "--valueZipf";
1998 if (cmd.startsWith(valueZipf)) {
1999 opts.valueZipf = true;
2000 if (opts.valueRandom) {
2001 throw new IllegalStateException("Either valueZipf or valueRandom but not both");
2002 }
2003 continue;
2004 }
2005
2006 final String period = "--period=";
2007 if (cmd.startsWith(period)) {
2008 opts.period = Integer.parseInt(cmd.substring(period.length()));
2009 continue;
2010 }
2011
2012 final String addColumns = "--addColumns=";
2013 if (cmd.startsWith(addColumns)) {
2014 opts.addColumns = Boolean.parseBoolean(cmd.substring(addColumns.length()));
2015 continue;
2016 }
2017
2018 final String columns = "--columns=";
2019 if (cmd.startsWith(columns)) {
2020 opts.columns = Integer.parseInt(cmd.substring(columns.length()));
2021 continue;
2022 }
2023
2024 final String caching = "--caching=";
2025 if (cmd.startsWith(caching)) {
2026 opts.caching = Integer.parseInt(cmd.substring(caching.length()));
2027 continue;
2028 }
2029
2030 if (isCommandClass(cmd)) {
2031 opts.cmdName = cmd;
2032 opts.numClientThreads = Integer.parseInt(args.remove());
2033 int rowsPerGB = getRowsPerGB(opts);
2034 if (opts.size != DEFAULT_OPTS.size &&
2035 opts.perClientRunRows != DEFAULT_OPTS.perClientRunRows) {
2036 throw new IllegalArgumentException(rows + " and " + size + " are mutually exclusive arguments.");
2037 }
2038 if (opts.size != DEFAULT_OPTS.size) {
2039
2040 opts.totalRows = (int) opts.size * rowsPerGB;
2041 opts.perClientRunRows = opts.totalRows / opts.numClientThreads;
2042 } else if (opts.perClientRunRows != DEFAULT_OPTS.perClientRunRows) {
2043
2044 opts.totalRows = opts.perClientRunRows * opts.numClientThreads;
2045 opts.size = opts.totalRows / rowsPerGB;
2046 }
2047 break;
2048 } else {
2049 printUsageAndExit("ERROR: Unrecognized option/command: " + cmd, -1);
2050 }
2051
2052
2053 System.err.println("Error: Wrong option or command: " + cmd);
2054 args.add(cmd);
2055 break;
2056 }
2057 return opts;
2058 }
2059
2060 static int getRowsPerGB(final TestOptions opts) {
2061 return ONE_GB / ((opts.valueRandom? opts.valueSize/2: opts.valueSize) * opts.getColumns());
2062 }
2063
2064 @Override
2065 public int run(String[] args) throws Exception {
2066
2067
2068 int errCode = -1;
2069 if (args.length < 1) {
2070 printUsage();
2071 return errCode;
2072 }
2073
2074 try {
2075 LinkedList<String> argv = new LinkedList<String>();
2076 argv.addAll(Arrays.asList(args));
2077 TestOptions opts = parseOpts(argv);
2078
2079
2080 if (!argv.isEmpty()) {
2081 errCode = 0;
2082 printUsage();
2083 return errCode;
2084 }
2085
2086
2087 if (opts.numClientThreads <= 0) {
2088 throw new IllegalArgumentException("Number of clients must be > 0");
2089 }
2090
2091 Class<? extends Test> cmdClass = determineCommandClass(opts.cmdName);
2092 if (cmdClass != null) {
2093 runTest(cmdClass, opts);
2094 errCode = 0;
2095 }
2096
2097 } catch (Exception e) {
2098 e.printStackTrace();
2099 }
2100
2101 return errCode;
2102 }
2103
2104 private static boolean isCommandClass(String cmd) {
2105 return COMMANDS.containsKey(cmd);
2106 }
2107
2108 private static Class<? extends Test> determineCommandClass(String cmd) {
2109 CmdDescriptor descriptor = COMMANDS.get(cmd);
2110 return descriptor != null ? descriptor.getCmdClass() : null;
2111 }
2112
2113 public static void main(final String[] args) throws Exception {
2114 int res = ToolRunner.run(new PerformanceEvaluation(HBaseConfiguration.create()), args);
2115 System.exit(res);
2116 }
2117 }