/** * Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com) * * 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.linkedin.pinot.common.metrics; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import com.yammer.metrics.core.Gauge; import com.yammer.metrics.core.MetricName; import com.yammer.metrics.core.MetricsRegistry; /** * Validation metrics utility class, which contains the glue code to publish metrics. * */ public class ValidationMetrics { private final MetricsRegistry _metricsRegistry; private final Map<String, Long> gaugeValues = new HashMap<String, Long>(); /** * A simple gauge that returns whatever last value was stored in the gaugeValues hash map. */ private class StoredValueGauge extends Gauge<Long> { private final String key; public StoredValueGauge(String key) { this.key = key; } @Override public Long value() { return gaugeValues.get(key); } } /** * A simple gauge that returns the difference between the current system time in millis and the value stored in the * gaugeValues hash map. */ private class CurrentTimeMillisDeltaGauge extends Gauge<Long> { private final String key; public CurrentTimeMillisDeltaGauge(String key) { this.key = key; } @Override public Long value() { Long gaugeValue = gaugeValues.get(key); if (gaugeValue != null && gaugeValue != Long.MIN_VALUE) return System.currentTimeMillis() - gaugeValue; else return Long.MIN_VALUE; } } /** * A simple gauge that returns the difference in hours between the current system time and the value stored in the * gaugeValues hash map. */ private class CurrentTimeMillisDeltaGaugeHours extends Gauge<Double> { private final String key; private final double MILLIS_PER_HOUR = TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS); public CurrentTimeMillisDeltaGaugeHours(String key) { this.key = key; } @Override public Double value() { Long gaugeValue = gaugeValues.get(key); if (gaugeValue != null && gaugeValue != Long.MIN_VALUE) return (System.currentTimeMillis() - gaugeValue) / MILLIS_PER_HOUR; else return Double.MIN_VALUE; } } private interface GaugeFactory<T> { Gauge<T> buildGauge(final String key); } private class StoredValueGaugeFactory implements GaugeFactory<Long> { @Override public Gauge<Long> buildGauge(final String key) { return new StoredValueGauge(key); } } private class CurrentTimeMillisDeltaGaugeFactory implements GaugeFactory<Long> { @Override public Gauge<Long> buildGauge(final String key) { return new CurrentTimeMillisDeltaGauge(key); } } private class CurrentTimeMillisDeltaGaugeHoursFactory implements GaugeFactory<Double> { @Override public Gauge<Double> buildGauge(final String key) { return new CurrentTimeMillisDeltaGaugeHours(key); } } private final StoredValueGaugeFactory _storedValueGaugeFactory = new StoredValueGaugeFactory(); private final CurrentTimeMillisDeltaGaugeFactory _currentTimeMillisDeltaGaugeFactory = new CurrentTimeMillisDeltaGaugeFactory(); private final CurrentTimeMillisDeltaGaugeHoursFactory _currentTimeMillisDeltaGaugeHoursFactory = new CurrentTimeMillisDeltaGaugeHoursFactory(); /** * Builds the validation metrics. * * @param metricsRegistry The metrics registry used to store all the gauges. */ public ValidationMetrics(MetricsRegistry metricsRegistry) { _metricsRegistry = metricsRegistry; } /** * Updates the gauge for the number of missing segments. * * @param resource The resource for which the gauge is updated * @param missingSegmentCount The number of missing segments */ public void updateMissingSegmentsGauge(final String resource, final int missingSegmentCount) { final String fullGaugeName = makeGaugeName(resource, "missingSegmentCount"); makeGauge(fullGaugeName, makeMetricName(fullGaugeName), _storedValueGaugeFactory, missingSegmentCount); } /** * Updates the gauge for the offline segment delay. * * @param resource The resource for which the gauge is updated * @param lastOfflineSegmentTime The last offline segment end time, in milliseconds since the epoch, or Long.MIN_VALUE * if there is no such time. */ public void updateOfflineSegmentDelayGauge(final String resource, final long lastOfflineSegmentTime) { final String fullGaugeName = makeGaugeName(resource, "offlineSegmentDelayMillis"); makeGauge(fullGaugeName, makeMetricName(fullGaugeName), _currentTimeMillisDeltaGaugeFactory, lastOfflineSegmentTime); final String fullGaugeNameHours = makeGaugeName(resource, "offlineSegmentDelayHours"); makeGauge(fullGaugeNameHours, makeMetricName(fullGaugeNameHours), _currentTimeMillisDeltaGaugeHoursFactory, lastOfflineSegmentTime); } /** * Updates the gauge for the last push time. * * @param resource The resource for which the gauge is updated * @param lastPushTimeMillis The last push time, in milliseconds since the epoch, or Long.MIN_VALUE if there is no * such time. */ public void updateLastPushTimeGauge(final String resource, final long lastPushTimeMillis) { final String fullGaugeName = makeGaugeName(resource, "lastPushTimeDelayMillis"); makeGauge(fullGaugeName, makeMetricName(fullGaugeName), _currentTimeMillisDeltaGaugeFactory, lastPushTimeMillis); final String fullGaugeNameHours = makeGaugeName(resource, "lastPushTimeDelayHours"); makeGauge(fullGaugeNameHours, makeMetricName(fullGaugeNameHours), _currentTimeMillisDeltaGaugeHoursFactory, lastPushTimeMillis); } /** * Updates the gauge for the Total Document Count * * @param resource The resource for which the gauge is updated * @param documentCount Total document count for the give resource name / tablename */ public void updateTotalDocumentsGauge(final String resource, final long documentCount) { final String fullGaugeName = makeGaugeName(resource, "TotalDocumentCount"); makeGauge(fullGaugeName, makeMetricName(fullGaugeName), _storedValueGaugeFactory, documentCount); } /** * * @param resource The resource for which the guage is updated * @param partitionCount Number of kafka partitions that do not have any segment in CONSUMING state. */ public void updateNumNonConsumingPartitionsMetric(final String resource, final int partitionCount) { final String fullGaugeName = makeGaugeName(resource, "NonConsumingPartitionCount"); makeGauge(fullGaugeName, makeMetricName(fullGaugeName), _storedValueGaugeFactory, partitionCount); } /** * Updates the gauge for the Total segment count * * @param resource The resource for which the gauge is updated * @param segmentCount Total segment count for the give resource name / tablename */ public void updateSegmentCountGauge(final String resource, final long segmentCount) { final String fullGaugeName = makeGaugeName(resource, "SegmentCount"); makeGauge(fullGaugeName, makeMetricName(fullGaugeName), _storedValueGaugeFactory, segmentCount); } private String makeGaugeName(final String resource, final String gaugeName) { return "pinot.controller." + resource + "." + gaugeName; } private MetricName makeMetricName(final String gaugeName) { return new MetricName(ValidationMetrics.class, gaugeName); } private void makeGauge(final String gaugeName, final MetricName metricName, final GaugeFactory<?> gaugeFactory, final long value) { if (!gaugeValues.containsKey(gaugeName)) { gaugeValues.put(gaugeName, value); MetricsHelper.newGauge(_metricsRegistry, metricName, gaugeFactory.buildGauge(gaugeName)); } else { gaugeValues.put(gaugeName, value); } } }