package io.prometheus.client; import io.prometheus.client.CKMSQuantiles.Quantile; import java.util.concurrent.TimeUnit; /** * Wrapper around CKMSQuantiles. * * Maintains a ring buffer of CKMSQuantiles to provide quantiles over a sliding windows of time. */ class TimeWindowQuantiles { private final Quantile[] quantiles; private final CKMSQuantiles[] ringBuffer; private int currentBucket; private long lastRotateTimestampMillis; private final long durationBetweenRotatesMillis; public TimeWindowQuantiles(Quantile[] quantiles, long maxAgeSeconds, int ageBuckets) { this.quantiles = quantiles; this.ringBuffer = new CKMSQuantiles[ageBuckets]; for (int i = 0; i < ageBuckets; i++) { this.ringBuffer[i] = new CKMSQuantiles(quantiles); } this.currentBucket = 0; this.lastRotateTimestampMillis = System.currentTimeMillis(); this.durationBetweenRotatesMillis = TimeUnit.SECONDS.toMillis(maxAgeSeconds) / ageBuckets; } public double get(double q) { CKMSQuantiles currentBucket = rotate(); return currentBucket.get(q); } public void insert(double value) { rotate(); for (CKMSQuantiles ckmsQuantiles : ringBuffer) { ckmsQuantiles.insert(value); } } private synchronized CKMSQuantiles rotate() { long timeSinceLastRotateMillis = System.currentTimeMillis() - lastRotateTimestampMillis; while (timeSinceLastRotateMillis > durationBetweenRotatesMillis) { ringBuffer[currentBucket] = new CKMSQuantiles(quantiles); if (++currentBucket >= ringBuffer.length) { currentBucket = 0; } timeSinceLastRotateMillis -= durationBetweenRotatesMillis; lastRotateTimestampMillis += durationBetweenRotatesMillis; } return ringBuffer[currentBucket]; } }