/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.ambari.server.controller.metrics;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import org.apache.ambari.server.controller.spi.TemporalInfo;
import org.apache.hadoop.metrics2.sink.timeline.Precision;
import org.apache.hadoop.metrics2.sink.timeline.TimelineMetric;
public class MetricsPaddingMethod {
private final PADDING_STRATEGY strategy;
public static final String ZERO_PADDING_PARAM = "params/padding";
private static final long MINIMUM_STEP_INTERVAL = 999l; // ~ 1 second
public enum PADDING_STRATEGY {
ZEROS,
NULLS,
NONE
}
public MetricsPaddingMethod(PADDING_STRATEGY strategy) {
this.strategy = strategy;
}
/**
* Adds zero/null values towards the end of metrics sequence as well as the
* beginning to support backward compatibility with Ganglia.
* We use step if only a single datapoint is found.
* It is assumed that @TimelineMetric.metricsValues are sorted in ascending
* order and thee is no padding between the interval.
*
* @param metric AMS @TimelineMetric
* @param temporalInfo @TemporalInfo Requested interval
*/
public void applyPaddingStrategy(TimelineMetric metric, TemporalInfo temporalInfo) {
if (strategy.equals(PADDING_STRATEGY.NONE) || temporalInfo == null) {
return;
}
TreeMap<Long, Double> values = metric.getMetricValues();
if (values==null || values.isEmpty()) {
return;
}
long intervalStartTime = longToMillis(temporalInfo.getStartTime());
long intervalEndTime = longToMillis(temporalInfo.getEndTime());
long dataStartTime = longToMillis(values.firstKey());
long dataEndTime = longToMillis(values.lastKey());
long dataInterval = getTimelineMetricInterval(values, intervalStartTime, intervalEndTime);
if (dataInterval == -1 || dataInterval < MINIMUM_STEP_INTERVAL) {
dataInterval = temporalInfo.getStep() != null ? temporalInfo.getStep() : -1;
}
// Unable to determine what interval to use for padding
if (dataInterval == -1) {
return;
}
Double paddingValue = 0.0d;
if (strategy.equals(PADDING_STRATEGY.NULLS)) {
paddingValue = null;
}
// Pad before data interval
for (long counter = intervalStartTime; counter < dataStartTime; counter += dataInterval) {
// Until counter approaches or goes past dataStartTime : pad
values.put(counter, paddingValue);
}
// Pad after data interval
for (long counter = dataEndTime + dataInterval; counter <= intervalEndTime; counter += dataInterval) {
values.put(counter, paddingValue);
}
// Put back new + old values
metric.setMetricValues(values);
}
private long longToMillis(long time) {
if (time < 9999999999l) {
return time * 1000;
}
return time;
}
private long getTimelineMetricInterval(TreeMap<Long, Double> values, long startTime, long endTime) {
Precision precision = Precision.getPrecision(startTime, endTime);
long interval;
if (precision.equals(Precision.DAYS)) {
interval = TimeUnit.DAYS.toMillis(1);
} else if (precision.equals(Precision.HOURS)) {
interval = TimeUnit.HOURS.toMillis(1);
} else if (precision.equals(Precision.MINUTES)) {
interval = TimeUnit.MINUTES.toMillis(1);
} else {
//Precision = SECONDS.
//More than 1 point.
if (values != null && values.size() > 1) {
Iterator<Long> tsValuesIterator = values.descendingKeySet().iterator();
long lastValue = tsValuesIterator.next();
long secondToLastValue = tsValuesIterator.next();
interval = Math.abs(lastValue - secondToLastValue);
} else {
// Only 1 point
interval = -1;
}
}
return interval;
}
}