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 to clients as: {}{}.", Utils.NL,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is unnecessarily verbose, plus part of it is replaced by line 960 below, so I trimmed a bit here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, this one has always made my head spin when reading the logs

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 of the processor to trigger probing rebalance, other threads

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

super nit: processor --> process (or bet yet, 'client')

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