lucasbru commented on code in PR #23325: URL: https://github.com/apache/kafka/pull/23325#discussion_r3934090924
########## group-coordinator/src/main/java/org/apache/kafka/coordinator/group/streams/AssignmentRefinerImpl.java: ########## @@ -0,0 +1,506 @@ +/* + * 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; + +import org.apache.kafka.coordinator.group.streams.assignor.TaskId; +import org.apache.kafka.coordinator.group.streams.topics.ConfiguredSubtopology; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.function.Consumer; + +/** + * The {@link AssignmentRefiner} being built out to replace {@link NoOpAssignmentRefiner} as the broker's default + * once the derivation is complete. + * + * <p>{@link #refine} is still a stub -- it returns the target assignment unchanged, exactly like + * {@link NoOpAssignmentRefiner} -- while the derivation is built out incrementally across several changes. The + * methods below are its building blocks: indexing the current assignment, and deciding which migrations can + * complete immediately versus which have to stage behind a warm-up task. None of them are called from + * {@link #refine} yet. + */ +public class AssignmentRefinerImpl implements AssignmentRefiner { + + @Override + public Map<String, TasksTuple> refine( + Map<String, StreamsGroupMember> members, + Map<String, TasksTuple> targetAssignment, + Map<String, MemberTaskOffsets> taskOffsets, + SortedMap<String, ConfiguredSubtopology> subtopologies, + int numWarmupReplicas, + long acceptableRecoveryLag + ) { + return targetAssignment; + } + + /** + * Indexes the members' current assignment by task, so that the case analysis can look up what a task's situation is + * without scanning the group again for every task. This is a single pass over the members' task entries. + * + * <p>Only stateful tasks are indexed. A stateless task has no state to restore, so it is never staged and never + * consulted here; it simply flows through from the target assignment. + * + * @param members + * All members of the group. + * @param taskOffsets + * The latest changelog offsets/end-offsets reported by the members. + * @param subtopologies + * The resolved subtopologies, which tell whether a subtopology is stateful. + * @param acceptableRecoveryLag + * The lag at or below which a replica counts as caught up. + * + * @return The current assignment, indexed by task. + */ + static CurrentAssignmentIndex indexCurrentAssignment( + final Map<String, StreamsGroupMember> members, + final Map<String, MemberTaskOffsets> taskOffsets, + final SortedMap<String, ConfiguredSubtopology> subtopologies, + final long acceptableRecoveryLag + ) { + final Map<TaskId, String> activeOwner = new HashMap<>(); + final Map<TaskId, Set<String>> processesRevoking = new HashMap<>(); + final Map<TaskId, List<TaskCopy>> taskCopies = new HashMap<>(); + + for (final StreamsGroupMember member : members.values()) { + final MemberTaskOffsets offsets = taskOffsets.getOrDefault(member.memberId(), MemberTaskOffsets.EMPTY); + + forEachStatefulActiveTask( + member.assignedTasks().activeTasksWithEpochs(), + subtopologies, + task -> activeOwner.put(task, member.memberId()) + ); + + // The member has been told to give this task up and has stopped running it, so the member is + // deliberately not recorded as the task's active owner. Recording the member as the owner would make the + // case analysis try to keep the task there, undoing a hand-over that is already under way. + // + // The task does still occupy the member's process until the revocation completes, and that is what stops Review Comment: Wouldn't this case be taken care of by the reconciler? I mean the reconciler makes sure that I do not assign a new task before the old task is revoked. Why do we need to handle this in two places? -- 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]
