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


##########
group-coordinator/src/main/java/org/apache/kafka/coordinator/group/streams/assignor/RackAwareStandbyPicker.java:
##########
@@ -0,0 +1,101 @@
+/*
+ * 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.kafka.coordinator.group.streams.assignor;
+
+import 
org.apache.kafka.coordinator.group.streams.assignor.IdenticalTagGroups.TagGroup;
+import org.apache.kafka.coordinator.group.streams.assignor.TagTree.TagKey;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Picks the conditions for the next standby of one task: the most tag keys 
that a tag group with room can meet,
+ * preferring the keys earlier in {@code rack.aware.assignment.tags}. A tag 
group meets a tag key when it has a value
+ * for it that no holder of the task has yet. It can then tell whether a tag 
group meets the conditions and return the
+ * least-loaded process with room among those that do.
+ *
+ * @param <P> The assignor's process type.
+ */
+final class RackAwareStandbyPicker<P> {
+
+    private final TagTree<P> tree;
+
+    /**
+     * @param tree    The tag tree of the assignment, whose used values the 
picker resets for this task.
+     * @param holders The holders of the task: its active owner and the 
standbys placed so far.
+     */
+    RackAwareStandbyPicker(final TagTree<P> tree, final 
Collection<TagGroup<P>> holders) {
+        this.tree = tree;
+        tree.clearUsedValues();
+        for (final TagGroup<P> holder : holders) {
+            markUsed(holder);
+        }
+    }
+
+    /** Records the tag values of a new holder of the task as used. */
+    void markUsed(final TagGroup<P> holder) {
+        tree.markUsed(holder);
+    }
+
+    /** The conditions for the next standby, or empty once no tag group with 
room can make the task more diverse. */
+    List<TagKey> pick() {
+        // Only if a tag key has an unused value is it possible to choose a 
process that makes the task more diverse
+        // in this tag key.
+        final List<TagKey> tagKeysWithUnusedValue = new ArrayList<>();
+        for (final TagKey tagKey : tree.tagKeys()) {
+            if (tree.hasUnusedValue(tagKey)) {
+                tagKeysWithUnusedValue.add(tagKey);
+            }
+        }
+        if (tagKeysWithUnusedValue.isEmpty()) {
+            return List.of();
+        }
+        tree.startPick();
+        // No tag group has room for the standby; with no conditions, only 
room counts.
+        if (!tree.hasTagGroupMeeting(List.of())) {
+            return List.of();
+        }
+
+        // Most picks find a tag group with room that meets the condition of 
every such tag key.
+        List<TagKey> conditions = tagKeysWithUnusedValue;
+        if (!tree.hasTagGroupMeeting(conditions)) {
+            // Otherwise the tag keys are added in priority order; a tag key 
is given up when no tag group with room
+            // meets it together with the tag keys kept so far.
+            conditions = new ArrayList<>();
+            for (final TagKey tagKey : tagKeysWithUnusedValue) {
+                final List<TagKey> withTagKey = new ArrayList<>(conditions);
+                withTagKey.add(tagKey);
+                // If withTagKey holds every tag key, the query above already 
found no tag group for it.
+                if (withTagKey.size() < tagKeysWithUnusedValue.size() && 
tree.hasTagGroupMeeting(withTagKey)) {
+                    conditions = withTagKey;
+                }
+            }
+        }
+        return conditions;
+    }
+
+    /** Whether a tag group is a candidate under the conditions of a pick: it 
meets them and has room. */
+    boolean isCandidate(final TagGroup<P> tagGroup, final List<TagKey> 
conditions) {
+        return tree.tagGroupMeeting(tagGroup, conditions) && 
tagGroup.hasRoom();
+    }
+
+    /** The least-loaded process with room of the candidates; call it before 
placing the standby. */
+    P leastLoaded(final List<TagKey> conditions) {
+        return tree.leastLoadedProcessMeeting(conditions).process;

Review Comment:
   sure,I have added a check that leastLoaded() throws on empty conditions, 
which also covers the case where it would silently return a holder. It  is only 
valid for the non-empty conditions pick() returned; the assignor stops once 
pick() is empty.



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