org.apache.flink.runtime.scheduler.LocalInputPreferredSlotSharingStrategy Maven / Gradle / Ivy
/*
* 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.flink.runtime.scheduler;
import org.apache.flink.runtime.instance.SlotSharingGroupId;
import org.apache.flink.runtime.jobgraph.JobVertexID;
import org.apache.flink.runtime.jobmanager.scheduler.CoLocationConstraint;
import org.apache.flink.runtime.jobmanager.scheduler.CoLocationGroup;
import org.apache.flink.runtime.jobmanager.scheduler.SlotSharingGroup;
import org.apache.flink.runtime.scheduler.strategy.ExecutionVertexID;
import org.apache.flink.runtime.scheduler.strategy.SchedulingExecutionVertex;
import org.apache.flink.runtime.scheduler.strategy.SchedulingResultPartition;
import org.apache.flink.runtime.scheduler.strategy.SchedulingTopology;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static org.apache.flink.util.Preconditions.checkNotNull;
import static org.apache.flink.util.Preconditions.checkState;
/**
* This strategy tries to reduce remote data exchanges. Execution vertices, which are connected and
* belong to the same SlotSharingGroup, tend to be put in the same ExecutionSlotSharingGroup.
* Co-location constraints will be respected.
*/
class LocalInputPreferredSlotSharingStrategy implements SlotSharingStrategy {
private final Map executionSlotSharingGroupMap;
LocalInputPreferredSlotSharingStrategy(
final SchedulingTopology topology,
final Set logicalSlotSharingGroups,
final Set coLocationGroups) {
this.executionSlotSharingGroupMap =
new ExecutionSlotSharingGroupBuilder(
topology, logicalSlotSharingGroups, coLocationGroups)
.build();
}
@Override
public ExecutionSlotSharingGroup getExecutionSlotSharingGroup(
final ExecutionVertexID executionVertexId) {
return executionSlotSharingGroupMap.get(executionVertexId);
}
@Override
public Set getExecutionSlotSharingGroups() {
return new HashSet<>(executionSlotSharingGroupMap.values());
}
static class Factory implements SlotSharingStrategy.Factory {
public LocalInputPreferredSlotSharingStrategy create(
final SchedulingTopology topology,
final Set logicalSlotSharingGroups,
final Set coLocationGroups) {
return new LocalInputPreferredSlotSharingStrategy(
topology, logicalSlotSharingGroups, coLocationGroups);
}
}
private static class ExecutionSlotSharingGroupBuilder {
private final SchedulingTopology topology;
private final Map slotSharingGroupMap;
private final Map coLocationGroupMap;
private final Map
executionSlotSharingGroupMap;
final Map
constraintToExecutionSlotSharingGroupMap;
final Map> executionSlotSharingGroups;
private final Map> assignedJobVerticesForGroups;
private ExecutionSlotSharingGroupBuilder(
final SchedulingTopology topology,
final Set logicalSlotSharingGroups,
final Set coLocationGroups) {
this.topology = checkNotNull(topology);
this.slotSharingGroupMap = new HashMap<>();
for (SlotSharingGroup slotSharingGroup : logicalSlotSharingGroups) {
for (JobVertexID jobVertexId : slotSharingGroup.getJobVertexIds()) {
slotSharingGroupMap.put(jobVertexId, slotSharingGroup);
}
}
this.coLocationGroupMap = new HashMap<>();
for (CoLocationGroup coLocationGroup : coLocationGroups) {
for (JobVertexID jobVertexId : coLocationGroup.getVertexIds()) {
coLocationGroupMap.put(jobVertexId, coLocationGroup);
}
}
executionSlotSharingGroupMap = new HashMap<>();
constraintToExecutionSlotSharingGroupMap = new HashMap<>();
executionSlotSharingGroups = new HashMap<>();
assignedJobVerticesForGroups = new IdentityHashMap<>();
}
/**
* Build ExecutionSlotSharingGroups for all vertices in the topology. The
* ExecutionSlotSharingGroup of a vertex is determined in order below:
*
* 1. try finding an existing group of the corresponding co-location constraint.
*
*
2. try finding an available group of its producer vertex if the producer is in the
* same slot sharing group.
*
*
3. try finding any available group.
*
*
4. create a new group.
*/
private Map build() {
final LinkedHashMap> allVertices =
getExecutionVertices();
// loop on job vertices so that an execution vertex will not be added into a group
// if that group better fits another execution vertex
for (List executionVertices : allVertices.values()) {
final List remaining =
tryFindOptimalAvailableExecutionSlotSharingGroupFor(executionVertices);
findAvailableOrCreateNewExecutionSlotSharingGroupFor(remaining);
updateConstraintToExecutionSlotSharingGroupMap(executionVertices);
}
return executionSlotSharingGroupMap;
}
private LinkedHashMap> getExecutionVertices() {
final LinkedHashMap> vertices =
new LinkedHashMap<>();
for (SchedulingExecutionVertex executionVertex : topology.getVertices()) {
final List executionVertexGroup =
vertices.computeIfAbsent(
executionVertex.getId().getJobVertexId(), k -> new ArrayList<>());
executionVertexGroup.add(executionVertex);
}
return vertices;
}
private List tryFindOptimalAvailableExecutionSlotSharingGroupFor(
final List executionVertices) {
final List remaining = new ArrayList<>();
for (SchedulingExecutionVertex executionVertex : executionVertices) {
ExecutionSlotSharingGroup group =
tryFindAvailableCoLocatedExecutionSlotSharingGroupFor(executionVertex);
if (group == null) {
group = tryFindAvailableProducerExecutionSlotSharingGroupFor(executionVertex);
}
if (group == null) {
remaining.add(executionVertex);
} else {
addVertexToExecutionSlotSharingGroup(executionVertex, group);
}
}
return remaining;
}
private ExecutionSlotSharingGroup tryFindAvailableCoLocatedExecutionSlotSharingGroupFor(
final SchedulingExecutionVertex executionVertex) {
final ExecutionVertexID executionVertexId = executionVertex.getId();
final CoLocationGroup coLocationGroup =
coLocationGroupMap.get(executionVertexId.getJobVertexId());
if (coLocationGroup != null) {
final CoLocationConstraint constraint =
coLocationGroup.getLocationConstraint(executionVertexId.getSubtaskIndex());
return constraintToExecutionSlotSharingGroupMap.get(constraint);
} else {
return null;
}
}
private ExecutionSlotSharingGroup tryFindAvailableProducerExecutionSlotSharingGroupFor(
final SchedulingExecutionVertex executionVertex) {
final ExecutionVertexID executionVertexId = executionVertex.getId();
for (SchedulingResultPartition partition : executionVertex.getConsumedResults()) {
final ExecutionVertexID producerVertexId = partition.getProducer().getId();
if (!inSameLogicalSlotSharingGroup(producerVertexId, executionVertexId)) {
continue;
}
final ExecutionSlotSharingGroup producerGroup =
executionSlotSharingGroupMap.get(producerVertexId);
checkState(producerGroup != null);
if (isGroupAvailableForVertex(producerGroup, executionVertexId)) {
return producerGroup;
}
}
return null;
}
private boolean inSameLogicalSlotSharingGroup(
final ExecutionVertexID executionVertexId1,
final ExecutionVertexID executionVertexId2) {
return Objects.equals(
getSlotSharingGroup(executionVertexId1).getSlotSharingGroupId(),
getSlotSharingGroup(executionVertexId2).getSlotSharingGroupId());
}
private SlotSharingGroup getSlotSharingGroup(final ExecutionVertexID executionVertexId) {
// slot sharing group of a vertex would never be null in production
return checkNotNull(slotSharingGroupMap.get(executionVertexId.getJobVertexId()));
}
private boolean isGroupAvailableForVertex(
final ExecutionSlotSharingGroup executionSlotSharingGroup,
final ExecutionVertexID executionVertexId) {
final Set assignedVertices =
assignedJobVerticesForGroups.get(executionSlotSharingGroup);
return assignedVertices == null
|| !assignedVertices.contains(executionVertexId.getJobVertexId());
}
private void addVertexToExecutionSlotSharingGroup(
final SchedulingExecutionVertex vertex, final ExecutionSlotSharingGroup group) {
group.addVertex(vertex.getId());
executionSlotSharingGroupMap.put(vertex.getId(), group);
assignedJobVerticesForGroups
.computeIfAbsent(group, k -> new HashSet<>())
.add(vertex.getId().getJobVertexId());
}
private void findAvailableOrCreateNewExecutionSlotSharingGroupFor(
final List executionVertices) {
for (SchedulingExecutionVertex executionVertex : executionVertices) {
final SlotSharingGroup slotSharingGroup =
getSlotSharingGroup(executionVertex.getId());
final List groups =
executionSlotSharingGroups.computeIfAbsent(
slotSharingGroup.getSlotSharingGroupId(), k -> new ArrayList<>());
ExecutionSlotSharingGroup group = null;
for (ExecutionSlotSharingGroup executionSlotSharingGroup : groups) {
if (isGroupAvailableForVertex(
executionSlotSharingGroup, executionVertex.getId())) {
group = executionSlotSharingGroup;
break;
}
}
if (group == null) {
group = new ExecutionSlotSharingGroup();
group.setResourceProfile(slotSharingGroup.getResourceProfile());
groups.add(group);
}
addVertexToExecutionSlotSharingGroup(executionVertex, group);
}
}
private void updateConstraintToExecutionSlotSharingGroupMap(
final List executionVertices) {
for (SchedulingExecutionVertex executionVertex : executionVertices) {
final ExecutionVertexID executionVertexId = executionVertex.getId();
final CoLocationGroup coLocationGroup =
coLocationGroupMap.get(executionVertexId.getJobVertexId());
if (coLocationGroup != null) {
final CoLocationConstraint constraint =
coLocationGroup.getLocationConstraint(
executionVertexId.getSubtaskIndex());
constraintToExecutionSlotSharingGroupMap.put(
constraint, executionSlotSharingGroupMap.get(executionVertexId));
}
}
}
}
}