/**
* Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com)
*
* 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 com.linkedin.pinot.common.metrics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import com.yammer.metrics.core.Histogram;
import com.yammer.metrics.core.Metric;
import com.yammer.metrics.core.MetricName;
import com.yammer.metrics.core.MetricProcessor;
import com.yammer.metrics.core.Sampling;
import com.yammer.metrics.core.Summarizable;
import com.yammer.metrics.stats.Snapshot;
/**
*
* Aggregated Histogram which aggregates and provides Uniform Sampling Histograms.
* We can have multi-level aggregations.
*
*
*/
public class AggregatedHistogram<T extends Sampling> implements Sampling, Summarizable, Metric {
private final List<T> _histograms = new CopyOnWriteArrayList<T>();
private static final long DEFAULT_REFRESH_MS = 60 * 1000L; // 1 minute
// Refresh Delay config
private final long _refreshMs;
// Last Refreshed timestamp
private volatile long _lastRefreshedTime;
// Sampling stats
private volatile double _max;
private volatile double _min;
private volatile double _mean;
private volatile double _stdDev;
private volatile double _sum;
//Sanpshot
private volatile Snapshot _snapshot;
public AggregatedHistogram(long refreshMs) {
_refreshMs = refreshMs;
}
public AggregatedHistogram() {
_refreshMs = DEFAULT_REFRESH_MS;
}
/**
* Add Collection of metrics to be aggregated
* @return this instance
*/
public AggregatedHistogram<T> addAll(Collection<T> histograms) {
_histograms.addAll(histograms);
return this;
}
/**
* Add a metric to be aggregated
* @return this instance
*/
public AggregatedHistogram<T> add(T histogram) {
_histograms.add(histogram);
return this;
}
/**
* Remove a metric which was already added
* @return true if the metric was present in the list
*/
public boolean remove(T histogram) {
return _histograms.remove(histogram);
}
/**
* Check elapsed time since last refresh and only refresh if time difference is
* greater than threshold.
*/
private void refreshIfElapsed() {
long currentTime = System.currentTimeMillis();
if (currentTime - _lastRefreshedTime > _refreshMs && !_histograms.isEmpty()) {
refresh();
_lastRefreshedTime = currentTime;
}
}
/**
* update all stats using underlying histograms
*/
public void refresh() {
List<Double> values = new ArrayList<Double>();
_min = Double.MAX_VALUE;
_max = Double.MIN_VALUE;
_sum = 0;
double meanSum = 0.0;
for (T hist : _histograms) {
if (hist instanceof Histogram) {
Histogram h = (Histogram) hist;
_min = Math.min(_min, h.min());
_max = Math.max(_max, h.max());
_sum += h.sum();
meanSum += h.mean();
} else {
AggregatedHistogram<Sampling> h = (AggregatedHistogram<Sampling>) hist;
_min = Math.min(_min, h.min());
_max = Math.max(_max, h.max());
_sum += h.sum();
meanSum += h.mean();
}
double[] val = hist.getSnapshot().getValues();
for (double d : val) {
values.add(d);
}
}
if (!_histograms.isEmpty()) {
_mean = meanSum / _histograms.size();
}
if (!values.isEmpty()) {
double[] vals = new double[values.size()];
int i = 0;
for (Double d : values) {
vals[i++] = d;
}
_snapshot = new Snapshot(vals);
}
}
@Override
public double max() {
refreshIfElapsed();
return _max;
}
@Override
public double min() {
refreshIfElapsed();
return _min;
}
@Override
public double mean() {
refreshIfElapsed();
return _mean;
}
@Override
public double stdDev() {
refreshIfElapsed();
return _stdDev;
}
@Override
public double sum() {
refreshIfElapsed();
return _sum;
}
@Override
public Snapshot getSnapshot() {
refreshIfElapsed();
return _snapshot;
}
@Override
public <T2> void processWith(MetricProcessor<T2> processor, MetricName name, T2 context) throws Exception {
for (T h : _histograms) {
if (h instanceof Metric) {
((Metric) h).processWith(processor, name, context);
}
}
}
}