// Copyright 2016 Twitter. All rights reserved. // // 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.twitter.heron.metricscachemgr.metricscache; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.twitter.heron.metricscachemgr.metricscache.query.ExceptionDatum; import com.twitter.heron.metricscachemgr.metricscache.query.ExceptionRequest; import com.twitter.heron.metricscachemgr.metricscache.query.ExceptionResponse; import com.twitter.heron.metricscachemgr.metricscache.query.MetricDatum; import com.twitter.heron.metricscachemgr.metricscache.query.MetricGranularity; import com.twitter.heron.metricscachemgr.metricscache.query.MetricRequest; import com.twitter.heron.metricscachemgr.metricscache.query.MetricResponse; import com.twitter.heron.metricscachemgr.metricscache.query.MetricTimeRangeValue; import com.twitter.heron.proto.system.Common; import com.twitter.heron.proto.tmaster.TopologyMaster; import static com.twitter.heron.metricscachemgr.metricscache.query.MetricGranularity.AGGREGATE_ALL_METRICS; import static com.twitter.heron.metricscachemgr.metricscache.query.MetricGranularity.AGGREGATE_BY_BUCKET; /** * converter from/to protobuf */ public final class MetricsCacheQueryUtils { private MetricsCacheQueryUtils() { } /** * compatible with com.twitter.heron.proto.tmaster.TopologyMaster.MetricRequest * @param request protobuf defined message * @return metricscache defined data structure */ public static MetricRequest fromProtobuf(TopologyMaster.MetricRequest request) { String componentName = request.getComponentName(); Map<String, Set<String>> componentNameInstanceId = new HashMap<>(); if (request.getInstanceIdCount() == 0) { // empty list means all instances // 'null' means all instances componentNameInstanceId.put(componentName, null); } else { Set<String> instances = new HashSet<>(); // only one component componentNameInstanceId.put(componentName, instances); // if there are instances specified instances.addAll(request.getInstanceIdList()); } Set<String> metricNames = new HashSet<>(); if (request.getMetricCount() > 0) { metricNames.addAll(request.getMetricList()); } // empty list means no metrics // default: the whole time horizon long startTime = 0; long endTime = Long.MAX_VALUE; if (request.hasInterval()) { // endTime = now endTime = System.currentTimeMillis(); long interval = request.getInterval(); // in seconds if (interval <= 0) { // means all startTime = 0; } else { // means [-interval, now] startTime = endTime - interval * 1000; } } else { startTime = request.getExplicitInterval().getStart() * 1000; endTime = request.getExplicitInterval().getEnd() * 1000; } // default: aggregate all metrics MetricGranularity aggregationGranularity = AGGREGATE_ALL_METRICS; if (request.hasMinutely() && request.getMinutely()) { aggregationGranularity = AGGREGATE_BY_BUCKET; } return new MetricRequest(componentNameInstanceId, metricNames, startTime, endTime, aggregationGranularity); } /** * compatible with com.twitter.heron.proto.tmaster.TopologyMaster.MetricResponse * @param response metricscache defined data structure * @param request metricscache defined data structure * @return protobuf defined message */ public static TopologyMaster.MetricResponse toProtobuf(MetricResponse response, MetricRequest request) { TopologyMaster.MetricResponse.Builder builder = TopologyMaster.MetricResponse.newBuilder(); builder.setInterval((request.getEndTime() - request.getStartTime()) / 1000); // in seconds // default OK if we have response to build already builder.setStatus(Common.Status.newBuilder().setStatus(Common.StatusCode.OK)); // instanceId -> [metricName -> metricValue] // componentName is ignored, since there is only one component in the query Map<String, Map<String, List<MetricTimeRangeValue>>> aggregation = new HashMap<>(); for (MetricDatum datum : response.getMetricList()) { String instanceId = datum.getInstanceId(); String metricName = datum.getMetricName(); List<MetricTimeRangeValue> metricValue = datum.getMetricValue(); // prepare if (!aggregation.containsKey(instanceId)) { aggregation.put(instanceId, new HashMap<String, List<MetricTimeRangeValue>>()); } if (!aggregation.get(instanceId).containsKey(metricName)) { aggregation.get(instanceId).put(metricName, new ArrayList<MetricTimeRangeValue>()); } // aggregate aggregation.get(instanceId).get(metricName).addAll(metricValue); } // add TaskMetric for (String instanceId : aggregation.keySet()) { TopologyMaster.MetricResponse.TaskMetric.Builder taskMetricBuilder = TopologyMaster.MetricResponse.TaskMetric.newBuilder(); taskMetricBuilder.setInstanceId(instanceId); // add IndividualMetric for (String metricName : aggregation.get(instanceId).keySet()) { TopologyMaster.MetricResponse.IndividualMetric.Builder individualMetricBuilder = TopologyMaster.MetricResponse.IndividualMetric.newBuilder(); individualMetricBuilder.setName(metricName); // add value|IntervalValue List<MetricTimeRangeValue> list = aggregation.get(instanceId).get(metricName); if (list.size() == 1) { individualMetricBuilder.setValue(list.get(0).getValue()); } else { for (MetricTimeRangeValue v : list) { TopologyMaster.MetricResponse.IndividualMetric.IntervalValue.Builder intervalValueBuilder = TopologyMaster.MetricResponse.IndividualMetric.IntervalValue.newBuilder(); intervalValueBuilder.setValue(v.getValue()); intervalValueBuilder.setInterval(TopologyMaster.MetricInterval.newBuilder() .setStart(v.getStartTime()).setEnd(v.getEndTime())); individualMetricBuilder.addIntervalValues(intervalValueBuilder); }// end IntervalValue } taskMetricBuilder.addMetric(individualMetricBuilder); }// end IndividualMetric builder.addMetric(taskMetricBuilder); }// end TaskMetric return builder.build(); } // compatible with com.twitter.heron.proto.tmaster.TopologyMaster.ExceptionLogRequest public static ExceptionRequest fromProtobuf(TopologyMaster.ExceptionLogRequest request) { String componentName = request.getComponentName(); Map<String, Set<String>> componentNameInstanceId = new HashMap<>(); Set<String> instances = null; if (request.getInstancesCount() > 0) { instances = new HashSet<>(); instances.addAll(request.getInstancesList()); } // empty list means all instances; 'null' means all instances // only one component componentNameInstanceId.put(componentName, instances); return new ExceptionRequest(componentNameInstanceId); } // compatible with com.twitter.heron.proto.tmaster.TopologyMaster.ExceptionLogResponse public static TopologyMaster.ExceptionLogResponse toProtobuf(ExceptionResponse response) { TopologyMaster.ExceptionLogResponse.Builder builder = TopologyMaster.ExceptionLogResponse.newBuilder(); // default OK if we have response to build already builder.setStatus(Common.Status.newBuilder().setStatus(Common.StatusCode.OK)); for (ExceptionDatum e : response.getExceptionDatapointList()) { TopologyMaster.TmasterExceptionLog.Builder exceptionBuilder = TopologyMaster.TmasterExceptionLog.newBuilder(); // ExceptionDatapoint exceptionBuilder.setComponentName(e.getComponentName()); exceptionBuilder.setHostname(e.getHostname()); exceptionBuilder.setInstanceId(e.getInstanceId()); // ExceptionData exceptionBuilder.setStacktrace(e.getStackTrace()); exceptionBuilder.setLasttime(e.getLastTime()); exceptionBuilder.setFirsttime(e.getFirstTime()); exceptionBuilder.setCount(e.getCount()); exceptionBuilder.setLogging(e.getLogging()); builder.addExceptions(exceptionBuilder); } return builder.build(); } }