/* * Copyright 2012 NGDATA nv * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.lilyproject.testclientfw; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionBuilder; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.client.HBaseAdmin; import org.apache.hadoop.hbase.client.HConnectionManager; import org.joda.time.DateTime; import org.lilyproject.cli.BaseZkCliTool; import org.lilyproject.cli.OptionUtil; import org.lilyproject.clientmetrics.HBaseMetrics; import org.lilyproject.clientmetrics.HBaseMetricsPlugin; import org.lilyproject.clientmetrics.LilyMetrics; import org.lilyproject.clientmetrics.ListMetricsPlugin; import org.lilyproject.clientmetrics.Metrics; import org.lilyproject.util.concurrent.WaitPolicy; import org.lilyproject.util.io.Closer; import org.lilyproject.util.zookeeper.StateWatchingZooKeeper; import org.lilyproject.util.zookeeper.ZooKeeperItf; public abstract class BaseTestTool extends BaseZkCliTool { private Option workersOption; private Option verboseOption; private Option hbaseMetricsOption; private Option lilyMetricsOption; protected int workers; protected boolean verbose; protected boolean useHbaseMetrics; protected boolean useLilyMetrics; protected HBaseMetrics hbaseMetrics; private LilyMetrics lilyMetrics; protected PrintStream metricsStream; protected ThreadPoolExecutor executor; protected Metrics metrics; private ZooKeeperItf zk; private Configuration hbaseConf; protected int getDefaultWorkers() { return 2; } @Override @SuppressWarnings("static-access") public List<Option> getOptions() { List<Option> options = super.getOptions(); workersOption = OptionBuilder .withArgName("count") .hasArg() .withDescription("Number of workers (threads)") .withLongOpt("workers") .create("w"); options.add(workersOption); verboseOption = OptionBuilder .withDescription("Be verbose") .withLongOpt("verbose") .create("vb"); options.add(verboseOption); hbaseMetricsOption = OptionBuilder .withDescription("Enable HBase metrics options (requires JMX on default port 10102)") .withLongOpt("hbase-metrics") .create("hm"); options.add(hbaseMetricsOption); lilyMetricsOption = OptionBuilder .withDescription("Enable Lily metrics options (requires JMX on default port 10202)") .withLongOpt("lily-metrics") .create("lm"); options.add(lilyMetricsOption); return options; } @Override protected int processOptions(CommandLine cmd) throws Exception { int result = super.processOptions(cmd); if (result != 0) { return result; } workers = OptionUtil.getIntOption(cmd, workersOption, getDefaultWorkers()); if (cmd.hasOption(verboseOption.getOpt())) { verbose = true; } if (cmd.hasOption(hbaseMetricsOption.getOpt())) { useHbaseMetrics = true; } if (cmd.hasOption(lilyMetricsOption.getOpt())) { useLilyMetrics = true; } return 0; } @Override protected void cleanup() { Closer.close(zk); // Close any HBase connections used for the metrics HConnectionManager.deleteAllConnections(true); super.cleanup(); } public void setupMetrics() throws Exception { String metricsFileName = getClass().getSimpleName() + "-metrics"; System.out.println(); System.out.println("Setting up metrics to file " + metricsFileName); File metricsFile = Util.getOutputFileRollOldOne(metricsFileName); HBaseAdmin hbaseAdmin = new HBaseAdmin(getHBaseConf()); hbaseMetrics = new HBaseMetrics(hbaseAdmin); lilyMetrics = new LilyMetrics(getZooKeeper()); ListMetricsPlugin plugins = new ListMetricsPlugin(); HBaseMetricsPlugin metricsPlugin = new HBaseMetricsPlugin(hbaseMetrics, hbaseAdmin, useHbaseMetrics); plugins.add(metricsPlugin); addMetricsPlugins(plugins); metricsStream = new PrintStream(new FileOutputStream(metricsFile)); metrics = new Metrics(metricsStream, plugins); metrics.setThreadCount(workers); metrics.startHeader(); metricsStream.println("Now: " + new DateTime()); metricsStream.println("Number of threads used: " + workers); metricsStream.println("More threads might increase number of ops/sec, but typically also increases time spent"); metricsStream.println("in each individual operation."); metricsStream.println(); metricsStream.println("About the ops/sec (if present):"); metricsStream.println(" - interval ops/sec = number of ops by the complete time of the interval"); metricsStream.println(" - real ops/sec = number of ops by the time they took"); metricsStream.println("The interval ops/sec looks at how many of the operations have been done in"); metricsStream.println("the interval, but includes thus the time spent doing other kinds of operations"); metricsStream.println("or the overhead of the test tool itself. Therefore, you would expect the"); metricsStream.println("real ops/sec to be better (higher) than the interval ops/sec. But when using"); metricsStream.println("multiple threads, this is not always the case, since each thread runs for the"); metricsStream.println("duration of the interval, e.g. 3 threads running for 30s makes 90s time passed"); metricsStream.println("by the threads together. Another issue is that sometimes operations are very"); metricsStream.println("quick but that their time is measured with a too low granularity."); metricsStream.println(); metricsStream.println("About hbaseLoad (if present): this is currently the same as the number of regions"); metricsStream.println(" deployed on the region server."); metricsStream.println("About hbaseRequestCount (if present): this is number of HBase requests per seconds"); metricsStream.println(); outputGeneralMetricReports(); metrics.endHeader(); System.out.println("Metrics ready, summary will be outputted every " + (metrics.getIntervalDuration() / 1000) + "s"); System.out.println("Follow them using tail -f " + metricsFileName); System.out.println(); } protected void addMetricsPlugins(ListMetricsPlugin plugins) { } public void finishMetrics() throws Exception { metrics.finish(); metrics.startFooter(); outputGeneralMetricReports(); metrics.endFooter(); hbaseMetrics.close(); lilyMetrics.close(); } private void outputGeneralMetricReports() throws Exception { hbaseMetrics.outputHBaseState(metricsStream); hbaseMetrics.outputRegionCountByServer(metricsStream); if (useHbaseMetrics) { hbaseMetrics.outputRegionServersInfo(metricsStream); } if (useLilyMetrics) { lilyMetrics.outputLilyServerInfo(metricsStream); } } public void startExecutor() { executor = new ThreadPoolExecutor(workers, workers, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100)); executor.setRejectedExecutionHandler(new WaitPolicy()); } public void stopExecutor() throws InterruptedException { executor.shutdown(); // There might be quite some jobs waiting in the queue, and we don't care how long they take to run // to an end boolean successfulFinish = executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); if (!successfulFinish) { System.out.println("Executor did not end successfully"); } executor = null; } public Configuration getHBaseConf() { if (hbaseConf == null) { hbaseConf = HBaseConfiguration.create(); // TODO if (zkConnectionString.contains(":")) { System.err.println("ATTENTION: do not include port numbers in zookeeper connection string when using features/tests that use HBase."); } hbaseConf.set("hbase.zookeeper.quorum", zkConnectionString); } return hbaseConf; } protected ZooKeeperItf getZooKeeper() throws IOException { if (zk == null) { zk = new StateWatchingZooKeeper(zkConnectionString, zkSessionTimeout); } return zk; } }