/*
* Copyright (C) 2012 Facebook, Inc.
*
* 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.facebook.stats;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static com.facebook.stats.MultiWindowDistribution.Quantile.*;
import static com.google.common.collect.Lists.transform;
public class MultiWindowDistribution implements WritableMultiWindowStat {
private final QuantileDigest oneMinute;
private final QuantileDigest tenMinutes;
private final QuantileDigest oneHour;
private final QuantileDigest allTime;
@VisibleForTesting
MultiWindowDistribution(
QuantileDigest oneMinute,
QuantileDigest tenMinutes,
QuantileDigest oneHour,
QuantileDigest allTime
) {
this.oneMinute = oneMinute;
this.tenMinutes = tenMinutes;
this.oneHour = oneHour;
this.allTime = allTime;
}
public MultiWindowDistribution() {
this(
new QuantileDigest(0.01, ExponentialDecay.computeAlpha(0.1, 60)),
new QuantileDigest(0.01, ExponentialDecay.computeAlpha(0.1, 600)),
new QuantileDigest(0.01, ExponentialDecay.computeAlpha(0.1, 3600)),
new QuantileDigest(0.01)
);
}
@Override
public void add(long value) {
oneMinute.add(value);
tenMinutes.add(value);
oneHour.add(value);
allTime.add(value);
}
public QuantileDigest getOneMinute() {
return oneMinute;
}
public QuantileDigest getTenMinutes() {
return tenMinutes;
}
public QuantileDigest getOneHour() {
return oneHour;
}
public QuantileDigest getAllTime() {
return allTime;
}
public Map<Quantile, Long> getOneMinuteQuantiles() {
return getQuantiles(oneMinute);
}
public Map<Quantile, Long> getTenMinuteQuantiles() {
return getQuantiles(tenMinutes);
}
public Map<Quantile, Long> getOneHourQuantiles() {
return getQuantiles(oneHour);
}
public Map<Quantile, Long> getAllTimeQuantiles() {
return getQuantiles(allTime);
}
private Map<Quantile, Long> getQuantiles(QuantileDigest digest) {
List<Quantile> keys = ImmutableList.of(P50, P75, P95, P99);
List<Long> values = digest.getQuantiles(transform(keys, getQuantileFunction()));
Iterator<Quantile> keyIterator = keys.iterator();
Iterator<Long> valueIterator = values.iterator();
EnumMap<Quantile, Long> result = new EnumMap<Quantile, Long>(Quantile.class);
while (keyIterator.hasNext() && valueIterator.hasNext()) {
result.put(keyIterator.next(), valueIterator.next());
}
return result;
}
public enum Quantile {
P50("p50", 0.5),
P75("p75", 0.75),
P95("p95", 0.95),
P99("p99", 0.99);
private final String key;
private final double quantile;
private Quantile(String key, double quantile) {
this.key = key;
this.quantile = quantile;
}
public String getKey() {
return key;
}
public double getQuantile() {
return quantile;
}
public static Function<Quantile, Double> getQuantileFunction() {
return new Function<Quantile, Double>() {
public Double apply(Quantile input) {
return input.getQuantile();
}
};
}
}
}