// 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.simulator.utils;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.twitter.heron.api.Config;
import com.twitter.heron.api.generated.TopologyAPI;
import com.twitter.heron.common.basics.TypeUtils;
import com.twitter.heron.proto.system.PhysicalPlans;
public final class PhysicalPlanUtil {
private PhysicalPlanUtil() {
}
/**
* Want get a PhysicalPlan basing the topology given.
* It would contain one fake stream mgr/container info. And all instances would be belong to
* this container.
*
* @param topology The topology protobuf given
* @return Physical Plan containing this topology
*/
public static PhysicalPlans.PhysicalPlan getPhysicalPlan(TopologyAPI.Topology topology) {
PhysicalPlans.PhysicalPlan.Builder pPlanBuilder = PhysicalPlans.PhysicalPlan.newBuilder();
// Add the topology
pPlanBuilder.setTopology(topology);
// Add fake stream mgr
PhysicalPlans.StMgr stMgr = PhysicalPlans.StMgr.newBuilder().
setId("").setHostName("").setDataPort(-1).setLocalEndpoint("").setCwd("").build();
pPlanBuilder.addStmgrs(stMgr);
// Add instances
int globalTaskIndex = 1;
for (Map.Entry<String, Integer> componentParallelism
: getComponentParallelism(topology).entrySet()) {
String componentName = componentParallelism.getKey();
int parallelism = componentParallelism.getValue();
int componentIndex = 1;
for (int i = 0; i < parallelism; i++) {
PhysicalPlans.InstanceInfo instanceInfo =
PhysicalPlans.InstanceInfo.newBuilder().
setComponentName(componentName).
setTaskId(globalTaskIndex).
setComponentIndex(componentIndex).
build();
PhysicalPlans.Instance instance =
PhysicalPlans.Instance.newBuilder().
setStmgrId("").
setInstanceId(String.format("%s_%s", componentName, componentIndex)).
setInfo(instanceInfo).build();
pPlanBuilder.addInstances(instance);
componentIndex++;
globalTaskIndex++;
}
}
return pPlanBuilder.build();
}
/**
* Get the map <componentId -> taskIds> from the Physical Plan given
*
* @param physicalPlan the given Physical Plan
* @return the map from componentId to its task ids
*/
public static Map<String, List<Integer>> getComponentToTaskIds(
PhysicalPlans.PhysicalPlan physicalPlan) {
Map<String, List<Integer>> componentToTaskIds =
new HashMap<>();
// Iterate over all instances and insert necessary info into the map
for (PhysicalPlans.Instance instance : physicalPlan.getInstancesList()) {
int taskId = instance.getInfo().getTaskId();
String componentName = instance.getInfo().getComponentName();
if (!componentToTaskIds.containsKey(componentName)) {
componentToTaskIds.put(componentName, new ArrayList<Integer>());
}
componentToTaskIds.get(componentName).add(taskId);
}
return componentToTaskIds;
}
/**
* Extract the config value "topology.message.timeout.secs" for given topology protobuf
*
* @param topology The given topology protobuf
* @return the config value of "topology.message.timeout.secs"
*/
public static Duration extractTopologyTimeout(TopologyAPI.Topology topology) {
for (TopologyAPI.Config.KeyValue keyValue : topology.getTopologyConfig().getKvsList()) {
if (keyValue.getKey().equals("topology.message.timeout.secs")) {
return TypeUtils.getDuration(keyValue.getValue(), ChronoUnit.SECONDS);
}
}
throw new IllegalArgumentException("topology.message.timeout.secs does not exist");
}
// TODO(mfu): put it into api package, since it is used by also scheduler package
public static Map<String, Integer> getComponentParallelism(TopologyAPI.Topology topology) {
Map<String, Integer> parallelismMap = new HashMap<>();
for (TopologyAPI.Spout spout : topology.getSpoutsList()) {
String componentName = spout.getComp().getName();
String parallelism = getConfigWithException(
spout.getComp().getConfig().getKvsList(), Config.TOPOLOGY_COMPONENT_PARALLELISM).trim();
parallelismMap.put(componentName, Integer.parseInt(parallelism));
}
for (TopologyAPI.Bolt bolt : topology.getBoltsList()) {
String componentName = bolt.getComp().getName();
String parallelism = getConfigWithException(
bolt.getComp().getConfig().getKvsList(), Config.TOPOLOGY_COMPONENT_PARALLELISM).trim();
parallelismMap.put(componentName, Integer.parseInt(parallelism));
}
return parallelismMap;
}
// TODO(mfu): put it into api package, since it is used by also scheduler package
public static String getConfigWithException(
List<TopologyAPI.Config.KeyValue> config, String key) {
for (TopologyAPI.Config.KeyValue kv : config) {
if (kv.getKey().equals(key)) {
return kv.getValue();
}
}
throw new RuntimeException("Missing config for required key " + key);
}
}