ferenc-csaky commented on code in PR #25218:
URL: https://github.com/apache/flink/pull/25218#discussion_r2081856639


##########
flink-core/src/main/java/org/apache/flink/configuration/JobManagerOptions.java:
##########
@@ -677,6 +679,31 @@ public InlineElement getDescription() {
                                             
code(SchedulerType.AdaptiveBatch.name()))
                                     .build());
 
+    @Experimental
+    @Documentation.Section({
+        Documentation.Sections.EXPERT_SCHEDULING,
+        Documentation.Sections.ALL_JOB_MANAGER
+    })
+    public static final ConfigOption<Boolean> 
SCHEDULER_RESOURCE_MINIMAL_TASK_MANAGERS_ENABLED =
+            
key("jobmanager.adaptive-scheduler.resource.minimal-taskmanagers-preferred")
+                    .booleanType()
+                    .defaultValue(true)
+                    .withDescription(
+                            Description.builder()
+                                    .text(
+                                            "This parameter defines whether 
the adaptive scheduler prioritizes "
+                                                    + "using the minimum 
number of %s when scheduling tasks.",
+                                            code("TaskManagers"))
+                                    .linebreak()
+                                    .text(
+                                            "Note, this parameter is 
introduced exclusively in Flink 1.x(1.1x & 1.2x) LTS versions and suitable for 
non-enabling %s. "
+                                                    + "More detail about the 
parameter could be viewed in %s.",

Review Comment:
   nit: `More details about this configuration are available at %s.` 



##########
flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/allocator/StateLocalitySlotAssigner.java:
##########
@@ -94,22 +95,24 @@ public Collection<SlotAssignment> assignSlots(
             Collection<? extends SlotInfo> freeSlots,
             VertexParallelism vertexParallelism,
             JobAllocationsInformation previousAllocations) {
-        checkState(
-                freeSlots.size() >= 
jobInformation.getSlotSharingGroups().size(),
-                "Not enough slots to allocate all the slot sharing groups 
(have: %s, need: %s)",
-                freeSlots.size(),
-                jobInformation.getSlotSharingGroups().size());
+        checkMinimalRequiredSlots(jobInformation, freeSlots);
 
-        final List<ExecutionSlotSharingGroup> allGroups = new ArrayList<>();
+        final List<ExecutionSlotSharingGroup> allExecutionSlotSharingGroups = 
new ArrayList<>();

Review Comment:
   I'd rather not rename this. Does not add anything to the final change and 
makes it less obvious what actually changed.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/allocator/DefaultSlotAssigner.java:
##########
@@ -18,21 +18,25 @@
 
 package org.apache.flink.runtime.scheduler.adaptive.allocator;
 
+import org.apache.flink.annotation.VisibleForTesting;
 import org.apache.flink.runtime.jobmanager.scheduler.SlotSharingGroup;
 import org.apache.flink.runtime.jobmaster.SlotInfo;
 import 
org.apache.flink.runtime.scheduler.adaptive.JobSchedulingPlan.SlotAssignment;
 import 
org.apache.flink.runtime.scheduler.adaptive.allocator.SlotSharingSlotAllocator.ExecutionSlotSharingGroup;
-import org.apache.flink.runtime.scheduler.strategy.ExecutionVertexID;
+import org.apache.flink.runtime.taskmanager.TaskManagerLocation;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.stream.Collectors;
+
+import static 
org.apache.flink.runtime.scheduler.adaptive.allocator.SlotAssigner.checkSlotsSufficient;
+import static 
org.apache.flink.runtime.scheduler.adaptive.allocator.SlotAssigner.createExecutionSlotSharingGroups;
+import static 
org.apache.flink.runtime.scheduler.adaptive.allocator.SlotAssigner.getSlotsPerTaskExecutor;
+import static 
org.apache.flink.runtime.scheduler.adaptive.allocator.SlotAssigner.sortTaskExecutors;
 
 /** Simple {@link SlotAssigner} that treats all slots and slot sharing groups 
equally. */

Review Comment:
   This class level javadoc should be updated.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/allocator/AllocatorUtil.java:
##########
@@ -0,0 +1,61 @@
+/*
+ * 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.adaptive.allocator;
+
+import org.apache.flink.runtime.instance.SlotSharingGroupId;
+import org.apache.flink.runtime.jobmaster.SlotInfo;
+
+import javax.annotation.Nonnull;
+
+import java.util.Collection;
+import java.util.Map;
+
+import static org.apache.flink.util.Preconditions.checkState;
+
+/** The allocator util class. */
+class AllocatorUtil {
+
+    private AllocatorUtil() {}
+
+    static Map<SlotSharingGroupId, 
SlotSharingSlotAllocator.SlotSharingGroupMetaInfo>
+            getSlotSharingGroupMetaInfos(@Nonnull JobInformation 
jobInformation) {

Review Comment:
   Although there are examples in the repo of `@Nonnull` usage, I personally do 
not think it is a very good practice. It just adds more boilerplate to an 
already wordy language. I think marking possibly `null` params/fields as 
`@Nullable` and handling everything else non-null implicitly is a lot better 
practice.
   
   Feel free to leave it as is if you prefer it this way, just wanted to share 
my opinion.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/allocator/DefaultSlotAssigner.java:
##########
@@ -22,56 +22,119 @@
 import org.apache.flink.runtime.jobmaster.SlotInfo;
 import 
org.apache.flink.runtime.scheduler.adaptive.JobSchedulingPlan.SlotAssignment;
 import 
org.apache.flink.runtime.scheduler.adaptive.allocator.SlotSharingSlotAllocator.ExecutionSlotSharingGroup;
-import org.apache.flink.runtime.scheduler.strategy.ExecutionVertexID;
+import org.apache.flink.runtime.taskmanager.TaskManagerLocation;
+
+import javax.annotation.Nullable;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import static java.util.function.Function.identity;
+import static 
org.apache.flink.runtime.scheduler.adaptive.allocator.AllocatorUtil.checkMinimalRequiredSlots;
+import static 
org.apache.flink.runtime.scheduler.adaptive.allocator.SlotAssigner.createExecutionSlotSharingGroups;
+
 /** Simple {@link SlotAssigner} that treats all slots and slot sharing groups 
equally. */
 public class DefaultSlotAssigner implements SlotAssigner {
 
+    public static final String APPLICATION_MODE_EXECUTION_TARGET = "embedded";

Review Comment:
   This constant can be package-private.



##########
flink-core/src/main/java/org/apache/flink/configuration/JobManagerOptions.java:
##########
@@ -677,6 +679,31 @@ public InlineElement getDescription() {
                                             
code(SchedulerType.AdaptiveBatch.name()))
                                     .build());
 
+    @Experimental
+    @Documentation.Section({
+        Documentation.Sections.EXPERT_SCHEDULING,
+        Documentation.Sections.ALL_JOB_MANAGER
+    })
+    public static final ConfigOption<Boolean> 
SCHEDULER_RESOURCE_MINIMAL_TASK_MANAGERS_ENABLED =
+            
key("jobmanager.adaptive-scheduler.resource.minimal-taskmanagers-preferred")

Review Comment:
   I think something like 
`jobmanager.adaptive-scheduler.prefer-minimal-taskmanagers` would be comply 
more to the other config options. There is no `resource` subgroup, so it's a 
bit odd to use it for only this one config option.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/allocator/DefaultSlotAssigner.java:
##########
@@ -22,56 +22,119 @@
 import org.apache.flink.runtime.jobmaster.SlotInfo;
 import 
org.apache.flink.runtime.scheduler.adaptive.JobSchedulingPlan.SlotAssignment;
 import 
org.apache.flink.runtime.scheduler.adaptive.allocator.SlotSharingSlotAllocator.ExecutionSlotSharingGroup;
-import org.apache.flink.runtime.scheduler.strategy.ExecutionVertexID;
+import org.apache.flink.runtime.taskmanager.TaskManagerLocation;
+
+import javax.annotation.Nullable;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import static java.util.function.Function.identity;
+import static 
org.apache.flink.runtime.scheduler.adaptive.allocator.AllocatorUtil.checkMinimalRequiredSlots;
+import static 
org.apache.flink.runtime.scheduler.adaptive.allocator.SlotAssigner.createExecutionSlotSharingGroups;
+
 /** Simple {@link SlotAssigner} that treats all slots and slot sharing groups 
equally. */
 public class DefaultSlotAssigner implements SlotAssigner {
 
+    public static final String APPLICATION_MODE_EXECUTION_TARGET = "embedded";
+
+    private final @Nullable String executionTarget;
+    private final boolean minimalTaskManagerPreferred;
+
+    public DefaultSlotAssigner(

Review Comment:
   Ctor can be package-private.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/allocator/AllocatorUtil.java:
##########
@@ -0,0 +1,61 @@
+/*
+ * 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.adaptive.allocator;
+
+import org.apache.flink.runtime.instance.SlotSharingGroupId;
+import org.apache.flink.runtime.jobmaster.SlotInfo;
+
+import javax.annotation.Nonnull;
+
+import java.util.Collection;
+import java.util.Map;
+
+import static org.apache.flink.util.Preconditions.checkState;
+
+/** The allocator util class. */
+class AllocatorUtil {
+
+    private AllocatorUtil() {}
+
+    static Map<SlotSharingGroupId, 
SlotSharingSlotAllocator.SlotSharingGroupMetaInfo>
+            getSlotSharingGroupMetaInfos(@Nonnull JobInformation 
jobInformation) {
+        return 
SlotSharingSlotAllocator.SlotSharingGroupMetaInfo.from(jobInformation.getVertices());
+    }
+
+    static int getMinimalRequiredSlots(
+            @Nonnull
+                    Map<SlotSharingGroupId, 
SlotSharingSlotAllocator.SlotSharingGroupMetaInfo>
+                            slotSharingGroupMetaInfos) {
+        return slotSharingGroupMetaInfos.values().stream()
+                
.map(SlotSharingSlotAllocator.SlotSharingGroupMetaInfo::getMaxLowerBound)
+                .reduce(0, Integer::sum);
+    }
+
+    static void checkMinimalRequiredSlots(

Review Comment:
   nit: minimal -> minimum, IMO that is the correct phrase here.



##########
flink-core/src/main/java/org/apache/flink/configuration/JobManagerOptions.java:
##########
@@ -677,6 +679,31 @@ public InlineElement getDescription() {
                                             
code(SchedulerType.AdaptiveBatch.name()))
                                     .build());
 
+    @Experimental
+    @Documentation.Section({
+        Documentation.Sections.EXPERT_SCHEDULING,
+        Documentation.Sections.ALL_JOB_MANAGER
+    })
+    public static final ConfigOption<Boolean> 
SCHEDULER_RESOURCE_MINIMAL_TASK_MANAGERS_ENABLED =
+            
key("jobmanager.adaptive-scheduler.resource.minimal-taskmanagers-preferred")
+                    .booleanType()
+                    .defaultValue(true)
+                    .withDescription(
+                            Description.builder()
+                                    .text(
+                                            "This parameter defines whether 
the adaptive scheduler prioritizes "
+                                                    + "using the minimum 
number of %s when scheduling tasks.",
+                                            code("TaskManagers"))
+                                    .linebreak()
+                                    .text(
+                                            "Note, this parameter is 
introduced exclusively in Flink 1.x(1.1x & 1.2x) LTS versions and suitable for 
non-enabling %s. "

Review Comment:
   Now I think the appropriate phrase here would be: `...exclusively in Flink 
1.20 LTS release line and...`.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/allocator/AllocatorUtil.java:
##########
@@ -0,0 +1,61 @@
+/*
+ * 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.adaptive.allocator;
+
+import org.apache.flink.runtime.instance.SlotSharingGroupId;
+import org.apache.flink.runtime.jobmaster.SlotInfo;
+
+import javax.annotation.Nonnull;
+
+import java.util.Collection;
+import java.util.Map;
+
+import static org.apache.flink.util.Preconditions.checkState;
+
+/** The allocator util class. */
+class AllocatorUtil {
+
+    private AllocatorUtil() {}
+
+    static Map<SlotSharingGroupId, 
SlotSharingSlotAllocator.SlotSharingGroupMetaInfo>
+            getSlotSharingGroupMetaInfos(@Nonnull JobInformation 
jobInformation) {
+        return 
SlotSharingSlotAllocator.SlotSharingGroupMetaInfo.from(jobInformation.getVertices());
+    }
+
+    static int getMinimalRequiredSlots(

Review Comment:
   nit: minimal -> minimum, IMO that is the correct phrase here.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/allocator/SlotAssigner.java:
##########
@@ -32,4 +46,77 @@ Collection<SlotAssignment> assignSlots(
             Collection<? extends SlotInfo> freeSlots,
             VertexParallelism vertexParallelism,
             JobAllocationsInformation previousAllocations);
+
+    /**
+     * Select the target slots to assign with the requested groups.
+     *
+     * @param slots the raw slots to filter.
+     * @param slotsByTaskExecutor slots per task executor.
+     * @param requestedGroups the number of the request execution slot sharing 
groups.
+     * @return the target slots that are distributed on the minimal task 
executors.
+     */
+    default Collection<? extends SlotInfo> selectSlotsInMinimalTaskExecutors(
+            Collection<? extends SlotInfo> slots,
+            Map<TaskManagerLocation, ? extends Set<? extends SlotInfo>> 
slotsByTaskExecutor,
+            int requestedGroups,
+            List<TaskManagerLocation> sortedTaskExecutors) {
+        if (slots.size() - requestedGroups <= 0) {
+            return slots;
+        }
+
+        int requestedSlots = requestedGroups;
+        final List<SlotInfo> result = new ArrayList<>();
+        for (TaskManagerLocation tml : sortedTaskExecutors) {
+            if (requestedSlots <= 0) {
+                break;
+            }
+            final Set<? extends SlotInfo> slotInfos = 
slotsByTaskExecutor.get(tml);
+            requestedSlots -= slotInfos.size();
+            result.addAll(slotInfos);
+        }
+        return result;
+    }
+
+    /**
+     * Get the task executors with the order that aims to priority assigning 
requested groups on it.
+     *
+     * @param taskManagerLocations task executors to sort.
+     * @param taskExecutorComparator the comparator to compare the target task 
executors.
+     * @return The sorted task executors list with the specified order by the 
comparator.
+     */
+    static List<TaskManagerLocation> sortTaskExecutors(
+            Collection<TaskManagerLocation> taskManagerLocations,
+            Comparator<TaskManagerLocation> taskExecutorComparator) {
+        return taskManagerLocations.stream()
+                .sorted(taskExecutorComparator)
+                .collect(Collectors.toList());
+    }
+
+    static Map<TaskManagerLocation, ? extends Set<? extends SlotInfo>> 
getSlotsPerTaskExecutor(
+            Collection<? extends SlotInfo> slots) {
+        return slots.stream()
+                .collect(
+                        Collectors.groupingBy(
+                                SlotInfo::getTaskManagerLocation,
+                                Collectors.mapping(identity(), 
Collectors.toSet())));
+    }
+
+    static List<ExecutionSlotSharingGroup> createExecutionSlotSharingGroups(

Review Comment:
   I agree with @XComp, this method should be moved to `AllocatorUtil`.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/allocator/DefaultSlotAssigner.java:
##########
@@ -22,56 +22,119 @@
 import org.apache.flink.runtime.jobmaster.SlotInfo;
 import 
org.apache.flink.runtime.scheduler.adaptive.JobSchedulingPlan.SlotAssignment;
 import 
org.apache.flink.runtime.scheduler.adaptive.allocator.SlotSharingSlotAllocator.ExecutionSlotSharingGroup;
-import org.apache.flink.runtime.scheduler.strategy.ExecutionVertexID;
+import org.apache.flink.runtime.taskmanager.TaskManagerLocation;
+
+import javax.annotation.Nullable;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import static java.util.function.Function.identity;
+import static 
org.apache.flink.runtime.scheduler.adaptive.allocator.AllocatorUtil.checkMinimalRequiredSlots;
+import static 
org.apache.flink.runtime.scheduler.adaptive.allocator.SlotAssigner.createExecutionSlotSharingGroups;
+
 /** Simple {@link SlotAssigner} that treats all slots and slot sharing groups 
equally. */
 public class DefaultSlotAssigner implements SlotAssigner {
 
+    public static final String APPLICATION_MODE_EXECUTION_TARGET = "embedded";
+
+    private final @Nullable String executionTarget;
+    private final boolean minimalTaskManagerPreferred;
+
+    public DefaultSlotAssigner(
+            @Nullable String executionTarget, boolean 
minimalTaskManagerPreferred) {
+        this.executionTarget = executionTarget;
+        this.minimalTaskManagerPreferred = minimalTaskManagerPreferred;
+    }
+
     @Override
     public Collection<SlotAssignment> assignSlots(
             JobInformation jobInformation,
             Collection<? extends SlotInfo> freeSlots,
             VertexParallelism vertexParallelism,
             JobAllocationsInformation previousAllocations) {
-        List<ExecutionSlotSharingGroup> allGroups = new ArrayList<>();
+        checkMinimalRequiredSlots(jobInformation, freeSlots);
+
+        final List<ExecutionSlotSharingGroup> allExecutionSlotSharingGroups = 
new ArrayList<>();
         for (SlotSharingGroup slotSharingGroup : 
jobInformation.getSlotSharingGroups()) {
-            
allGroups.addAll(createExecutionSlotSharingGroups(vertexParallelism, 
slotSharingGroup));
+            allExecutionSlotSharingGroups.addAll(
+                    createExecutionSlotSharingGroups(vertexParallelism, 
slotSharingGroup));
         }
 
-        Iterator<? extends SlotInfo> iterator = freeSlots.iterator();
+        final Collection<? extends SlotInfo> pickedSlots =
+                pickSlotsIfNeeded(allExecutionSlotSharingGroups.size(), 
freeSlots);
+
+        Iterator<? extends SlotInfo> iterator = pickedSlots.iterator();
         Collection<SlotAssignment> assignments = new ArrayList<>();
-        for (ExecutionSlotSharingGroup group : allGroups) {
+        for (ExecutionSlotSharingGroup group : allExecutionSlotSharingGroups) {
             assignments.add(new SlotAssignment(iterator.next(), group));
         }
         return assignments;
     }
 
-    static List<ExecutionSlotSharingGroup> createExecutionSlotSharingGroups(
-            VertexParallelism vertexParallelism, SlotSharingGroup 
slotSharingGroup) {
-        final Map<Integer, Set<ExecutionVertexID>> 
sharedSlotToVertexAssignment = new HashMap<>();
-        slotSharingGroup
-                .getJobVertexIds()
-                .forEach(
-                        jobVertexId -> {
-                            int parallelism = 
vertexParallelism.getParallelism(jobVertexId);
-                            for (int subtaskIdx = 0; subtaskIdx < parallelism; 
subtaskIdx++) {
-                                sharedSlotToVertexAssignment
-                                        .computeIfAbsent(subtaskIdx, ignored 
-> new HashSet<>())
-                                        .add(new 
ExecutionVertexID(jobVertexId, subtaskIdx));
-                            }
-                        });
-        return sharedSlotToVertexAssignment.values().stream()
-                .map(ExecutionSlotSharingGroup::new)
-                .collect(Collectors.toList());
+    private Collection<? extends SlotInfo> pickSlotsIfNeeded(
+            int requestExecutionSlotSharingGroups, Collection<? extends 
SlotInfo> freeSlots) {
+        Collection<? extends SlotInfo> pickedSlots = freeSlots;
+        // To avoid the sort-work loading.
+        if (APPLICATION_MODE_EXECUTION_TARGET.equalsIgnoreCase(executionTarget)
+                && minimalTaskManagerPreferred
+                && freeSlots.size() > requestExecutionSlotSharingGroups) {
+            final Map<TaskManagerLocation, ? extends Set<? extends SlotInfo>> 
slotsPerTaskExecutor =
+                    getSlotsPerTaskExecutor(freeSlots);
+            pickedSlots =
+                    pickSlotsInMinimalTaskExecutors(
+                            slotsPerTaskExecutor, 
requestExecutionSlotSharingGroups);
+        }
+        return pickedSlots;
+    }
+
+    /**
+     * In order to minimize the using of task executors at the resource 
manager side in the

Review Comment:
   If we add a proper javadoc, lets make it complete and include `@param` and 
`@return`.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/allocator/DefaultSlotAssigner.java:
##########
@@ -22,56 +22,119 @@
 import org.apache.flink.runtime.jobmaster.SlotInfo;
 import 
org.apache.flink.runtime.scheduler.adaptive.JobSchedulingPlan.SlotAssignment;
 import 
org.apache.flink.runtime.scheduler.adaptive.allocator.SlotSharingSlotAllocator.ExecutionSlotSharingGroup;
-import org.apache.flink.runtime.scheduler.strategy.ExecutionVertexID;
+import org.apache.flink.runtime.taskmanager.TaskManagerLocation;
+
+import javax.annotation.Nullable;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import static java.util.function.Function.identity;
+import static 
org.apache.flink.runtime.scheduler.adaptive.allocator.AllocatorUtil.checkMinimalRequiredSlots;
+import static 
org.apache.flink.runtime.scheduler.adaptive.allocator.SlotAssigner.createExecutionSlotSharingGroups;
+
 /** Simple {@link SlotAssigner} that treats all slots and slot sharing groups 
equally. */
 public class DefaultSlotAssigner implements SlotAssigner {
 
+    public static final String APPLICATION_MODE_EXECUTION_TARGET = "embedded";
+
+    private final @Nullable String executionTarget;
+    private final boolean minimalTaskManagerPreferred;
+
+    public DefaultSlotAssigner(
+            @Nullable String executionTarget, boolean 
minimalTaskManagerPreferred) {
+        this.executionTarget = executionTarget;
+        this.minimalTaskManagerPreferred = minimalTaskManagerPreferred;
+    }
+
     @Override
     public Collection<SlotAssignment> assignSlots(
             JobInformation jobInformation,
             Collection<? extends SlotInfo> freeSlots,
             VertexParallelism vertexParallelism,
             JobAllocationsInformation previousAllocations) {
-        List<ExecutionSlotSharingGroup> allGroups = new ArrayList<>();
+        checkMinimalRequiredSlots(jobInformation, freeSlots);
+
+        final List<ExecutionSlotSharingGroup> allExecutionSlotSharingGroups = 
new ArrayList<>();
         for (SlotSharingGroup slotSharingGroup : 
jobInformation.getSlotSharingGroups()) {
-            
allGroups.addAll(createExecutionSlotSharingGroups(vertexParallelism, 
slotSharingGroup));
+            allExecutionSlotSharingGroups.addAll(
+                    createExecutionSlotSharingGroups(vertexParallelism, 
slotSharingGroup));
         }
 
-        Iterator<? extends SlotInfo> iterator = freeSlots.iterator();
+        final Collection<? extends SlotInfo> pickedSlots =
+                pickSlotsIfNeeded(allExecutionSlotSharingGroups.size(), 
freeSlots);
+
+        Iterator<? extends SlotInfo> iterator = pickedSlots.iterator();
         Collection<SlotAssignment> assignments = new ArrayList<>();
-        for (ExecutionSlotSharingGroup group : allGroups) {
+        for (ExecutionSlotSharingGroup group : allExecutionSlotSharingGroups) {
             assignments.add(new SlotAssignment(iterator.next(), group));
         }
         return assignments;
     }
 
-    static List<ExecutionSlotSharingGroup> createExecutionSlotSharingGroups(
-            VertexParallelism vertexParallelism, SlotSharingGroup 
slotSharingGroup) {
-        final Map<Integer, Set<ExecutionVertexID>> 
sharedSlotToVertexAssignment = new HashMap<>();
-        slotSharingGroup
-                .getJobVertexIds()
-                .forEach(
-                        jobVertexId -> {
-                            int parallelism = 
vertexParallelism.getParallelism(jobVertexId);
-                            for (int subtaskIdx = 0; subtaskIdx < parallelism; 
subtaskIdx++) {
-                                sharedSlotToVertexAssignment
-                                        .computeIfAbsent(subtaskIdx, ignored 
-> new HashSet<>())
-                                        .add(new 
ExecutionVertexID(jobVertexId, subtaskIdx));
-                            }
-                        });
-        return sharedSlotToVertexAssignment.values().stream()
-                .map(ExecutionSlotSharingGroup::new)
-                .collect(Collectors.toList());
+    private Collection<? extends SlotInfo> pickSlotsIfNeeded(
+            int requestExecutionSlotSharingGroups, Collection<? extends 
SlotInfo> freeSlots) {
+        Collection<? extends SlotInfo> pickedSlots = freeSlots;
+        // To avoid the sort-work loading.
+        if (APPLICATION_MODE_EXECUTION_TARGET.equalsIgnoreCase(executionTarget)
+                && minimalTaskManagerPreferred
+                && freeSlots.size() > requestExecutionSlotSharingGroups) {
+            final Map<TaskManagerLocation, ? extends Set<? extends SlotInfo>> 
slotsPerTaskExecutor =
+                    getSlotsPerTaskExecutor(freeSlots);
+            pickedSlots =
+                    pickSlotsInMinimalTaskExecutors(
+                            slotsPerTaskExecutor, 
requestExecutionSlotSharingGroups);
+        }
+        return pickedSlots;
+    }
+
+    /**
+     * In order to minimize the using of task executors at the resource 
manager side in the
+     * application-mode and release more task executors in a timely manner, it 
is a good choice to
+     * prioritize selecting slots on task executors with the most available 
slots.
+     */
+    private Iterator<TaskManagerLocation> getSortedTaskExecutors(
+            Map<TaskManagerLocation, ? extends Set<? extends SlotInfo>> 
slotsPerTaskExecutor) {
+        final Comparator<TaskManagerLocation> taskExecutorComparator =
+                (leftTml, rightTml) ->
+                        Integer.compare(
+                                slotsPerTaskExecutor.get(rightTml).size(),
+                                slotsPerTaskExecutor.get(leftTml).size());
+        return 
slotsPerTaskExecutor.keySet().stream().sorted(taskExecutorComparator).iterator();
+    }
+
+    /**
+     * Pick the target slots to assign with the requested groups.
+     *
+     * @param slotsByTaskExecutor slots per task executor.
+     * @param requestedGroups the number of the request execution slot sharing 
groups.
+     * @return the target slots that are distributed on the minimal task 
executors.
+     */
+    private Collection<? extends SlotInfo> pickSlotsInMinimalTaskExecutors(
+            Map<TaskManagerLocation, ? extends Set<? extends SlotInfo>> 
slotsByTaskExecutor,
+            int requestedGroups) {
+        final List<SlotInfo> pickedSlots = new ArrayList<>();
+        final Iterator<TaskManagerLocation> sortedTaskExecutors =
+                getSortedTaskExecutors(slotsByTaskExecutor);
+        while (pickedSlots.size() < requestedGroups) {
+            Set<? extends SlotInfo> slotInfos = 
slotsByTaskExecutor.get(sortedTaskExecutors.next());
+            pickedSlots.addAll(slotInfos);
+        }
+        return pickedSlots;
+    }
+
+    static Map<TaskManagerLocation, ? extends Set<? extends SlotInfo>> 
getSlotsPerTaskExecutor(

Review Comment:
   Can be private. Why this one is `static` and `getSortedTaskExecutors`, 
`pickSlotsInMinimalTaskExecutors` are not? All 3 can be `static`, either make 
all of them `static` or none.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to