View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */ 
18  
19  package org.apache.hadoop.hbase.metrics.histogram;
20  
21  import java.util.concurrent.atomic.AtomicLong;
22  import java.util.concurrent.atomic.AtomicReference;
23  
24  import org.apache.hadoop.metrics.MetricsRecord;
25  import org.apache.hadoop.metrics.util.MetricsBase;
26  import org.apache.hadoop.metrics.util.MetricsRegistry;
27  
28  import com.yammer.metrics.stats.Sample;
29  import com.yammer.metrics.stats.Snapshot;
30  import com.yammer.metrics.stats.UniformSample;
31  import com.yammer.metrics.stats.ExponentiallyDecayingSample;
32  
33  @Deprecated
34  public class MetricsHistogram extends MetricsBase {
35    
36    // 1028 items implies 99.9% CI w/ 5% margin of error 
37    // (assuming a normal distribution on the underlying data)
38    private static final int DEFAULT_SAMPLE_SIZE = 1028;
39  
40    // the bias towards sampling from more recent data. 
41    // Per Cormode et al. an alpha of 0.015 strongly biases to the last 5 minutes
42    private static final double DEFAULT_ALPHA = 0.015;
43    public static final String NUM_OPS_METRIC_NAME = "_num_ops";
44    public static final String MIN_METRIC_NAME = "_min";
45    public static final String MAX_METRIC_NAME = "_max";
46    public static final String MEAN_METRIC_NAME = "_mean";
47    public static final String STD_DEV_METRIC_NAME = "_std_dev";
48    public static final String MEDIAN_METRIC_NAME = "_median";
49    public static final String SEVENTY_FIFTH_PERCENTILE_METRIC_NAME = "_75th_percentile";
50    public static final String NINETY_FIFTH_PERCENTILE_METRIC_NAME = "_95th_percentile";
51    public static final String NINETY_NINETH_PERCENTILE_METRIC_NAME = "_99th_percentile";
52  
53    /**
54     * Constructor to create a new histogram metric
55     * @param nam           the name to publish the metric under
56     * @param registry      where the metrics object will be registered
57     * @param description   the metric's description
58     * @param forwardBiased true if you want this histogram to give more 
59     *                      weight to recent data, 
60     *                      false if you want all data to have uniform weight
61     */
62    public MetricsHistogram(final String nam, final MetricsRegistry registry, 
63        final String description, boolean forwardBiased) {
64      super(nam, description);
65  
66      this.min = new AtomicLong();
67      this.max = new AtomicLong();
68      this.sum = new AtomicLong();
69      this.sample = forwardBiased ? 
70          new ExponentiallyDecayingSample(DEFAULT_SAMPLE_SIZE, DEFAULT_ALPHA) 
71      : new UniformSample(DEFAULT_SAMPLE_SIZE);
72  
73      this.variance =  new AtomicReference<double[]>(new double[]{-1, 0});
74      this.count = new AtomicLong();
75  
76      this.clear();
77  
78      if (registry != null) {
79        registry.add(nam, this);      
80      }
81    }
82  
83    /**
84     * Constructor create a new (forward biased) histogram metric
85     * @param nam         the name to publish the metric under
86     * @param registry    where the metrics object will be registered
87     * @param description the metric's description
88     */
89    public MetricsHistogram(final String nam, MetricsRegistry registry, 
90        final String description) {
91      this(nam, registry, NO_DESCRIPTION, true);
92    }
93      
94    /**
95     * Constructor - create a new (forward biased) histogram metric
96     * @param nam the name of the metrics to be used to publish the metric
97     * @param registry - where the metrics object will be registered
98     */
99    public MetricsHistogram(final String nam, MetricsRegistry registry) {
100     this(nam, registry, NO_DESCRIPTION);
101   }
102 
103   private final Sample sample;
104   private final AtomicLong min;
105   private final AtomicLong max;
106   private final AtomicLong sum;
107 
108   // these are for computing a running-variance, 
109   // without letting floating point errors accumulate via Welford's algorithm
110   private final AtomicReference<double[]> variance;
111   private final AtomicLong count;
112 
113   /**
114    * Clears all recorded values.
115    */
116   public void clear() {
117     this.sample.clear();
118     this.count.set(0);
119     this.max.set(Long.MIN_VALUE);
120     this.min.set(Long.MAX_VALUE);
121     this.sum.set(0);
122     variance.set(new double[]{-1, 0});
123   }
124 
125   public void update(int val) {
126     update((long) val);
127   }
128 
129   public void update(final long val) {
130     count.incrementAndGet();
131     sample.update(val);
132     setMax(val);
133     setMin(val);
134     sum.getAndAdd(val);
135     updateVariance(val);
136   }
137 
138   private void setMax(final long potentialMax) {
139     boolean done = false;
140     while (!done) {
141       final long currentMax = max.get();
142       done = currentMax >= potentialMax 
143           || max.compareAndSet(currentMax, potentialMax);
144     }
145   }
146 
147   private void setMin(long potentialMin) {
148     boolean done = false;
149     while (!done) {
150       final long currentMin = min.get();
151       done = currentMin <= potentialMin 
152           || min.compareAndSet(currentMin, potentialMin);
153     }
154   }
155 
156   private void updateVariance(long value) {
157     boolean done = false;
158     while (!done) {
159       final double[] oldValues = variance.get();
160       final double[] newValues = new double[2];
161       if (oldValues[0] == -1) {
162         newValues[0] = value;
163         newValues[1] = 0;
164       } else {
165         final double oldM = oldValues[0];
166         final double oldS = oldValues[1];
167 
168         final double newM = oldM + ((value - oldM) / getCount());
169         final double newS = oldS + ((value - oldM) * (value - newM));
170 
171         newValues[0] = newM;
172         newValues[1] = newS;
173       }
174       done = variance.compareAndSet(oldValues, newValues);
175     }
176   }
177 
178 
179   public long getCount() {
180     return count.get();
181   }
182 
183   public long getMax() {
184     if (getCount() > 0) {
185       return max.get();
186     }
187     return 0L;
188   }
189 
190   public long getMin() {
191     if (getCount() > 0) {
192       return min.get();
193     }
194     return 0L;
195   }
196 
197   public double getMean() {
198     if (getCount() > 0) {
199       return sum.get() / (double) getCount();
200     }
201     return 0.0;
202   }
203 
204   public double getStdDev() {
205     if (getCount() > 0) {
206       return Math.sqrt(getVariance());
207     }
208     return 0.0;
209   }
210 
211   public Snapshot getSnapshot() {
212     return sample.getSnapshot();
213   }
214 
215   private double getVariance() {
216     if (getCount() <= 1) {
217       return 0.0;
218     }
219     return variance.get()[1] / (getCount() - 1);
220   }
221 
222   @Override
223   public void pushMetric(MetricsRecord mr) {
224     final Snapshot s = this.getSnapshot();
225     mr.setMetric(getName() + NUM_OPS_METRIC_NAME, this.getCount());
226     mr.setMetric(getName() + MIN_METRIC_NAME, this.getMin());
227     mr.setMetric(getName() + MAX_METRIC_NAME, this.getMax());
228 
229     mr.setMetric(getName() + MEAN_METRIC_NAME, (float) this.getMean());
230     mr.setMetric(getName() + STD_DEV_METRIC_NAME, (float) this.getStdDev());
231 
232     mr.setMetric(getName() + MEDIAN_METRIC_NAME, (float) s.getMedian());
233     mr.setMetric(getName() + SEVENTY_FIFTH_PERCENTILE_METRIC_NAME,
234         (float) s.get75thPercentile());
235     mr.setMetric(getName() + NINETY_FIFTH_PERCENTILE_METRIC_NAME,
236         (float) s.get95thPercentile());
237     mr.setMetric(getName() + NINETY_NINETH_PERCENTILE_METRIC_NAME,
238         (float) s.get99thPercentile());
239   }
240 }