Author: liyin Date: Tue May 7 18:31:06 2013 New Revision: 1480004 URL: http://svn.apache.org/r1480004 Log: [HBASE-6404] Collect p95 stats for get/put/delete/next
Author: gauravm Summary: Collect p95 stats for get / put / delete / next operations by having a histogram and a percentile metric (with percentile = 0.95) for each operation, respectively. The method of stat collections is similar to D715751. Test Plan: Unit tests and checked on Titan Shadow. Reviewers: liyintang, adela, manukranthk Reviewed By: manukranthk CC: hbase-eng@ Differential Revision: https://phabricator.fb.com/D779670 Task ID: 1200149 Added: hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/regionserver/metrics/RpcMetricWrapper.java hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/regionserver/metrics/TestRpcMetricWrapper.java Modified: hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/ipc/HBaseRpcMetrics.java hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/regionserver/metrics/PercentileMetric.java hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/Histogram.java Modified: hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/ipc/HBaseRpcMetrics.java URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/ipc/HBaseRpcMetrics.java?rev=1480004&r1=1480003&r2=1480004&view=diff ============================================================================== --- hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/ipc/HBaseRpcMetrics.java (original) +++ hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/ipc/HBaseRpcMetrics.java Tue May 7 18:31:06 2013 @@ -22,12 +22,14 @@ package org.apache.hadoop.hbase.ipc; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.regionserver.metrics.RpcMetricWrapper; import org.apache.hadoop.metrics.MetricsContext; import org.apache.hadoop.metrics.MetricsRecord; import org.apache.hadoop.metrics.MetricsUtil; import org.apache.hadoop.metrics.Updater; import org.apache.hadoop.metrics.util.MetricsRegistry; import org.apache.hadoop.metrics.util.MetricsTimeVaryingRate; +import java.util.concurrent.ConcurrentHashMap; /** * @@ -60,7 +62,16 @@ public class HBaseRpcMetrics implements rpcStatistics = new HBaseRPCStatistics(this.registry, hostName, port); } - + /** + * A metric name to RPCMetric object map. We do not keep RpcMetricWrapper objects + * in the registry, because firstly the RpcMetricWrapper class does not extend + * MetricsBase (since it is a container class, and not an atomic metric), + * and secondly we do not want to confuse between the RpcMetricWrapper name and the + * name of the rate metric contained within a RpcMetricWrapper object, when + * querying the registry. + */ + private final ConcurrentHashMap<String, RpcMetricWrapper> rpcMetricWrapperMap = + new ConcurrentHashMap<String, RpcMetricWrapper>(); /** * The metrics variables are public: * - they can be set directly by calling their set/inc methods @@ -71,18 +82,25 @@ public class HBaseRpcMetrics implements public MetricsTimeVaryingRate rpcQueueTime = new MetricsTimeVaryingRate("RpcQueueTime", registry); public MetricsTimeVaryingRate rpcProcessingTime = new MetricsTimeVaryingRate("RpcProcessingTime", registry); - //public Map <String, MetricsTimeVaryingRate> metricsList = Collections.synchronizedMap(new HashMap<String, MetricsTimeVaryingRate>()); - - - private MetricsTimeVaryingRate get(String key) { - return (MetricsTimeVaryingRate) registry.get(key); + private RpcMetricWrapper get(String name) { + return rpcMetricWrapperMap.get(name); } - private MetricsTimeVaryingRate create(String key) { - return new MetricsTimeVaryingRate(key, this.registry); + + private RpcMetricWrapper create(String name) { + RpcMetricWrapper m = new RpcMetricWrapper(name, this.registry); + rpcMetricWrapperMap.put(name, m); + return m; } + /** + * Increment a metric. This would increment two different metrics. + * The first one being the standard MetricsTimeVaryingRate, and the + * other will be a PercentMetric. + * @param name + * @param amt + */ public void inc(String name, int amt) { - MetricsTimeVaryingRate m = get(name); + RpcMetricWrapper m = get(name); if (m == null) { synchronized (this) { if ((m = get(name)) == null) { @@ -104,13 +122,13 @@ public class HBaseRpcMetrics implements rpcProcessingTime.pushMetric(metricsRecord); rpcProcessingTime.resetMinMax(); - synchronized (registry) { - // Iterate through the registry to propagate the different rpc metrics. + synchronized (rpcMetricWrapperMap) { + // Iterate through the rpcMetricWrapperMap to propagate the different rpc metrics. - for (String metricName : registry.getKeyList() ) { - MetricsTimeVaryingRate value = (MetricsTimeVaryingRate) registry.get(metricName); + for (String metricName : rpcMetricWrapperMap.keySet()) { + RpcMetricWrapper value = get(metricName); value.pushMetric(metricsRecord); - value.resetMinMax(); + value.resetMetric(); } } metricsRecord.update(); Modified: hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/regionserver/metrics/PercentileMetric.java URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/regionserver/metrics/PercentileMetric.java?rev=1480004&r1=1480003&r2=1480004&view=diff ============================================================================== --- hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/regionserver/metrics/PercentileMetric.java (original) +++ hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/regionserver/metrics/PercentileMetric.java Tue May 7 18:31:06 2013 @@ -19,6 +19,7 @@ package org.apache.hadoop.hbase.regionse import org.apache.hadoop.hbase.util.Histogram; import org.apache.hadoop.metrics.util.MetricsLongValue; import org.apache.hadoop.metrics.util.MetricsRegistry; +import org.apache.hadoop.metrics.MetricsRecord; /** * Used the org.apache.hadoop.hbase.util.Histogram to maintain time varying @@ -26,13 +27,14 @@ import org.apache.hadoop.metrics.util.Me * a stream of data supplied to the PercentileMetric without actually * storing the data. */ -public class PercentileMetric extends MetricsLongValue{ +public class PercentileMetric extends MetricsLongValue { public static final int HISTOGRAM_NUM_BUCKETS_DEFAULT = 20; public static final double HISTOGRAM_MINVALUE_DEFAULT = 0.0; public static final double HISTOGRAM_MAXVALUE_DEFAULT = 1000000000.0; public static final double DEFAULT_PERCENTILE = 99.0; public static final long DEFAULT_SAMPLE_WINDOW = 60; public static final double P99 = 99.0; + public static final double P95 = 95.0; private int numBuckets; private double percentile; @@ -91,4 +93,21 @@ public class PercentileMetric extends Me public void refresh() { underlyingHistogram.refresh(this.numBuckets); } + + /** + * Add a value in the underlying histogram. + * @param value The value to be added. + */ + public void addValueInHistogram(long value) { + underlyingHistogram.addValue(value); + } + + /** + * Push the metric value to the <code>MetricsRecord</code> object + * @param mr + */ + public void pushMetric(final MetricsRecord mr) { + this.updateMetric(); + mr.setMetric(getName(), (long)getValue()); + } } Added: hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/regionserver/metrics/RpcMetricWrapper.java URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/regionserver/metrics/RpcMetricWrapper.java?rev=1480004&view=auto ============================================================================== --- hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/regionserver/metrics/RpcMetricWrapper.java (added) +++ hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/regionserver/metrics/RpcMetricWrapper.java Tue May 7 18:31:06 2013 @@ -0,0 +1,74 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.hadoop.hbase.regionserver.metrics; + +import org.apache.hadoop.hbase.util.Histogram; +import org.apache.hadoop.metrics.util.MetricsTimeVaryingRate; +import org.apache.hadoop.metrics.util.MetricsRegistry; +import org.apache.hadoop.metrics.MetricsRecord; + +/** + * This is a container class for metrics associated with RPC calls. It keeps a + * rate metric, which provides the min/max/avg for the metric. It also contains + * a percentile metric, which provides the approx value at a specific + * percentile (in this case, 95.0), in the distribution of the metric. + */ +public class RpcMetricWrapper { + public static final double percentile = PercentileMetric.P95; + private PercentileMetric percentileMetric; + private MetricsTimeVaryingRate rateMetric; + + /** + * @param key The key for RpcMetricWrapper + * @param metricsRegistry The appropriate metric registry + */ + public RpcMetricWrapper(String key, MetricsRegistry metricsRegistry) { + this.percentileMetric = + new PercentileMetric((key + "_p" + (long)percentile), + metricsRegistry, new Histogram(), percentile, + PercentileMetric.HISTOGRAM_NUM_BUCKETS_DEFAULT); + this.rateMetric = new MetricsTimeVaryingRate(key, metricsRegistry); + } + + /** + * Increment the metric by <code>amt</code>. + * @param amt + */ + public void inc(int amt) { + rateMetric.inc(amt); + percentileMetric.addValueInHistogram(amt); + } + + /** + * Push the metric to the MetricsRecord. + * @param metricsRecord + */ + public void pushMetric(MetricsRecord metricsRecord) { + percentileMetric.pushMetric(metricsRecord); + rateMetric.pushMetric(metricsRecord); + } + + /** + * Reset the metric + */ + public void resetMetric() { + percentileMetric.refresh(); + rateMetric.resetMinMax(); + } +} Modified: hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/Histogram.java URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/Histogram.java?rev=1480004&r1=1480003&r2=1480004&view=diff ============================================================================== --- hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/Histogram.java (original) +++ hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/util/Histogram.java Tue May 7 18:31:06 2013 @@ -17,6 +17,7 @@ */ package org.apache.hadoop.hbase.util; +import org.apache.hadoop.hbase.regionserver.metrics.PercentileMetric; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -51,6 +52,16 @@ public class Histogram { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public static final Log LOG = LogFactory.getLog(Histogram.class.getName()); + /** + * Create a histogram with the default values of number of buckets, + * and min/max for the values. + */ + public Histogram() { + this(PercentileMetric.HISTOGRAM_NUM_BUCKETS_DEFAULT, + PercentileMetric.HISTOGRAM_MINVALUE_DEFAULT, + PercentileMetric.HISTOGRAM_MAXVALUE_DEFAULT); + } + // Bucket indexing is from 1 to N public Histogram(int numBuckets, Double minValue, Double maxValue) { if (numBuckets < 1 || minValue >= maxValue) { Added: hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/regionserver/metrics/TestRpcMetricWrapper.java URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/regionserver/metrics/TestRpcMetricWrapper.java?rev=1480004&view=auto ============================================================================== --- hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/regionserver/metrics/TestRpcMetricWrapper.java (added) +++ hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/regionserver/metrics/TestRpcMetricWrapper.java Tue May 7 18:31:06 2013 @@ -0,0 +1,111 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.hadoop.hbase.regionserver.metrics; + +import junit.framework.TestCase; +import org.apache.hadoop.metrics.MetricsRecord; +import org.apache.hadoop.metrics.util.MetricsRegistry; + +import java.util.HashMap; + +/** + * A dummy MetricsRecord class which essentially just maintains a map of + * metric name to value (Long, in this case). We will use it to check if the + * RpcMetricsWrapper class properly sets values in the actual metrics record. + */ +class DummyMetricsRecord implements MetricsRecord { + private HashMap<String, Long> metricMap = new HashMap<String, Long>(); + + /** Do not need to implement these functions */ + public String getRecordName() { return ""; } + public void setTag(String key, String value) { } + public void setTag(String tagName, int tagValue) {} + public void setTag(String tagName, long tagValue) {} + public void setTag(String tagName, short tagValue) {} + public void setTag(String tagName, byte tagValue) {} + public void removeTag(String tagName) {} + public void setMetric(String metricName, int metricValue) {} + public void setMetric(String metricName, short metricValue) {} + public void setMetric(String metricName, byte metricValue) {} + public void setMetric(String metricName, float metricValue) {} + public void incrMetric(String metricName, long metricValue) {} + public void incrMetric(String metricName, short metricValue) {} + public void incrMetric(String metricName, byte metricValue) {} + public void incrMetric(String metricName, float metricValue) {} + public void update() {} + public void remove() {} + + /** + * Get the metric value + * @param metricName + * @return + */ + public Long getMetric(String metricName) { + return metricMap.get(metricName); + } + + /** + * Set the metric value + * @param metricName + * @param metricValue + */ + public void setMetric(String metricName, long metricValue) { + metricMap.put(metricName, Long.valueOf(metricValue)); + } + + /** + * Increment the metric + * @param metricName + * @param metricValue + */ + public void incrMetric(String metricName, int metricValue) { + Long value = getMetric(metricName); + if (value == null) { + setMetric(metricName, (long)metricValue); + } else { + setMetric(metricName, value.longValue() + (long)metricValue); + } + } +} + +public class TestRpcMetricWrapper extends TestCase { + private MetricsRegistry registry = new MetricsRegistry(); + + /** + * Test if the RpcMetricWrapper pushes the metric name properly to the + * metric record object. + */ + public void testRpcMetricPush() { + String metricName = "foo"; + DummyMetricsRecord dummyMetricsRecord = new DummyMetricsRecord(); + RpcMetricWrapper rpcMetricWrapper = + new RpcMetricWrapper(metricName, registry); + + for (int i = 1; i <= 100; i++) { + rpcMetricWrapper.inc(i); + } + rpcMetricWrapper.pushMetric(dummyMetricsRecord); + + assertEquals(50, dummyMetricsRecord.getMetric("foo_avg_time").longValue()); + assertEquals(1, dummyMetricsRecord.getMetric("foo_min").longValue()); + assertEquals(100, dummyMetricsRecord.getMetric("foo_max").longValue()); + assertEquals(100, dummyMetricsRecord.getMetric("foo_num_ops").longValue()); + assertEquals(95, dummyMetricsRecord.getMetric("foo_p95").longValue()); + } +}
