Skip to content

Instantly share code, notes, and snippets.

@jkschneider
Created August 28, 2017 22:34
Show Gist options
  • Save jkschneider/56746134a4c5806f41c1f1de4f958e3d to your computer and use it in GitHub Desktop.
Save jkschneider/56746134a4c5806f41c1f1de4f958e3d to your computer and use it in GitHub Desktop.
What happens when we add new buckets to a Prometheus Histogram dynamically?
package io.micrometer.core.samples;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.atomic.DoubleAdder;
import java.util.stream.Collectors;
public class TempPrometheusVaryingHistogram {
DoubleAdder count = new DoubleAdder();
DoubleAdder sum = new DoubleAdder();
TreeMap<Double, DoubleAdder> buckets = new TreeMap<>();
DoubleAdder currentValue = new DoubleAdder();
public static void main(String[] args) {
new TempPrometheusVaryingHistogram().run();
}
public void record(double value) {
count.add(1);
sum.add(value);
buckets.tailMap(value).forEach((bucket, bucketCount) -> bucketCount.add(1));
}
public String scrape() {
return "# HELP summary \n" +
"# TYPE summary histogram\n" +
"summary_count " + count.sum() + "\n" +
"summary_sum" + sum.sum() + "\n" +
buckets.entrySet().stream()
.map(entry -> "summary_bucket{le=\"" + entry.getKey() + "\",} " + entry.getValue())
.collect(Collectors.joining("\n")) + "\n\n" +
// so we have a sense of where the current value used for recording is
"# HELP curValue \n" +
"# TYPE curValue gauge\n" +
"curValue " + currentValue.sum() + "\n";
}
// add a new bucket, initializing it's value to be equal to that of the bucket that immediately precedes it
public void addBucket(double bucket) {
DoubleAdder initialValue = new DoubleAdder();
Map.Entry<Double, DoubleAdder> floor = buckets.floorEntry(bucket);
if(floor != null)
initialValue.add(floor.getValue().sum());
buckets.put(bucket, initialValue);
}
@SuppressWarnings("Duplicates")
public void run() {
addBucket(1);
addBucket(2);
addBucket(3);
currentValue.add(2.5);
try {
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/prometheus", httpExchange -> {
record(currentValue.sum());
currentValue.add(0.1);
// as we sample higher and higher values, we keep adding buckets
if(currentValue.sum() > buckets.lastEntry().getKey()) {
addBucket(buckets.lastEntry().getKey() + 1);
}
String scrape = scrape();
httpExchange.sendResponseHeaders(200, scrape.length());
OutputStream os = httpExchange.getResponseBody();
os.write(scrape.getBytes());
os.close();
});
new Thread(server::start).run();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment