package io.dropwizard.metrics; import org.HdrHistogram.Histogram; import org.HdrHistogram.Recorder; import javax.annotation.Nonnull; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; @ThreadSafe public final class HdrHistogramReservoir implements Reservoir { private final Recorder recorder; @GuardedBy("this") private final Histogram runningTotals; @GuardedBy("this") @Nonnull private Histogram intervalHistogram; /** * Create a reservoir with a default recorder. This recorder should be suitable for most usage. */ public HdrHistogramReservoir() { this(new Recorder(2)); } /** * Create a reservoir with a user-specified recorder. * * @param recorder Recorder to use */ public HdrHistogramReservoir(Recorder recorder) { this.recorder = recorder; /* * Start by flipping the recorder's interval histogram. * - it starts our counting at zero. Arguably this might be a bad thing if you wanted to feed in * a recorder that already had some measurements? But that seems crazy. * - intervalHistogram can be nonnull. * - it lets us figure out the number of significant digits to use in runningTotals. */ intervalHistogram = recorder.getIntervalHistogram(); runningTotals = new Histogram(intervalHistogram.getNumberOfSignificantValueDigits()); } @Override public int size() { // This appears to be infrequently called, so not keeping a separate counter just for this. return getSnapshot().size(); } @Override public void update(long value) { recorder.recordValue(value); } /** * @return the data accumulated since the reservoir was created */ @Override public Snapshot getSnapshot() { return new HistogramSnapshot(updateRunningTotals()); } /** * @return a copy of the accumulated state since the reservoir was created */ @Nonnull private synchronized Histogram updateRunningTotals() { intervalHistogram = recorder.getIntervalHistogram(intervalHistogram); runningTotals.add(intervalHistogram); return runningTotals.copy(); } }