gabriellefu commented on code in PR #23588:
URL: https://github.com/apache/kafka/pull/23588#discussion_r4174593162


##########
group-coordinator/src/main/java/org/apache/kafka/coordinator/group/streams/assignor/StickyTaskAssignor.java:
##########
@@ -524,6 +605,123 @@ private static void assignStandby(final LocalState 
localState, final LinkedList<
         }
     }
 
+    /**
+     * Places the standbys of {@code task} that still make it more 
rack-diverse on processes with room and returns
+     * the processes they went to. Among the equally most diverse processes 
the choice is the tag-blind picks unchanged:
+     * the previous active member, else the least-loaded previous standby 
member, each while below the quota, else
+     * the least-loaded process and its least-loaded member.
+     * <p>
+     * {@code alreadyPlaced} are the standbys of the task placed by an earlier 
call. With {@code stickyOnly},
+     * the picks stop at the first standby that would not go back to a 
previous member and add the task to
+     * {@code rackNonSticky}, leaving it and the rest to a later call.
+     */
+    private static List<ProcessState> pickRackAwareStandbys(
+        final LocalState localState,
+        final RackAwareStandbyPicker<ProcessGroup> picker,
+        final TaskId task,
+        final List<ProcessState> alreadyPlaced,
+        final boolean stickyOnly,
+        final List<TaskId> rackNonSticky
+    ) {
+        picker.startTask();
+        
picker.markUsed(localState.processIdToGroup.get(localState.statefulActiveTaskToProcess.get(task).processId()));
+        for (final ProcessState process : alreadyPlaced) {
+            
picker.markUsed(localState.processIdToGroup.get(process.processId()));
+        }
+
+        // The picker only tests groups whose value for the priority key no 
holder carries, so no process of such a group
+        // holds the task and only room is checked.
+        final Predicate<ProcessGroup> eligible = group -> 
group.hasRoom(localState);
+
+        final List<ProcessState> placed = new ArrayList<>();
+        while (alreadyPlaced.size() + placed.size() < 
localState.numStandbyReplicas) {
+            final Set<ProcessGroup> candidates = 
picker.pickCandidates(eligible);
+            if (candidates.isEmpty()) {
+                break;
+            }
+
+            // prev active or standby member on a process with room in a 
candidate group
+            final Member prevMember = findPrevMemberForStandby(
+                localState,
+                task,
+                process -> 
candidates.contains(localState.processIdToGroup.get(process.processId())) && 
leastLoadedMemberWithRoom(localState, process) != null
+            );
+            if (prevMember != null) {
+                placeRackAwareStandby(localState, picker, 
localState.processIdToState.get(prevMember.processId), prevMember.memberId, 
task, placed);
+                continue;
+            }
+
+            if (stickyOnly) {
+                rackNonSticky.add(task);
+                break;
+            }
+
+            // least loaded process of the candidate groups. It has a member 
with room, so the lookup cannot return null.
+            // Adding to that member directly keeps the heap of members by 
load out of this pass, so that it is built
+            // once, in the least-loaded pass, as without rack awareness.
+            final ProcessState processWithLeastLoad = leastLoaded(localState, 
candidates);
+            placeRackAwareStandby(localState, picker, processWithLeastLoad, 
leastLoadedMemberWithRoom(localState, processWithLeastLoad), task, placed);
+        }
+        return placed;
+    }
+
+    private static void placeRackAwareStandby(
+        final LocalState localState,
+        final RackAwareStandbyPicker<ProcessGroup> picker,
+        final ProcessState process,
+        final String memberId,
+        final TaskId task,
+        final List<ProcessState> placed
+    ) {
+        // The queue of the group catches up with the new load once the 
process reaches its head.
+        maybeUpdateTotalTasksPerMember(localState, process.addTask(memberId, 
task, false, true));
+        picker.markUsed(localState.processIdToGroup.get(process.processId()));
+        placed.add(process);
+    }
+
+    /** The least-loaded process with room of the candidate groups, which all 
have one. */
+    private static ProcessState leastLoaded(final LocalState localState, final 
Collection<ProcessGroup> candidates) {

Review Comment:
   cause for rack-aware pick, the processes for leastLoaded() are those process 
also satisfy rack-diverse condition. So it won't save that much of time since 
each time the input are different. Also, since we are using group, the 
O(#surviver_groups) should be small enough



-- 
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