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 latencyHistogram;
942 private Histogram valueSizeHistogram;
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.valueSizeHistogram.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 getLatencyHistogram() {
1012 return latencyHistogram;
1013 }
1014
1015 void testSetup() throws IOException {
1016 if (!opts.oneCon) {
1017 this.connection = HConnectionManager.createConnection(conf);
1018 }
1019 onStartup();
1020 latencyHistogram = YammerHistogramUtils.newHistogram(new UniformSample(1024 * 500));
1021 valueSizeHistogram = YammerHistogramUtils.newHistogram(new UniformSample(1024 * 500));
1022 }
1023
1024 abstract void onStartup() throws IOException;
1025
1026 void testTakedown() throws IOException {
1027 onTakedown();
1028
1029
1030
1031 synchronized (Test.class) {
1032 status.setStatus("Test : " + testName + ", Thread : " + Thread.currentThread().getName());
1033 status.setStatus("Latency (us) : " + YammerHistogramUtils.getHistogramReport(
1034 latencyHistogram));
1035 status.setStatus("Num measures (latency) : " + latencyHistogram.count());
1036 status.setStatus(YammerHistogramUtils.getPrettyHistogramReport(latencyHistogram));
1037 status.setStatus("ValueSize (bytes) : "
1038 + YammerHistogramUtils.getHistogramReport(valueSizeHistogram));
1039 status.setStatus("Num measures (ValueSize): " + valueSizeHistogram.count());
1040 status.setStatus(YammerHistogramUtils.getPrettyHistogramReport(valueSizeHistogram));
1041 }
1042 if (!opts.oneCon) {
1043 connection.close();
1044 }
1045 receiverHost.closeReceivers();
1046 }
1047
1048 abstract void onTakedown() throws IOException;
1049
1050
1051
1052
1053
1054
1055 long test() throws IOException, InterruptedException {
1056 testSetup();
1057 LOG.info("Timed test starting in thread " + Thread.currentThread().getName());
1058 final long startTime = System.nanoTime();
1059 try {
1060 testTimed();
1061 } finally {
1062 testTakedown();
1063 }
1064 return (System.nanoTime() - startTime) / 1000000;
1065 }
1066
1067 int getStartRow() {
1068 return opts.startRow;
1069 }
1070
1071 int getLastRow() {
1072 return getStartRow() + opts.perClientRunRows;
1073 }
1074
1075
1076
1077
1078 void testTimed() throws IOException, InterruptedException {
1079 int startRow = getStartRow();
1080 int lastRow = getLastRow();
1081
1082 for (int i = startRow; i < lastRow; i++) {
1083 if (i % everyN != 0) continue;
1084 long startTime = System.nanoTime();
1085 TraceScope scope = Trace.startSpan("test row", traceSampler);
1086 try {
1087 testRow(i);
1088 } finally {
1089 scope.close();
1090 }
1091
1092
1093
1094 if (opts.multiGet == 0 || (i - startRow + 1) % opts.multiGet == 0) {
1095 latencyHistogram.update((System.nanoTime() - startTime) / 1000);
1096 }
1097 if (status != null && i > 0 && (i % getReportingPeriod()) == 0) {
1098 status.setStatus(generateStatus(startRow, i, lastRow));
1099 }
1100 }
1101 }
1102
1103
1104
1105
1106 public String getShortLatencyReport() {
1107 return YammerHistogramUtils.getShortHistogramReport(this.latencyHistogram);
1108 }
1109
1110
1111
1112
1113 public String getShortValueSizeReport() {
1114 return YammerHistogramUtils.getShortHistogramReport(this.valueSizeHistogram);
1115 }
1116
1117
1118
1119
1120
1121 abstract void testRow(final int i) throws IOException, InterruptedException;
1122 }
1123
1124 static abstract class TableTest extends Test {
1125 protected HTableInterface table;
1126
1127 TableTest(HConnection con, TestOptions options, Status status) {
1128 super(con, options, status);
1129 }
1130
1131 @Override
1132 void onStartup() throws IOException {
1133 this.table = connection.getTable(TableName.valueOf(opts.tableName));
1134 }
1135
1136 @Override
1137 void onTakedown() throws IOException {
1138 table.close();
1139 }
1140 }
1141
1142 static class RandomSeekScanTest extends TableTest {
1143 RandomSeekScanTest(HConnection con, TestOptions options, Status status) {
1144 super(con, options, status);
1145 }
1146
1147 @Override
1148 void testRow(final int i) throws IOException {
1149 Scan scan = new Scan(getRandomRow(this.rand, opts.totalRows));
1150 scan.setCaching(opts.caching);
1151 FilterList list = new FilterList();
1152 if (opts.addColumns) {
1153 scan.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1154 } else {
1155 scan.addFamily(FAMILY_NAME);
1156 }
1157 if (opts.filterAll) {
1158 list.addFilter(new FilterAllFilter());
1159 }
1160 list.addFilter(new WhileMatchFilter(new PageFilter(120)));
1161 scan.setFilter(list);
1162 ResultScanner s = this.table.getScanner(scan);
1163 for (Result rr; (rr = s.next()) != null;) {
1164 updateValueSize(rr);
1165 }
1166 s.close();
1167 }
1168
1169 @Override
1170 protected int getReportingPeriod() {
1171 int period = opts.perClientRunRows / 100;
1172 return period == 0 ? opts.perClientRunRows : period;
1173 }
1174
1175 }
1176
1177 static abstract class RandomScanWithRangeTest extends TableTest {
1178 RandomScanWithRangeTest(HConnection con, TestOptions options, Status status) {
1179 super(con, options, status);
1180 }
1181
1182 @Override
1183 void testRow(final int i) throws IOException {
1184 Pair<byte[], byte[]> startAndStopRow = getStartAndStopRow();
1185 Scan scan = new Scan(startAndStopRow.getFirst(), startAndStopRow.getSecond());
1186 scan.setCaching(opts.caching);
1187 if (opts.filterAll) {
1188 scan.setFilter(new FilterAllFilter());
1189 }
1190 if (opts.addColumns) {
1191 scan.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1192 } else {
1193 scan.addFamily(FAMILY_NAME);
1194 }
1195 Result r = null;
1196 int count = 0;
1197 ResultScanner s = this.table.getScanner(scan);
1198 for (; (r = s.next()) != null;) {
1199 updateValueSize(r);
1200 count++;
1201 }
1202 if (i % 100 == 0) {
1203 LOG.info(String.format("Scan for key range %s - %s returned %s rows",
1204 Bytes.toString(startAndStopRow.getFirst()),
1205 Bytes.toString(startAndStopRow.getSecond()), count));
1206 }
1207
1208 s.close();
1209 }
1210
1211 protected abstract Pair<byte[],byte[]> getStartAndStopRow();
1212
1213 protected Pair<byte[], byte[]> generateStartAndStopRows(int maxRange) {
1214 int start = this.rand.nextInt(Integer.MAX_VALUE) % opts.totalRows;
1215 int stop = start + maxRange;
1216 return new Pair<byte[],byte[]>(format(start), format(stop));
1217 }
1218
1219 @Override
1220 protected int getReportingPeriod() {
1221 int period = opts.perClientRunRows / 100;
1222 return period == 0? opts.perClientRunRows: period;
1223 }
1224 }
1225
1226 static class RandomScanWithRange10Test extends RandomScanWithRangeTest {
1227 RandomScanWithRange10Test(HConnection con, TestOptions options, Status status) {
1228 super(con, options, status);
1229 }
1230
1231 @Override
1232 protected Pair<byte[], byte[]> getStartAndStopRow() {
1233 return generateStartAndStopRows(10);
1234 }
1235 }
1236
1237 static class RandomScanWithRange100Test extends RandomScanWithRangeTest {
1238 RandomScanWithRange100Test(HConnection con, TestOptions options, Status status) {
1239 super(con, options, status);
1240 }
1241
1242 @Override
1243 protected Pair<byte[], byte[]> getStartAndStopRow() {
1244 return generateStartAndStopRows(100);
1245 }
1246 }
1247
1248 static class RandomScanWithRange1000Test extends RandomScanWithRangeTest {
1249 RandomScanWithRange1000Test(HConnection con, TestOptions options, Status status) {
1250 super(con, options, status);
1251 }
1252
1253 @Override
1254 protected Pair<byte[], byte[]> getStartAndStopRow() {
1255 return generateStartAndStopRows(1000);
1256 }
1257 }
1258
1259 static class RandomScanWithRange10000Test extends RandomScanWithRangeTest {
1260 RandomScanWithRange10000Test(HConnection con, TestOptions options, Status status) {
1261 super(con, options, status);
1262 }
1263
1264 @Override
1265 protected Pair<byte[], byte[]> getStartAndStopRow() {
1266 return generateStartAndStopRows(10000);
1267 }
1268 }
1269
1270 static class RandomReadTest extends TableTest {
1271 private ArrayList<Get> gets;
1272 private Random rd = new Random();
1273
1274 RandomReadTest(HConnection con, TestOptions options, Status status) {
1275 super(con, options, status);
1276 if (opts.multiGet > 0) {
1277 LOG.info("MultiGet enabled. Sending GETs in batches of " + opts.multiGet + ".");
1278 this.gets = new ArrayList<Get>(opts.multiGet);
1279 }
1280 }
1281
1282 @Override
1283 void testRow(final int i) throws IOException, InterruptedException {
1284 if (opts.randomSleep > 0) {
1285 Thread.sleep(rd.nextInt(opts.randomSleep));
1286 }
1287 Get get = new Get(getRandomRow(this.rand, opts.totalRows));
1288 if (opts.addColumns) {
1289 get.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1290 } else {
1291 get.addFamily(FAMILY_NAME);
1292 }
1293 if (opts.filterAll) {
1294 get.setFilter(new FilterAllFilter());
1295 }
1296 if (LOG.isTraceEnabled()) LOG.trace(get.toString());
1297 if (opts.multiGet > 0) {
1298 this.gets.add(get);
1299 if (this.gets.size() == opts.multiGet) {
1300 Result [] rs = this.table.get(this.gets);
1301 updateValueSize(rs);
1302 this.gets.clear();
1303 }
1304 } else {
1305 updateValueSize(this.table.get(get));
1306 }
1307 }
1308
1309 @Override
1310 protected int getReportingPeriod() {
1311 int period = opts.perClientRunRows / 10;
1312 return period == 0 ? opts.perClientRunRows : period;
1313 }
1314
1315 @Override
1316 protected void testTakedown() throws IOException {
1317 if (this.gets != null && this.gets.size() > 0) {
1318 this.table.get(gets);
1319 this.gets.clear();
1320 }
1321 super.testTakedown();
1322 }
1323 }
1324
1325 static class RandomWriteTest extends TableTest {
1326 RandomWriteTest(HConnection con, TestOptions options, Status status) {
1327 super(con, options, status);
1328 }
1329
1330 @Override
1331 void testRow(final int i) throws IOException {
1332 byte[] row = getRandomRow(this.rand, opts.totalRows);
1333 Put put = new Put(row);
1334 for (int column = 0; column < opts.columns; column++) {
1335 byte [] qualifier = column == 0? COLUMN_ZERO: Bytes.toBytes("" + column);
1336 byte[] value = generateData(this.rand, getValueLength(this.rand));
1337 if (opts.useTags) {
1338 byte[] tag = generateData(this.rand, TAG_LENGTH);
1339 Tag[] tags = new Tag[opts.noOfTags];
1340 for (int n = 0; n < opts.noOfTags; n++) {
1341 Tag t = new Tag((byte) n, tag);
1342 tags[n] = t;
1343 }
1344 KeyValue kv = new KeyValue(row, FAMILY_NAME, qualifier, HConstants.LATEST_TIMESTAMP,
1345 value, tags);
1346 put.add(kv);
1347 updateValueSize(kv.getValueLength());
1348 } else {
1349 put.add(FAMILY_NAME, qualifier, value);
1350 updateValueSize(value.length);
1351 }
1352 }
1353 put.setDurability(opts.writeToWAL ? Durability.SYNC_WAL : Durability.SKIP_WAL);
1354 table.put(put);
1355 }
1356 }
1357
1358 static class ScanTest extends TableTest {
1359 private ResultScanner testScanner;
1360
1361 ScanTest(HConnection con, TestOptions options, Status status) {
1362 super(con, options, status);
1363 }
1364
1365 @Override
1366 void testTakedown() throws IOException {
1367 if (this.testScanner != null) {
1368 this.testScanner.close();
1369 }
1370 super.testTakedown();
1371 }
1372
1373
1374 @Override
1375 void testRow(final int i) throws IOException {
1376 if (this.testScanner == null) {
1377 Scan scan = new Scan(format(opts.startRow));
1378 scan.setCaching(opts.caching);
1379 if (opts.addColumns) {
1380 scan.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1381 } else {
1382 scan.addFamily(FAMILY_NAME);
1383 }
1384 if (opts.filterAll) {
1385 scan.setFilter(new FilterAllFilter());
1386 }
1387 this.testScanner = table.getScanner(scan);
1388 }
1389 Result r = testScanner.next();
1390 updateValueSize(r);
1391 }
1392 }
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402 static abstract class CASTableTest extends TableTest {
1403 private final byte [] qualifier;
1404 CASTableTest(HConnection con, TestOptions options, Status status) {
1405 super(con, options, status);
1406 qualifier = Bytes.toBytes(this.getClass().getSimpleName());
1407 }
1408
1409 byte [] getQualifier() {
1410 return this.qualifier;
1411 }
1412
1413 @Override
1414 int getStartRow() {
1415 return 0;
1416 }
1417
1418 @Override
1419 int getLastRow() {
1420 return opts.perClientRunRows;
1421 }
1422 }
1423
1424 static class IncrementTest extends CASTableTest {
1425 IncrementTest(HConnection con, TestOptions options, Status status) {
1426 super(con, options, status);
1427 }
1428
1429 @Override
1430 void testRow(final int i) throws IOException {
1431 Increment increment = new Increment(format(i));
1432 increment.addColumn(FAMILY_NAME, getQualifier(), 1l);
1433 updateValueSize(this.table.increment(increment));
1434 }
1435 }
1436
1437 static class AppendTest extends CASTableTest {
1438 AppendTest(HConnection con, TestOptions options, Status status) {
1439 super(con, options, status);
1440 }
1441
1442 @Override
1443 void testRow(final int i) throws IOException {
1444 byte [] bytes = format(i);
1445 Append append = new Append(bytes);
1446 append.add(FAMILY_NAME, getQualifier(), bytes);
1447 updateValueSize(this.table.append(append));
1448 }
1449 }
1450
1451 static class CheckAndMutateTest extends CASTableTest {
1452 CheckAndMutateTest(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
1460 Put put = new Put(bytes);
1461 put.add(FAMILY_NAME, getQualifier(), bytes);
1462 this.table.put(put);
1463 RowMutations mutations = new RowMutations(bytes);
1464 mutations.add(put);
1465 this.table.checkAndMutate(bytes, FAMILY_NAME, getQualifier(), CompareOp.EQUAL, bytes,
1466 mutations);
1467 }
1468 }
1469
1470 static class CheckAndPutTest extends CASTableTest {
1471 CheckAndPutTest(HConnection con, TestOptions options, Status status) {
1472 super(con, options, status);
1473 }
1474
1475 @Override
1476 void testRow(final int i) throws IOException {
1477 byte [] bytes = format(i);
1478
1479 Put put = new Put(bytes);
1480 put.add(FAMILY_NAME, getQualifier(), bytes);
1481 this.table.put(put);
1482 this.table.checkAndPut(bytes, FAMILY_NAME, getQualifier(), bytes, put);
1483 }
1484 }
1485
1486 static class CheckAndDeleteTest extends CASTableTest {
1487 CheckAndDeleteTest(HConnection con, TestOptions options, Status status) {
1488 super(con, options, status);
1489 }
1490
1491 @Override
1492 void testRow(final int i) throws IOException {
1493 byte [] bytes = format(i);
1494
1495 Put put = new Put(bytes);
1496 put.add(FAMILY_NAME, getQualifier(), bytes);
1497 this.table.put(put);
1498 Delete delete = new Delete(put.getRow());
1499 delete.deleteColumn(FAMILY_NAME, getQualifier());
1500 this.table.checkAndDelete(bytes, FAMILY_NAME, getQualifier(), bytes, delete);
1501 }
1502 }
1503
1504 static class SequentialReadTest extends TableTest {
1505 SequentialReadTest(HConnection con, TestOptions options, Status status) {
1506 super(con, options, status);
1507 }
1508
1509 @Override
1510 void testRow(final int i) throws IOException {
1511 Get get = new Get(format(i));
1512 if (opts.addColumns) {
1513 get.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1514 }
1515 if (opts.filterAll) {
1516 get.setFilter(new FilterAllFilter());
1517 }
1518 updateValueSize(table.get(get));
1519 }
1520 }
1521
1522 static class SequentialWriteTest extends TableTest {
1523 SequentialWriteTest(HConnection con, TestOptions options, Status status) {
1524 super(con, options, status);
1525 }
1526
1527 @Override
1528 void testRow(final int i) throws IOException {
1529 byte[] row = format(i);
1530 Put put = new Put(row);
1531 for (int column = 0; column < opts.columns; column++) {
1532 byte [] qualifier = column == 0? COLUMN_ZERO: Bytes.toBytes("" + column);
1533 byte[] value = generateData(this.rand, getValueLength(this.rand));
1534 if (opts.useTags) {
1535 byte[] tag = generateData(this.rand, TAG_LENGTH);
1536 Tag[] tags = new Tag[opts.noOfTags];
1537 for (int n = 0; n < opts.noOfTags; n++) {
1538 Tag t = new Tag((byte) n, tag);
1539 tags[n] = t;
1540 }
1541 KeyValue kv = new KeyValue(row, FAMILY_NAME, qualifier, HConstants.LATEST_TIMESTAMP,
1542 value, tags);
1543 put.add(kv);
1544 updateValueSize(kv.getValueLength());
1545 } else {
1546 put.add(FAMILY_NAME, qualifier, value);
1547 updateValueSize(value.length);
1548 }
1549 }
1550 put.setDurability(opts.writeToWAL ? Durability.SYNC_WAL : Durability.SKIP_WAL);
1551 table.put(put);
1552 }
1553 }
1554
1555 static class FilteredScanTest extends TableTest {
1556 protected static final Log LOG = LogFactory.getLog(FilteredScanTest.class.getName());
1557
1558 FilteredScanTest(HConnection con, TestOptions options, Status status) {
1559 super(con, options, status);
1560 }
1561
1562 @Override
1563 void testRow(int i) throws IOException {
1564 byte[] value = generateData(this.rand, getValueLength(this.rand));
1565 Scan scan = constructScan(value);
1566 ResultScanner scanner = null;
1567 try {
1568 scanner = this.table.getScanner(scan);
1569 for (Result r = null; (r = scanner.next()) != null;) {
1570 updateValueSize(r);
1571 }
1572 } finally {
1573 if (scanner != null) scanner.close();
1574 }
1575 }
1576
1577 protected Scan constructScan(byte[] valuePrefix) throws IOException {
1578 FilterList list = new FilterList();
1579 Filter filter = new SingleColumnValueFilter(
1580 FAMILY_NAME, COLUMN_ZERO, CompareFilter.CompareOp.EQUAL,
1581 new BinaryComparator(valuePrefix)
1582 );
1583 list.addFilter(filter);
1584 if(opts.filterAll) {
1585 list.addFilter(new FilterAllFilter());
1586 }
1587 Scan scan = new Scan();
1588 scan.setCaching(opts.caching);
1589 if (opts.addColumns) {
1590 scan.addColumn(FAMILY_NAME, QUALIFIER_NAME);
1591 } else {
1592 scan.addFamily(FAMILY_NAME);
1593 }
1594 scan.setFilter(list);
1595 return scan;
1596 }
1597 }
1598
1599
1600
1601
1602
1603
1604
1605 private static String calculateMbps(int rows, long timeMs, final int valueSize, int columns) {
1606 BigDecimal rowSize = BigDecimal.valueOf(ROW_LENGTH +
1607 ((valueSize + FAMILY_NAME.length + COLUMN_ZERO.length) * columns));
1608 BigDecimal mbps = BigDecimal.valueOf(rows).multiply(rowSize, CXT)
1609 .divide(BigDecimal.valueOf(timeMs), CXT).multiply(MS_PER_SEC, CXT)
1610 .divide(BYTES_PER_MB, CXT);
1611 return FMT.format(mbps) + " MB/s";
1612 }
1613
1614
1615
1616
1617
1618
1619
1620 public static byte [] format(final int number) {
1621 byte [] b = new byte[ROW_LENGTH];
1622 int d = Math.abs(number);
1623 for (int i = b.length - 1; i >= 0; i--) {
1624 b[i] = (byte)((d % 10) + '0');
1625 d /= 10;
1626 }
1627 return b;
1628 }
1629
1630
1631
1632
1633
1634
1635
1636 public static byte[] generateData(final Random r, int length) {
1637 byte [] b = new byte [length];
1638 int i;
1639
1640 for(i = 0; i < (length-8); i += 8) {
1641 b[i] = (byte) (65 + r.nextInt(26));
1642 b[i+1] = b[i];
1643 b[i+2] = b[i];
1644 b[i+3] = b[i];
1645 b[i+4] = b[i];
1646 b[i+5] = b[i];
1647 b[i+6] = b[i];
1648 b[i+7] = b[i];
1649 }
1650
1651 byte a = (byte) (65 + r.nextInt(26));
1652 for(; i < length; i++) {
1653 b[i] = a;
1654 }
1655 return b;
1656 }
1657
1658
1659
1660
1661
1662 @Deprecated
1663 public static byte[] generateValue(final Random r) {
1664 return generateData(r, DEFAULT_VALUE_LENGTH);
1665 }
1666
1667 static byte [] getRandomRow(final Random random, final int totalRows) {
1668 return format(random.nextInt(Integer.MAX_VALUE) % totalRows);
1669 }
1670
1671 static RunResult runOneClient(final Class<? extends Test> cmd, Configuration conf, HConnection con,
1672 TestOptions opts, final Status status)
1673 throws IOException, InterruptedException {
1674 status.setStatus("Start " + cmd + " at offset " + opts.startRow + " for " +
1675 opts.perClientRunRows + " rows");
1676 long totalElapsedTime;
1677
1678 final Test t;
1679 try {
1680 Constructor<? extends Test> constructor =
1681 cmd.getDeclaredConstructor(HConnection.class, TestOptions.class, Status.class);
1682 t = constructor.newInstance(con, opts, status);
1683 } catch (NoSuchMethodException e) {
1684 throw new IllegalArgumentException("Invalid command class: " +
1685 cmd.getName() + ". It does not provide a constructor as described by " +
1686 "the javadoc comment. Available constructors are: " +
1687 Arrays.toString(cmd.getConstructors()));
1688 } catch (Exception e) {
1689 throw new IllegalStateException("Failed to construct command class", e);
1690 }
1691 totalElapsedTime = t.test();
1692
1693 status.setStatus("Finished " + cmd + " in " + totalElapsedTime +
1694 "ms at offset " + opts.startRow + " for " + opts.perClientRunRows + " rows" +
1695 " (" + calculateMbps((int)(opts.perClientRunRows * opts.sampleRate), totalElapsedTime,
1696 getAverageValueLength(opts), opts.columns) + ")");
1697
1698 return new RunResult(totalElapsedTime, t.getLatencyHistogram());
1699 }
1700
1701 private static int getAverageValueLength(final TestOptions opts) {
1702 return opts.valueRandom? opts.valueSize/2: opts.valueSize;
1703 }
1704
1705 private void runTest(final Class<? extends Test> cmd, TestOptions opts) throws IOException,
1706 InterruptedException, ClassNotFoundException {
1707
1708
1709 LOG.info(cmd.getSimpleName() + " test run options=" + MAPPER.writeValueAsString(opts));
1710 HBaseAdmin admin = new HBaseAdmin(getConf());
1711 try {
1712 checkTable(admin, opts);
1713 } finally {
1714 admin.close();
1715 }
1716
1717 if (opts.nomapred) {
1718 doLocalClients(opts, getConf());
1719 } else {
1720 doMapReduce(opts, getConf());
1721 }
1722 }
1723
1724 protected void printUsage() {
1725 printUsage(this.getClass().getName(), null);
1726 }
1727
1728 protected static void printUsage(final String message) {
1729 printUsage(PerformanceEvaluation.class.getName(), message);
1730 }
1731
1732 protected static void printUsageAndExit(final String message, final int exitCode) {
1733 printUsage(message);
1734 System.exit(exitCode);
1735 }
1736
1737 protected static void printUsage(final String className, final String message) {
1738 if (message != null && message.length() > 0) {
1739 System.err.println(message);
1740 }
1741 System.err.println("Usage: java " + className + " \\");
1742 System.err.println(" <OPTIONS> [-D<property=value>]* <command> <nclients>");
1743 System.err.println();
1744 System.err.println("Options:");
1745 System.err.println(" nomapred Run multiple clients using threads " +
1746 "(rather than use mapreduce)");
1747 System.err.println(" rows Rows each client runs. Default: " +
1748 DEFAULT_OPTS.getPerClientRunRows());
1749 System.err.println(" size Total size in GiB. Mutually exclusive with --rows. " +
1750 "Default: 1.0.");
1751 System.err.println(" sampleRate Execute test on a sample of total " +
1752 "rows. Only supported by randomRead. Default: 1.0");
1753 System.err.println(" traceRate Enable HTrace spans. Initiate tracing every N rows. " +
1754 "Default: 0");
1755 System.err.println(" table Alternate table name. Default: 'TestTable'");
1756 System.err.println(" multiGet If >0, when doing RandomRead, perform multiple gets " +
1757 "instead of single gets. Default: 0");
1758 System.err.println(" compress Compression type to use (GZ, LZO, ...). Default: 'NONE'");
1759 System.err.println(" flushCommits Used to determine if the test should flush the table. " +
1760 "Default: false");
1761 System.err.println(" writeToWAL Set writeToWAL on puts. Default: True");
1762 System.err.println(" autoFlush Set autoFlush on htable. Default: False");
1763 System.err.println(" oneCon all the threads share the same connection. Default: False");
1764 System.err.println(" presplit Create presplit table. If a table with same name exists,"
1765 + " it'll be deleted and recreated (instead of verifying count of its existing regions). "
1766 + "Recommended for accurate perf analysis (see guide). Default: disabled");
1767 System.err.println(" inmemory Tries to keep the HFiles of the CF " +
1768 "inmemory as far as possible. Not guaranteed that reads are always served " +
1769 "from memory. Default: false");
1770 System.err.println(" usetags Writes tags along with KVs. Use with HFile V3. " +
1771 "Default: false");
1772 System.err.println(" numoftags Specify the no of tags that would be needed. " +
1773 "This works only if usetags is true. Default: " + DEFAULT_OPTS.noOfTags);
1774 System.err.println(" filterAll Helps to filter out all the rows on the server side" +
1775 " there by not returning any thing back to the client. Helps to check the server side" +
1776 " performance. Uses FilterAllFilter internally. ");
1777 System.err.println(" latency Set to report operation latencies. Default: False");
1778 System.err.println(" bloomFilter Bloom filter type, one of " + Arrays.toString(BloomType.values()));
1779 System.err.println(" blockEncoding Block encoding to use. Value should be one of "
1780 + Arrays.toString(DataBlockEncoding.values()) + ". Default: NONE");
1781 System.err.println(" valueSize Pass value size to use: Default: " +
1782 DEFAULT_OPTS.getValueSize());
1783 System.err.println(" valueRandom Set if we should vary value size between 0 and " +
1784 "'valueSize'; set on read for stats on size: Default: Not set.");
1785 System.err.println(" valueZipf Set if we should vary value size between 0 and " +
1786 "'valueSize' in zipf form: Default: Not set.");
1787 System.err.println(" period Report every 'period' rows: " +
1788 "Default: opts.perClientRunRows / 10 = " + DEFAULT_OPTS.getPerClientRunRows()/10);
1789 System.err.println(" multiGet Batch gets together into groups of N. Only supported " +
1790 "by randomRead. Default: disabled");
1791 System.err.println(" addColumns Adds columns to scans/gets explicitly. Default: true");
1792 System.err.println(" splitPolicy Specify a custom RegionSplitPolicy for the table.");
1793 System.err.println(" randomSleep Do a random sleep before each get between 0 and entered value. Defaults: 0");
1794 System.err.println(" columns Columns to write per row. Default: 1");
1795 System.err.println(" caching Scan caching to use. Default: 30");
1796 System.err.println();
1797 System.err.println(" Note: -D properties will be applied to the conf used. ");
1798 System.err.println(" For example: ");
1799 System.err.println(" -Dmapreduce.output.fileoutputformat.compress=true");
1800 System.err.println(" -Dmapreduce.task.timeout=60000");
1801 System.err.println();
1802 System.err.println("Command:");
1803 for (CmdDescriptor command : COMMANDS.values()) {
1804 System.err.println(String.format(" %-15s %s", command.getName(), command.getDescription()));
1805 }
1806 System.err.println();
1807 System.err.println("Args:");
1808 System.err.println(" nclients Integer. Required. Total number of clients "
1809 + "(and HRegionServers) running. 1 <= value <= 500");
1810 System.err.println("Examples:");
1811 System.err.println(" To run a single client doing the default 1M sequentialWrites:");
1812 System.err.println(" $ bin/hbase " + className + " sequentialWrite 1");
1813 System.err.println(" To run 10 clients doing increments over ten rows:");
1814 System.err.println(" $ bin/hbase " + className + " --rows=10 --nomapred increment 10");
1815 }
1816
1817
1818
1819
1820
1821
1822
1823 static TestOptions parseOpts(Queue<String> args) {
1824 TestOptions opts = new TestOptions();
1825
1826 String cmd = null;
1827 while ((cmd = args.poll()) != null) {
1828 if (cmd.equals("-h") || cmd.startsWith("--h")) {
1829
1830 args.add(cmd);
1831 break;
1832 }
1833
1834 final String nmr = "--nomapred";
1835 if (cmd.startsWith(nmr)) {
1836 opts.nomapred = true;
1837 continue;
1838 }
1839
1840 final String rows = "--rows=";
1841 if (cmd.startsWith(rows)) {
1842 opts.perClientRunRows = Integer.parseInt(cmd.substring(rows.length()));
1843 continue;
1844 }
1845
1846 final String sampleRate = "--sampleRate=";
1847 if (cmd.startsWith(sampleRate)) {
1848 opts.sampleRate = Float.parseFloat(cmd.substring(sampleRate.length()));
1849 continue;
1850 }
1851
1852 final String table = "--table=";
1853 if (cmd.startsWith(table)) {
1854 opts.tableName = cmd.substring(table.length());
1855 continue;
1856 }
1857
1858 final String startRow = "--startRow=";
1859 if (cmd.startsWith(startRow)) {
1860 opts.startRow = Integer.parseInt(cmd.substring(startRow.length()));
1861 continue;
1862 }
1863
1864 final String compress = "--compress=";
1865 if (cmd.startsWith(compress)) {
1866 opts.compression = Compression.Algorithm.valueOf(cmd.substring(compress.length()));
1867 continue;
1868 }
1869
1870 final String traceRate = "--traceRate=";
1871 if (cmd.startsWith(traceRate)) {
1872 opts.traceRate = Double.parseDouble(cmd.substring(traceRate.length()));
1873 continue;
1874 }
1875
1876 final String blockEncoding = "--blockEncoding=";
1877 if (cmd.startsWith(blockEncoding)) {
1878 opts.blockEncoding = DataBlockEncoding.valueOf(cmd.substring(blockEncoding.length()));
1879 continue;
1880 }
1881
1882 final String flushCommits = "--flushCommits=";
1883 if (cmd.startsWith(flushCommits)) {
1884 opts.flushCommits = Boolean.parseBoolean(cmd.substring(flushCommits.length()));
1885 continue;
1886 }
1887
1888 final String writeToWAL = "--writeToWAL=";
1889 if (cmd.startsWith(writeToWAL)) {
1890 opts.writeToWAL = Boolean.parseBoolean(cmd.substring(writeToWAL.length()));
1891 continue;
1892 }
1893
1894 final String presplit = "--presplit=";
1895 if (cmd.startsWith(presplit)) {
1896 opts.presplitRegions = Integer.parseInt(cmd.substring(presplit.length()));
1897 continue;
1898 }
1899
1900 final String inMemory = "--inmemory=";
1901 if (cmd.startsWith(inMemory)) {
1902 opts.inMemoryCF = Boolean.parseBoolean(cmd.substring(inMemory.length()));
1903 continue;
1904 }
1905
1906 final String autoFlush = "--autoFlush=";
1907 if (cmd.startsWith(autoFlush)) {
1908 opts.autoFlush = Boolean.parseBoolean(cmd.substring(autoFlush.length()));
1909 continue;
1910 }
1911
1912 final String onceCon = "--oneCon=";
1913 if (cmd.startsWith(onceCon)) {
1914 opts.oneCon = Boolean.parseBoolean(cmd.substring(onceCon.length()));
1915 continue;
1916 }
1917
1918 final String latency = "--latency";
1919 if (cmd.startsWith(latency)) {
1920 opts.reportLatency = true;
1921 continue;
1922 }
1923
1924 final String multiGet = "--multiGet=";
1925 if (cmd.startsWith(multiGet)) {
1926 opts.multiGet = Integer.parseInt(cmd.substring(multiGet.length()));
1927 continue;
1928 }
1929
1930 final String useTags = "--usetags=";
1931 if (cmd.startsWith(useTags)) {
1932 opts.useTags = Boolean.parseBoolean(cmd.substring(useTags.length()));
1933 continue;
1934 }
1935
1936 final String noOfTags = "--numoftags=";
1937 if (cmd.startsWith(noOfTags)) {
1938 opts.noOfTags = Integer.parseInt(cmd.substring(noOfTags.length()));
1939 continue;
1940 }
1941
1942 final String filterOutAll = "--filterAll";
1943 if (cmd.startsWith(filterOutAll)) {
1944 opts.filterAll = true;
1945 continue;
1946 }
1947
1948 final String size = "--size=";
1949 if (cmd.startsWith(size)) {
1950 opts.size = Float.parseFloat(cmd.substring(size.length()));
1951 continue;
1952 }
1953
1954 final String splitPolicy = "--splitPolicy=";
1955 if (cmd.startsWith(splitPolicy)) {
1956 opts.splitPolicy = cmd.substring(splitPolicy.length());
1957 continue;
1958 }
1959
1960 final String randomSleep = "--randomSleep=";
1961 if (cmd.startsWith(randomSleep)) {
1962 opts.randomSleep = Integer.parseInt(cmd.substring(randomSleep.length()));
1963 continue;
1964 }
1965
1966 final String bloomFilter = "--bloomFilter=";
1967 if (cmd.startsWith(bloomFilter)) {
1968 opts.bloomType = BloomType.valueOf(cmd.substring(bloomFilter.length()));
1969 continue;
1970 }
1971
1972 final String valueSize = "--valueSize=";
1973 if (cmd.startsWith(valueSize)) {
1974 opts.valueSize = Integer.parseInt(cmd.substring(valueSize.length()));
1975 continue;
1976 }
1977
1978 final String valueRandom = "--valueRandom";
1979 if (cmd.startsWith(valueRandom)) {
1980 opts.valueRandom = true;
1981 if (opts.valueZipf) {
1982 throw new IllegalStateException("Either valueZipf or valueRandom but not both");
1983 }
1984 continue;
1985 }
1986
1987 final String valueZipf = "--valueZipf";
1988 if (cmd.startsWith(valueZipf)) {
1989 opts.valueZipf = true;
1990 if (opts.valueRandom) {
1991 throw new IllegalStateException("Either valueZipf or valueRandom but not both");
1992 }
1993 continue;
1994 }
1995
1996 final String period = "--period=";
1997 if (cmd.startsWith(period)) {
1998 opts.period = Integer.parseInt(cmd.substring(period.length()));
1999 continue;
2000 }
2001
2002 final String addColumns = "--addColumns=";
2003 if (cmd.startsWith(addColumns)) {
2004 opts.addColumns = Boolean.parseBoolean(cmd.substring(addColumns.length()));
2005 continue;
2006 }
2007
2008 final String columns = "--columns=";
2009 if (cmd.startsWith(columns)) {
2010 opts.columns = Integer.parseInt(cmd.substring(columns.length()));
2011 continue;
2012 }
2013
2014 final String caching = "--caching=";
2015 if (cmd.startsWith(caching)) {
2016 opts.caching = Integer.parseInt(cmd.substring(caching.length()));
2017 continue;
2018 }
2019
2020 if (isCommandClass(cmd)) {
2021 opts.cmdName = cmd;
2022 opts.numClientThreads = Integer.parseInt(args.remove());
2023 int rowsPerGB = getRowsPerGB(opts);
2024 if (opts.size != DEFAULT_OPTS.size &&
2025 opts.perClientRunRows != DEFAULT_OPTS.perClientRunRows) {
2026 throw new IllegalArgumentException(rows + " and " + size + " are mutually exclusive arguments.");
2027 }
2028 if (opts.size != DEFAULT_OPTS.size) {
2029
2030 opts.totalRows = (int) opts.size * rowsPerGB;
2031 opts.perClientRunRows = opts.totalRows / opts.numClientThreads;
2032 } else if (opts.perClientRunRows != DEFAULT_OPTS.perClientRunRows) {
2033
2034 opts.totalRows = opts.perClientRunRows * opts.numClientThreads;
2035 opts.size = opts.totalRows / rowsPerGB;
2036 }
2037 break;
2038 } else {
2039 printUsageAndExit("ERROR: Unrecognized option/command: " + cmd, -1);
2040 }
2041
2042
2043 System.err.println("Error: Wrong option or command: " + cmd);
2044 args.add(cmd);
2045 break;
2046 }
2047 return opts;
2048 }
2049
2050 static int getRowsPerGB(final TestOptions opts) {
2051 return ONE_GB / ((opts.valueRandom? opts.valueSize/2: opts.valueSize) * opts.getColumns());
2052 }
2053
2054 @Override
2055 public int run(String[] args) throws Exception {
2056
2057
2058 int errCode = -1;
2059 if (args.length < 1) {
2060 printUsage();
2061 return errCode;
2062 }
2063
2064 try {
2065 LinkedList<String> argv = new LinkedList<String>();
2066 argv.addAll(Arrays.asList(args));
2067 TestOptions opts = parseOpts(argv);
2068
2069
2070 if (!argv.isEmpty()) {
2071 errCode = 0;
2072 printUsage();
2073 return errCode;
2074 }
2075
2076
2077 if (opts.numClientThreads <= 0) {
2078 throw new IllegalArgumentException("Number of clients must be > 0");
2079 }
2080
2081 Class<? extends Test> cmdClass = determineCommandClass(opts.cmdName);
2082 if (cmdClass != null) {
2083 runTest(cmdClass, opts);
2084 errCode = 0;
2085 }
2086
2087 } catch (Exception e) {
2088 e.printStackTrace();
2089 }
2090
2091 return errCode;
2092 }
2093
2094 private static boolean isCommandClass(String cmd) {
2095 return COMMANDS.containsKey(cmd);
2096 }
2097
2098 private static Class<? extends Test> determineCommandClass(String cmd) {
2099 CmdDescriptor descriptor = COMMANDS.get(cmd);
2100 return descriptor != null ? descriptor.getCmdClass() : null;
2101 }
2102
2103 public static void main(final String[] args) throws Exception {
2104 int res = ToolRunner.run(new PerformanceEvaluation(HBaseConfiguration.create()), args);
2105 System.exit(res);
2106 }
2107 }