Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ public int hashCode() {
private static class ClientMetadata {

private final HostInfo hostInfo;
private final SortedSet<String> consumers;
private final ClientState state;
private final SortedSet<String> consumers;

ClientMetadata(final String endPoint) {

Expand Down Expand Up @@ -725,8 +725,10 @@ private boolean assignTasksToClients(final Set<String> allSourceTopics,
statefulTasks,
assignmentConfigs);

log.info("Assigned tasks to clients as {}{}.",
Utils.NL, clientStates.entrySet().stream().map(Map.Entry::toString).collect(Collectors.joining(Utils.NL)));
log.info("Assigned tasks {} including stateful {} to clients as: \n{}.",
allTasks, statefulTasks, clientStates.entrySet().stream()
.map(entry -> entry.getKey() + "=" + entry.getValue().currentAssignment())
.collect(Collectors.joining(Utils.NL)));

return probingRebalanceNeeded;
}
Expand Down Expand Up @@ -931,7 +933,9 @@ private Map<String, Assignment> computeNewAssignment(final Map<UUID, ClientMetad
state
);

// Arbitrarily choose the leader's client to be responsible for triggering the probing rebalance
// Arbitrarily choose the leader's client to be responsible for triggering the probing rebalance,
// note once we pick the first consumer within the process to trigger probing rebalance, other consumer
// would not set to trigger any more.
final boolean encodeNextProbingRebalanceTime = shouldTriggerProbingRebalance && clientId.equals(taskManager.processId());

final boolean tasksRevoked = addClientAssignments(
Expand All @@ -952,6 +956,18 @@ private Map<String, Assignment> computeNewAssignment(final Map<UUID, ClientMetad
rebalanceRequired = true;
log.debug("Requested client {} to schedule a followup rebalance", clientId);
}

log.info("Client {} per-consumer assignment:\n" +
"\tprev owned active {}\n" +

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This alignment looks weird.

"\tprev owned standby {}\n" +
"\tassigned active {}\n" +
"\trevoking active {}" +
"\tassigned standby {}\n", clientId,
clientMetadata.state.prevOwnedActiveByConsumer(),
clientMetadata.state.prevOwnedStandbyByConsumer(),
clientMetadata.state.assignedActiveByConsumer(),
clientMetadata.state.revokingActiveByConsumer(),
clientMetadata.state.assignedStandbyByConsumer());
}

if (rebalanceRequired) {
Expand Down Expand Up @@ -1023,14 +1039,15 @@ private boolean addClientAssignments(final Map<String, Assignment> assignment,

if (!activeTasksRemovedPendingRevokation.isEmpty()) {
// TODO: once KAFKA-10078 is resolved we can leave it to the client to trigger this rebalance
log.info("Requesting followup rebalance be scheduled immediately due to tasks changing ownership.");
log.info("Requesting {} followup rebalance be scheduled immediately due to tasks changing ownership.", consumer);
info.setNextRebalanceTime(0L);
followupRebalanceRequiredForRevokedTasks = true;
// Don't bother to schedule a probing rebalance if an immediate one is already scheduled
shouldEncodeProbingRebalance = false;
} else if (shouldEncodeProbingRebalance) {
final long nextRebalanceTimeMs = time.milliseconds() + probingRebalanceIntervalMs();
log.info("Requesting followup rebalance be scheduled for {} ms to probe for caught-up replica tasks.", nextRebalanceTimeMs);
log.info("Requesting {} followup rebalance be scheduled for {} ms to probe for caught-up replica tasks.",
consumer, nextRebalanceTimeMs);
info.setNextRebalanceTime(nextRebalanceTimeMs);
shouldEncodeProbingRebalance = false;
}
Expand Down Expand Up @@ -1061,8 +1078,11 @@ private Set<TaskId> populateActiveTaskAndPartitionsLists(final List<TopicPartiti
final List<AssignedPartition> assignedPartitions = new ArrayList<>();
final Set<TaskId> removedActiveTasks = new TreeSet<>();

// Build up list of all assigned partition-task pairs
for (final TaskId taskId : activeTasksForConsumer) {
// Populate the consumer for assigned tasks without considering revocation,
// this is for debugging purposes only
clientState.assignActiveToConsumer(taskId, consumer);

final List<AssignedPartition> assignedPartitionsForTask = new ArrayList<>();
for (final TopicPartition partition : partitionsForTask.get(taskId)) {
final String oldOwner = clientState.previousOwnerForPartition(partition);
Expand All @@ -1078,6 +1098,8 @@ private Set<TaskId> populateActiveTaskAndPartitionsLists(final List<TopicPartiti
);
removedActiveTasks.add(taskId);

clientState.revokeActiveFromConsumer(taskId, consumer);
Comment thread
abbccdda marked this conversation as resolved.

// Clear the assigned partitions list for this task if any partition can not safely be assigned,
// so as not to encode a partial task
assignedPartitionsForTask.clear();
Expand Down Expand Up @@ -1113,6 +1135,7 @@ private Map<TaskId, Set<TopicPartition>> buildStandbyTaskMap(final String consum
final ClientState clientState) {
final Map<TaskId, Set<TopicPartition>> standbyTaskMap = new HashMap<>();
for (final TaskId task : standbys) {
clientState.assignStandbyToConsumer(task, consumer);
Comment thread
abbccdda marked this conversation as resolved.
standbyTaskMap.put(task, partitionsForTask.get(task));
}
for (final TaskId task : tasksRevoked) {
Expand All @@ -1121,6 +1144,8 @@ private Map<TaskId, Set<TopicPartition>> buildStandbyTaskMap(final String consum
task,
consumer
);

clientState.assignStandbyToConsumer(task, consumer);
standbyTaskMap.put(task, partitionsForTask.get(task));

// This has no effect on the assignment, as we'll never consult the ClientState again, but
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,20 @@
*/
package org.apache.kafka.streams.processor.internals.assignment;

import java.util.stream.Collectors;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.streams.processor.TaskId;
import org.apache.kafka.streams.processor.internals.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.stream.Collectors;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.UUID;
Expand All @@ -50,10 +51,15 @@ public class ClientState {
private final Set<TaskId> prevStandbyTasks;

private final Map<String, Set<TaskId>> consumerToPreviousStatefulTaskIds;
private final Map<String, List<TaskId>> consumerToPreviousActiveTaskIds;
private final Map<String, List<TaskId>> consumerToAssignedActiveTaskIds;
private final Map<String, List<TaskId>> consumerToAssignedStandbyTaskIds;
private final Map<String, List<TaskId>> consumerToRevokingActiveTaskIds;
private final Map<TopicPartition, String> ownedPartitions;
private final Map<TaskId, Long> taskOffsetSums; // contains only stateful tasks we previously owned
private final Map<TaskId, Long> taskLagTotals; // contains lag for all stateful tasks in the app topology


private int capacity;

public ClientState() {
Expand All @@ -66,32 +72,17 @@ public ClientState() {
prevActiveTasks = new TreeSet<>();
prevStandbyTasks = new TreeSet<>();
consumerToPreviousStatefulTaskIds = new TreeMap<>();
consumerToPreviousActiveTaskIds = new TreeMap<>();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we could initialize on the parameter definition.

consumerToAssignedActiveTaskIds = new TreeMap<>();
consumerToAssignedStandbyTaskIds = new TreeMap<>();
consumerToRevokingActiveTaskIds = new TreeMap<>();
ownedPartitions = new TreeMap<>(TOPIC_PARTITION_COMPARATOR);
taskOffsetSums = new TreeMap<>();
taskLagTotals = new TreeMap<>();
this.capacity = capacity;
}

private ClientState(final Set<TaskId> activeTasks,
Comment thread
abbccdda marked this conversation as resolved.
final Set<TaskId> standbyTasks,
final Set<TaskId> prevActiveTasks,
final Set<TaskId> prevStandbyTasks,
final Map<String, Set<TaskId>> consumerToPreviousStatefulTaskIds,
final SortedMap<TopicPartition, String> ownedPartitions,
final Map<TaskId, Long> taskOffsetSums,
final Map<TaskId, Long> taskLagTotals,
final int capacity) {
this.activeTasks = activeTasks;
this.standbyTasks = standbyTasks;
this.prevActiveTasks = prevActiveTasks;
this.prevStandbyTasks = prevStandbyTasks;
this.consumerToPreviousStatefulTaskIds = consumerToPreviousStatefulTaskIds;
this.ownedPartitions = ownedPartitions;
this.taskOffsetSums = taskOffsetSums;
this.taskLagTotals = taskLagTotals;
this.capacity = capacity;
}

// For testing only
public ClientState(final Set<TaskId> previousActiveTasks,
final Set<TaskId> previousStandbyTasks,
final Map<TaskId, Long> taskLagTotals,
Expand All @@ -101,27 +92,16 @@ public ClientState(final Set<TaskId> previousActiveTasks,
prevActiveTasks = unmodifiableSet(new TreeSet<>(previousActiveTasks));
prevStandbyTasks = unmodifiableSet(new TreeSet<>(previousStandbyTasks));
consumerToPreviousStatefulTaskIds = new TreeMap<>();
consumerToPreviousActiveTaskIds = new TreeMap<>();
consumerToAssignedActiveTaskIds = new TreeMap<>();
consumerToAssignedStandbyTaskIds = new TreeMap<>();
consumerToRevokingActiveTaskIds = new TreeMap<>();
ownedPartitions = new TreeMap<>(TOPIC_PARTITION_COMPARATOR);
taskOffsetSums = emptyMap();
this.taskLagTotals = unmodifiableMap(taskLagTotals);
this.capacity = capacity;
}

public ClientState copy() {
final TreeMap<TopicPartition, String> newOwnedPartitions = new TreeMap<>(TOPIC_PARTITION_COMPARATOR);
newOwnedPartitions.putAll(ownedPartitions);
return new ClientState(
new TreeSet<>(activeTasks),
new TreeSet<>(standbyTasks),
new TreeSet<>(prevActiveTasks),
new TreeSet<>(prevStandbyTasks),
new TreeMap<>(consumerToPreviousStatefulTaskIds),
newOwnedPartitions,
new TreeMap<>(taskOffsetSums),
new TreeMap<>(taskLagTotals),
capacity);
}

int capacity() {
return capacity;
}
Expand Down Expand Up @@ -150,6 +130,48 @@ public void assignActiveTasks(final Collection<TaskId> tasks) {
activeTasks.addAll(tasks);
}

public void assignActiveToConsumer(final TaskId task, final String consumer) {
consumerToAssignedActiveTaskIds.getOrDefault(consumer, new ArrayList<>()).add(task);
}

public void assignStandbyToConsumer(final TaskId task, final String consumer) {
consumerToAssignedStandbyTaskIds.getOrDefault(consumer, new ArrayList<>()).add(task);
}

public void revokeActiveFromConsumer(final TaskId task, final String consumer) {
consumerToRevokingActiveTaskIds.getOrDefault(consumer, new ArrayList<>()).add(task);
}

public Map<String, List<TaskId>> prevOwnedActiveByConsumer() {
return consumerToPreviousActiveTaskIds;
}

public Map<String, List<TaskId>> prevOwnedStandbyByConsumer() {
// standbys are just those stateful tasks minus active tasks
final Map<String, List<TaskId>> consumerToPreviousStandbyTaskIds = new TreeMap<>();

for (final Map.Entry<String, Set<TaskId>> entry: consumerToPreviousStatefulTaskIds.entrySet()) {
final List<TaskId> standbyTaskIds = new ArrayList<>(entry.getValue());
if (consumerToPreviousActiveTaskIds.containsKey(entry.getKey()))
standbyTaskIds.removeAll(consumerToPreviousActiveTaskIds.get(entry.getKey()));
consumerToPreviousStandbyTaskIds.put(entry.getKey(), standbyTaskIds);
}

return consumerToPreviousStandbyTaskIds;
}

public Map<String, List<TaskId>> assignedActiveByConsumer() {
return consumerToAssignedActiveTaskIds;
}

public Map<String, List<TaskId>> revokingActiveByConsumer() {
return consumerToRevokingActiveTaskIds;
}

public Map<String, List<TaskId>> assignedStandbyByConsumer() {
return consumerToAssignedStandbyTaskIds;
}

public void assignActive(final TaskId task) {
assertNotAssigned(task);
activeTasks.add(task);
Expand Down Expand Up @@ -345,13 +367,17 @@ boolean hasMoreAvailableCapacityThan(final ClientState other) {
}
}

public String currentAssignment() {
return "[activeTasks: (" + activeTasks +
") standbyTasks: (" + standbyTasks + ")]";
}

@Override
public String toString() {
return "[activeTasks: (" + activeTasks +
") standbyTasks: (" + standbyTasks +
") prevActiveTasks: (" + prevActiveTasks +
") prevStandbyTasks: (" + prevStandbyTasks +
") prevOwnedPartitionsByConsumerId: (" + ownedPartitions.keySet() +
") changelogOffsetTotalsByTask: (" + taskOffsetSums.entrySet() +
") taskLagTotals: (" + taskLagTotals.entrySet() +
") capacity: " + capacity +
Expand All @@ -373,6 +399,7 @@ private void initializePrevActiveTasksFromOwnedPartitions(final Map<TopicPartiti
final TaskId task = taskForPartitionMap.get(tp);
if (task != null) {
addPreviousActiveTask(task);
consumerToPreviousActiveTaskIds.getOrDefault(partitionEntry.getValue(), new ArrayList<>()).add(task);
} else {
LOG.error("No task found for topic partition {}", tp);
}
Expand Down