package com.linkedin.parseq.batching; import java.util.concurrent.TimeUnit; import java.util.function.Function; import org.HdrHistogram.Histogram; import org.HdrHistogram.Recorder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BatchAggregationTimeMetric { private static final Logger LOGGER = LoggerFactory.getLogger(BatchAggregationTimeMetric.class); private static final long LOWEST_DISCERNIBLE_VALUE = 1; private static final long HIGHEST_TRACKABLE_VALUE = TimeUnit.HOURS.toNanos(1); private static final int NUMBER_OF_FIGNIFICANT_VALUE_DIGITS = 3; private Recorder _recorder = null; private Histogram _recycle; /** * Records a batch aggregation time. * This method is thread safe. * @param batchAggregationTimeNano batch aggregation time */ public void record(long batchAggregationTimeNano) { recordSafeValue(narrow(batchAggregationTimeNano)); } private long narrow(long batchAggregationTimeNano) { if (batchAggregationTimeNano < LOWEST_DISCERNIBLE_VALUE) { LOGGER.warn("batch aggregation time lower than expected: {}, recording as: {}", batchAggregationTimeNano, LOWEST_DISCERNIBLE_VALUE); return LOWEST_DISCERNIBLE_VALUE; } if (batchAggregationTimeNano > HIGHEST_TRACKABLE_VALUE) { LOGGER.warn("batch aggregation time greater than expected: {}, recording as: {}", batchAggregationTimeNano, HIGHEST_TRACKABLE_VALUE); return HIGHEST_TRACKABLE_VALUE; } return batchAggregationTimeNano; } private void initializeRecorder() { if (_recorder == null) { _recorder = new Recorder(LOWEST_DISCERNIBLE_VALUE, HIGHEST_TRACKABLE_VALUE, NUMBER_OF_FIGNIFICANT_VALUE_DIGITS); } } private synchronized void recordSafeValue(long batchAggregationTimeNano) { initializeRecorder(); _recorder.recordValue(batchAggregationTimeNano); } /** * Allows consuming histogram and returning a result. * Histogram passed to the consumer includes stable, consistent view * of all values accumulated since last harvest. * This method is thread safe. * @param consumer consumer for a harvested histogram * @param <T> return type of a passed in function * @return a result of a passed in function */ public synchronized <T> T harvest(Function<Histogram, T> consumer) { initializeRecorder(); _recycle = _recorder.getIntervalHistogram(_recycle); return consumer.apply(_recycle); } }