Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* 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 com.datatorrent.stram.plan.physical;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.lang.StringUtils;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.datatorrent.api.AffinityRule;
import com.datatorrent.api.AffinityRule.Type;
import com.datatorrent.api.AffinityRulesSet;
import com.datatorrent.api.Context;
import com.datatorrent.api.Context.DAGContext;
import com.datatorrent.api.Context.OperatorContext;
import com.datatorrent.api.Context.PortContext;
import com.datatorrent.api.DAG.Locality;
import com.datatorrent.api.DefaultPartition;
import com.datatorrent.api.Operator;
import com.datatorrent.api.Operator.InputPort;
import com.datatorrent.api.Partitioner;
import com.datatorrent.api.Partitioner.Partition;
import com.datatorrent.api.Partitioner.PartitionKeys;
import com.datatorrent.api.StatsListener;
import com.datatorrent.api.StatsListener.OperatorRequest;
import com.datatorrent.api.StorageAgent;
import com.datatorrent.api.StreamCodec;
import com.datatorrent.api.annotation.Stateless;
import com.datatorrent.common.util.AsyncFSStorageAgent;
import com.datatorrent.stram.Journal.Recoverable;
import com.datatorrent.stram.api.Checkpoint;
import com.datatorrent.stram.api.StramEvent;
import com.datatorrent.stram.api.StreamingContainerUmbilicalProtocol.StramToNodeRequest;
import com.datatorrent.stram.plan.logical.LogicalPlan;
import com.datatorrent.stram.plan.logical.LogicalPlan.InputPortMeta;
import com.datatorrent.stram.plan.logical.LogicalPlan.OperatorMeta;
import com.datatorrent.stram.plan.logical.LogicalPlan.OperatorPair;
import com.datatorrent.stram.plan.logical.LogicalPlan.OutputPortMeta;
import com.datatorrent.stram.plan.logical.LogicalPlan.StreamMeta;
import com.datatorrent.stram.plan.logical.StreamCodecWrapperForPersistance;
import com.datatorrent.stram.plan.physical.PTOperator.HostOperatorSet;
import com.datatorrent.stram.plan.physical.PTOperator.PTInput;
import com.datatorrent.stram.plan.physical.PTOperator.PTOutput;
/**
* Translates the logical DAG into physical model. Is the initial query planner
* and performs dynamic changes.
*
* Attributes in the logical DAG affect how the physical plan is derived.
* Examples include partitioning schemes, resource allocation, recovery
* semantics etc.
*
* The current implementation does not dynamically change or optimize allocation
* of containers. The maximum number of containers and container size can be
* specified per application, but all containers are requested at the same size
* and execution will block until all containers were allocated by the resource
* manager. Future enhancements will allow to define resource constraints at the
* operator level and elasticity in resource allocation.
*
* @since 0.3.2
*/
public class PhysicalPlan implements Serializable
{
private static final long serialVersionUID = 201312112033L;
private static final Logger LOG = LoggerFactory.getLogger(PhysicalPlan.class);
public static class LoadIndicator
{
public final int indicator;
public final String note;
LoadIndicator(int indicator, String note)
{
this.indicator = indicator;
this.note = note;
}
}
private final AtomicInteger idSequence = new AtomicInteger();
final AtomicInteger containerSeq = new AtomicInteger();
private LinkedHashMap logicalToPTOperator = new LinkedHashMap<>();
private final List containers = new CopyOnWriteArrayList<>();
private final LogicalPlan dag;
private final transient PlanContext ctx;
private int maxContainers = 1;
private int availableMemoryMB = Integer.MAX_VALUE;
private final LocalityPrefs localityPrefs = new LocalityPrefs();
private final LocalityPrefs inlinePrefs = new LocalityPrefs();
final Set deployOpers = Sets.newHashSet();
final Map newOpers = Maps.newHashMap();
final Set undeployOpers = Sets.newHashSet();
final ConcurrentMap allOperators = Maps.newConcurrentMap();
private final ConcurrentMap pendingRepartition = Maps.newConcurrentMap();
private final AtomicInteger strCodecIdSequence = new AtomicInteger();
private final Map, Integer> streamCodecIdentifiers = Maps.newHashMap();
private PTContainer getContainer(int index)
{
if (index >= containers.size()) {
if (index >= maxContainers) {
index = maxContainers - 1;
}
for (int i = containers.size(); i < index + 1; i++) {
containers.add(i, new PTContainer(this));
}
}
return containers.get(index);
}
/**
* Interface to execution context that can be mocked for plan testing.
*/
public interface PlanContext
{
/**
* Record an event in the event log
*
* @param ev The event
*
*/
void recordEventAsync(StramEvent ev);
/**
* Request deployment change as sequence of undeploy, container start and deploy groups with dependency.
* Called on initial plan and on dynamic changes during execution.
* @param releaseContainers
* @param undeploy
* @param startContainers
* @param deploy
*/
void deploy(Set releaseContainers, Collection undeploy, Set startContainers, Collection deploy);
/**
* Trigger event to perform plan modification.
* @param r
*/
void dispatch(Runnable r);
/**
* Write the recoverable operation to the log.
* @param operation
*/
void writeJournal(Recoverable operation);
void addOperatorRequest(PTOperator oper, StramToNodeRequest request);
}
private static class StatsListenerProxy implements StatsListener, Serializable
{
private static final long serialVersionUID = 201312112033L;
private final OperatorMeta om;
private StatsListenerProxy(OperatorMeta om)
{
this.om = om;
}
@Override
public Response processStats(BatchedOperatorStats stats)
{
return ((StatsListener)om.getOperator()).processStats(stats);
}
}
/**
* The logical operator with physical plan info tagged on.
*/
public static class PMapping implements java.io.Serializable
{
private static final long serialVersionUID = 201312112033L;
private final OperatorMeta logicalOperator;
private List partitions = new LinkedList<>();
private final Map outputStreams = Maps.newHashMap();
private List statsHandlers;
/**
* Operators that form a parallel partition
*/
private Set parallelPartitions = Sets.newHashSet();
private PMapping(OperatorMeta om)
{
this.logicalOperator = om;
}
private void addPartition(PTOperator p)
{
partitions.add(p);
p.statsListeners = this.statsHandlers;
}
/**
* Return all partitions and unifiers, except MxN unifiers
* @return
*/
private Collection getAllOperators()
{
Collection c = new ArrayList<>(partitions.size() + 1);
c.addAll(partitions);
for (StreamMapping ug : outputStreams.values()) {
ug.addTo(c);
}
return c;
}
@Override
public String toString()
{
return logicalOperator.toString();
}
}
private class LocalityPref implements java.io.Serializable
{
private static final long serialVersionUID = 201312112033L;
String host;
Set operators = Sets.newHashSet();
}
/**
* Group logical operators by locality constraint. Used to derive locality
* groupings for physical operators, which are used when assigning containers
* and requesting resources from the scheduler.
*/
private class LocalityPrefs implements java.io.Serializable
{
private static final long serialVersionUID = 201312112033L;
private final Map prefs = Maps.newHashMap();
private final AtomicInteger groupSeq = new AtomicInteger();
void add(PMapping m, String group)
{
if (group != null) {
LocalityPref pref = null;
for (LocalityPref lp : prefs.values()) {
if (group.equals(lp.host)) {
lp.operators.add(m);
pref = lp;
break;
}
}
if (pref == null) {
pref = new LocalityPref();
pref.host = group;
pref.operators.add(m);
this.prefs.put(m, pref);
}
}
}
// if netbeans is not smart, don't produce warnings in other IDE
//@SuppressWarnings("null") /* for lp2.operators.add(m1); line below - netbeans is not very smart; you don't be an idiot! */
void setLocal(PMapping m1, PMapping m2)
{
LocalityPref lp1 = prefs.get(m1);
LocalityPref lp2 = prefs.get(m2);
if (lp1 == null && lp2 == null) {
lp1 = lp2 = new LocalityPref();
lp1.host = "host" + groupSeq.incrementAndGet();
lp1.operators.add(m1);
lp1.operators.add(m2);
} else if (lp1 != null && lp2 != null) {
// check if we can combine
if (StringUtils.equals(lp1.host, lp2.host)) {
lp1.operators.addAll(lp2.operators);
lp2.operators.addAll(lp1.operators);
} else {
LOG.warn("Node locality conflict {} {}", m1, m2);
}
} else {
if (lp1 == null) {
lp2.operators.add(m1);
lp1 = lp2;
} else {
lp1.operators.add(m2);
lp2 = lp1;
}
}
prefs.put(m1, lp1);
prefs.put(m2, lp2);
}
}
/**
*
* @param dag
* @param ctx
*/
public PhysicalPlan(LogicalPlan dag, PlanContext ctx)
{
this.dag = dag;
this.ctx = ctx;
this.maxContainers = Math.max(dag.getMaxContainerCount(), 1);
LOG.debug("Max containers: {}", this.maxContainers);
Stack pendingNodes = new Stack<>();
// Add logging operators for streams if not added already
updatePersistOperatorStreamCodec(dag);
for (OperatorMeta n : dag.getAllOperators()) {
pendingNodes.push(n);
}
while (!pendingNodes.isEmpty()) {
OperatorMeta n = pendingNodes.pop();
if (this.logicalToPTOperator.containsKey(n)) {
// already processed as upstream dependency
continue;
}
boolean upstreamDeployed = true;
for (Map.Entry entry : n.getInputStreams().entrySet()) {
StreamMeta s = entry.getValue();
boolean delay = entry.getKey().getValue(LogicalPlan.IS_CONNECTED_TO_DELAY_OPERATOR);
// skip delay sources since it's going to be handled as downstream
if (!delay && s.getSource() != null && !this.logicalToPTOperator.containsKey(s.getSource().getOperatorMeta())) {
pendingNodes.push(n);
pendingNodes.push(s.getSource().getOperatorMeta());
upstreamDeployed = false;
break;
}
}
if (upstreamDeployed) {
addLogicalOperator(n);
}
}
// Add inlinePrefs and localityPrefs for affinity rules
AffinityRulesSet affinityRuleSet = dag.getAttributes().get(DAGContext.AFFINITY_RULES_SET);
if (affinityRuleSet != null && affinityRuleSet.getAffinityRules() != null) {
for (AffinityRule rule : affinityRuleSet.getAffinityRules()) {
if (rule.getOperatorsList() != null) {
for (int i = 0; i < rule.getOperatorsList().size() - 1; i++) {
for (int j = i + 1; j < rule.getOperatorsList().size(); j++) {
OperatorPair operators = new OperatorPair(rule.getOperatorsList().get(i), rule.getOperatorsList().get(j));
PMapping firstPMapping = logicalToPTOperator.get(dag.getOperatorMeta(operators.first));
OperatorMeta opMeta = dag.getOperatorMeta(operators.second);
PMapping secondMapping = logicalToPTOperator.get(opMeta);
if (rule.getType() == Type.AFFINITY) {
// ONLY node and container mappings are supported right now
if (Locality.CONTAINER_LOCAL == rule.getLocality()) {
inlinePrefs.setLocal(firstPMapping, secondMapping);
} else if (Locality.NODE_LOCAL == rule.getLocality()) {
localityPrefs.setLocal(firstPMapping, secondMapping);
}
for (PTOperator ptOperator : firstPMapping.partitions) {
setLocalityGrouping(firstPMapping, ptOperator, inlinePrefs, Locality.CONTAINER_LOCAL, null);
setLocalityGrouping(firstPMapping, ptOperator, localityPrefs, Locality.NODE_LOCAL, null);
}
}
}
}
}
}
}
if (LOG.isDebugEnabled()) {
// Log group set for all PT Operators
for (OperatorMeta operator : dag.getAllOperators()) {
PMapping mapping = logicalToPTOperator.get(operator);
if (mapping != null) {
for (PTOperator ptOperaror : mapping.partitions) {
List operators = new ArrayList<>();
for (PTOperator op : ptOperaror.getGrouping(Locality.CONTAINER_LOCAL).getOperatorSet()) {
operators.add(op.getLogicalId());
}
LOG.debug("Operator {} Partition {} CONTAINER LOCAL Operator set = {}", operator.getName(), ptOperaror.id, StringUtils.join(operators, ","));
operators.clear();
for (PTOperator op : ptOperaror.getGrouping(Locality.NODE_LOCAL).getOperatorSet()) {
operators.add(op.getLogicalId());
}
LOG.debug("Operator {} Partition {} NODE LOCAL Operator set = {}", operator.getName(), ptOperaror.id, StringUtils.join(operators, ","));
}
}
}
}
updatePartitionsInfoForPersistOperator(dag);
Map operatorContainerMap = new HashMap<>();
// assign operators to containers
int groupCount = 0;
Set deployOperators = Sets.newHashSet();
for (Map.Entry e : logicalToPTOperator.entrySet()) {
for (PTOperator oper : e.getValue().getAllOperators()) {
if (oper.container == null) {
PTContainer container = getContainer((groupCount++) % maxContainers);
if (!container.operators.isEmpty()) {
LOG.warn("Operator {} shares container without locality contraint due to insufficient resources.", oper);
}
Set inlineSet = oper.getGrouping(Locality.CONTAINER_LOCAL).getOperatorSet();
if (!inlineSet.isEmpty()) {
// process inline operators
for (PTOperator inlineOper : inlineSet) {
setContainer(inlineOper, container);
operatorContainerMap.put(inlineOper, container);
}
} else {
setContainer(oper, container);
}
operatorContainerMap.put(oper, container);
deployOperators.addAll(container.operators);
}
}
}
for (PTContainer container : containers) {
updateContainerMemoryWithBufferServer(container);
container.setRequiredVCores(getVCores(container.getOperators()));
}
// Add anti-affinity restrictions in Containers
if (affinityRuleSet != null && affinityRuleSet.getAffinityRules() != null) {
setAntiAffinityForContainers(dag, affinityRuleSet.getAffinityRules(), operatorContainerMap);
}
// Log container anti-affinity
if (LOG.isDebugEnabled()) {
for (PTContainer container : containers) {
List antiOperators = new ArrayList();
for (PTContainer c : container.getStrictAntiPrefs()) {
for (PTOperator operator : c.getOperators()) {
antiOperators.add(operator.getName());
}
}
List containerOperators = new ArrayList();
for (PTOperator operator : container.getOperators()) {
containerOperators.add(operator.getName());
}
LOG.debug("Container with operators [{}] has anti affinity with [{}]", StringUtils.join(containerOperators, ","), StringUtils.join(antiOperators, ","));
}
}
for (Map.Entry operEntry : this.newOpers.entrySet()) {
initCheckpoint(operEntry.getKey(), operEntry.getValue(), Checkpoint.INITIAL_CHECKPOINT);
}
// request initial deployment
ctx.deploy(Collections.emptySet(), Collections.emptySet(), Sets.newHashSet(containers), deployOperators);
this.newOpers.clear();
this.deployOpers.clear();
this.undeployOpers.clear();
}
public void setAntiAffinityForContainers(LogicalPlan dag, Collection affinityRules, Map operatorContainerMap)
{
for (AffinityRule rule : affinityRules) {
if (rule.getOperatorsList() != null && rule.getType() == Type.ANTI_AFFINITY) {
for (int i = 0; i < rule.getOperatorsList().size() - 1; i++) {
for (int j = i + 1; j < rule.getOperatorsList().size(); j++) {
OperatorPair operators = new OperatorPair(rule.getOperatorsList().get(i), rule.getOperatorsList().get(j));
PMapping firstPMapping = logicalToPTOperator.get(dag.getOperatorMeta(operators.first));
OperatorMeta opMeta = dag.getOperatorMeta(operators.second);
PMapping secondMapping = logicalToPTOperator.get(opMeta);
for (PTOperator firstPtOperator : firstPMapping.partitions) {
PTContainer firstContainer = operatorContainerMap.get(firstPtOperator);
for (PTOperator secondPtOperator : secondMapping.partitions) {
PTContainer secondContainer = operatorContainerMap.get(secondPtOperator);
if (firstContainer == secondContainer || firstContainer.getStrictAntiPrefs().contains(secondContainer)) {
continue;
}
if (rule.isRelaxLocality()) {
firstContainer.getPreferredAntiPrefs().add(secondContainer);
secondContainer.getPreferredAntiPrefs().add(firstContainer);
} else {
firstContainer.getStrictAntiPrefs().add(secondContainer);
secondContainer.getStrictAntiPrefs().add(firstContainer);
}
}
}
}
}
}
}
}
private void updatePartitionsInfoForPersistOperator(LogicalPlan dag)
{
// Add Partition mask and partition keys of Sinks to persist to Wrapper
// StreamCodec for persist operator
try {
for (OperatorMeta n : dag.getAllOperators()) {
for (StreamMeta s : n.getOutputStreams().values()) {
if (s.getPersistOperator() != null) {
InputPortMeta persistInputPort = s.getPersistOperatorInputPort();
StreamCodecWrapperForPersistance> persistCodec = (StreamCodecWrapperForPersistance>)persistInputPort.getAttributes().get(PortContext.STREAM_CODEC);
if (persistCodec == null) {
continue;
}
// Logging is enabled for the stream
for (InputPortMeta portMeta : s.getSinksToPersist()) {
updatePersistOperatorWithSinkPartitions(persistInputPort, s.getPersistOperator(), persistCodec, portMeta);
}
}
// Check partitioning for persist operators per sink too
for (Map.Entry entry : s.sinkSpecificPersistInputPortMap.entrySet()) {
InputPortMeta persistInputPort = entry.getValue();
StreamCodec> codec = persistInputPort.getAttributes().get(PortContext.STREAM_CODEC);
if (codec != null) {
if (codec instanceof StreamCodecWrapperForPersistance) {
StreamCodecWrapperForPersistance> persistCodec = (StreamCodecWrapperForPersistance>)codec;
updatePersistOperatorWithSinkPartitions(persistInputPort, s.sinkSpecificPersistOperatorMap.get(entry.getKey()), persistCodec, entry.getKey());
}
}
}
}
}
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
private void updatePersistOperatorWithSinkPartitions(InputPortMeta persistInputPort, OperatorMeta persistOperatorMeta, StreamCodecWrapperForPersistance> persistCodec, InputPortMeta sinkPortMeta)
{
Collection ptOperators = getOperators(sinkPortMeta.getOperatorWrapper());
Collection partitionKeysList = new ArrayList<>();
for (PTOperator p : ptOperators) {
PartitionKeys keys = p.partitionKeys.get(sinkPortMeta);
partitionKeysList.add(keys);
}
persistCodec.inputPortToPartitionMap.put(sinkPortMeta, partitionKeysList);
}
private void updatePersistOperatorStreamCodec(LogicalPlan dag)
{
HashMap> streamMetaToCodecMap = new HashMap<>();
try {
for (OperatorMeta n : dag.getAllOperators()) {
for (StreamMeta s : n.getOutputStreams().values()) {
if (s.getPersistOperator() != null) {
Map> inputStreamCodecs = new HashMap<>();
// Logging is enabled for the stream
for (InputPortMeta portMeta : s.getSinksToPersist()) {
InputPort> port = portMeta.getPortObject();
StreamCodec> inputStreamCodec = (portMeta.getValue(PortContext.STREAM_CODEC) != null) ? portMeta.getValue(PortContext.STREAM_CODEC) : port.getStreamCodec();
if (inputStreamCodec != null) {
boolean alreadyAdded = false;
for (StreamCodec> codec : inputStreamCodecs.values()) {
if (inputStreamCodec.equals(codec)) {
alreadyAdded = true;
break;
}
}
if (!alreadyAdded) {
inputStreamCodecs.put(portMeta, inputStreamCodec);
}
}
}
if (inputStreamCodecs.isEmpty()) {
// Stream codec not specified
// So everything out of Source should be captured without any
// StreamCodec
// Do nothing
} else {
// Create Wrapper codec for Stream persistence using all unique
// stream codecs
// Logger should write merged or union of all input stream codecs
StreamCodec> specifiedCodecForLogger = (s.getPersistOperatorInputPort().getValue(PortContext.STREAM_CODEC) != null) ? s.getPersistOperatorInputPort().getValue(PortContext.STREAM_CODEC) : s.getPersistOperatorInputPort().getPortObject().getStreamCodec();
@SuppressWarnings({ "unchecked", "rawtypes" })
StreamCodecWrapperForPersistance