38
38
import org .apache .kafka .streams .errors .TaskAssignmentException ;
39
39
import org .apache .kafka .streams .processor .TaskId ;
40
40
import org .apache .kafka .streams .processor .assignment .ApplicationState ;
41
+ import org .apache .kafka .streams .processor .assignment .TaskInfo ;
41
42
import org .apache .kafka .streams .processor .assignment .ProcessId ;
42
43
import org .apache .kafka .streams .processor .assignment .TaskAssignor .TaskAssignment ;
43
44
import org .apache .kafka .streams .processor .internals .assignment .ApplicationStateImpl ;
52
53
import org .apache .kafka .streams .processor .internals .assignment .CopartitionedTopicsEnforcer ;
53
54
import org .apache .kafka .streams .processor .internals .assignment .FallbackPriorTaskAssignor ;
54
55
import org .apache .kafka .streams .processor .internals .assignment .RackAwareTaskAssignor ;
56
+ import org .apache .kafka .streams .processor .internals .assignment .RackUtils ;
55
57
import org .apache .kafka .streams .processor .internals .assignment .ReferenceContainer ;
56
58
import org .apache .kafka .streams .processor .internals .assignment .StickyTaskAssignor ;
57
59
import org .apache .kafka .streams .processor .internals .assignment .SubscriptionInfo ;
58
60
import org .apache .kafka .streams .processor .internals .assignment .TaskAssignor ;
61
+ import org .apache .kafka .streams .processor .internals .assignment .DefaultTaskInfo ;
59
62
import org .apache .kafka .streams .state .HostInfo ;
60
63
import org .slf4j .Logger ;
61
64
82
85
import java .util .function .Supplier ;
83
86
import java .util .stream .Collectors ;
84
87
88
+ import static java .util .Collections .unmodifiableSet ;
85
89
import static java .util .Map .Entry .comparingByKey ;
86
90
import static java .util .UUID .randomUUID ;
87
91
import static org .apache .kafka .common .utils .Utils .filterMap ;
@@ -133,8 +137,9 @@ public static class ClientMetadata {
133
137
private final HostInfo hostInfo ;
134
138
private final ClientState state ;
135
139
private final SortedSet <String > consumers ;
140
+ private final Optional <String > rackId ;
136
141
137
- ClientMetadata (final UUID processId , final String endPoint , final Map <String , String > clientTags ) {
142
+ ClientMetadata (final UUID processId , final String endPoint , final Map <String , String > clientTags , final Optional < String > rackId ) {
138
143
139
144
// get the host info, or null if no endpoint is configured (ie endPoint == null)
140
145
hostInfo = HostInfo .buildFromEndpoint (endPoint );
@@ -144,6 +149,8 @@ public static class ClientMetadata {
144
149
145
150
// initialize the client state with client tags
146
151
state = new ClientState (processId , clientTags );
152
+
153
+ this .rackId = rackId ;
147
154
}
148
155
149
156
void addConsumer (final String consumerMemberId , final List <TopicPartition > ownedPartitions ) {
@@ -164,6 +171,10 @@ public HostInfo hostInfo() {
164
171
return hostInfo ;
165
172
}
166
173
174
+ public Optional <String > rackId () {
175
+ return rackId ;
176
+ }
177
+
167
178
@ Override
168
179
public String toString () {
169
180
return "ClientMetadata{" +
@@ -355,7 +366,7 @@ public GroupAssignment assign(final Cluster metadata, final GroupSubscription gr
355
366
futureMetadataVersion = usedVersion ;
356
367
processId = FUTURE_ID ;
357
368
if (!clientMetadataMap .containsKey (FUTURE_ID )) {
358
- clientMetadataMap .put (FUTURE_ID , new ClientMetadata (FUTURE_ID , null , Collections .emptyMap ()));
369
+ clientMetadataMap .put (FUTURE_ID , new ClientMetadata (FUTURE_ID , null , Collections .emptyMap (), subscription . rackId () ));
359
370
}
360
371
} else {
361
372
processId = info .processId ();
@@ -367,7 +378,7 @@ public GroupAssignment assign(final Cluster metadata, final GroupSubscription gr
367
378
368
379
// create the new client metadata if necessary
369
380
if (clientMetadata == null ) {
370
- clientMetadata = new ClientMetadata (info .processId (), info .userEndPoint (), info .clientTags ());
381
+ clientMetadata = new ClientMetadata (info .processId (), info .userEndPoint (), info .clientTags (), subscription . rackId () );
371
382
clientMetadataMap .put (info .processId (), clientMetadata );
372
383
}
373
384
@@ -474,23 +485,84 @@ public GroupAssignment assign(final Cluster metadata, final GroupSubscription gr
474
485
*
475
486
* @param clientMetadataMap the map of process id to client metadata used to build an immutable
476
487
* {@code ApplicationState}
477
- * @param statefulTasks the set of {@code TaskId} that correspond to all the stateful
478
- * tasks that need to be reassigned.
479
488
* @return The {@code ApplicationState} needed by the TaskAssigner to compute new task
480
489
* assignments.
481
490
*/
482
- private ApplicationState buildApplicationState (final Map <UUID , ClientMetadata > clientMetadataMap ,
483
- final Set <TaskId > statefulTasks ) {
484
- final Set <TaskId > statelessTasks = new HashSet <>();
485
- for (final Map .Entry <UUID , ClientMetadata > clientEntry : clientMetadataMap .entrySet ()) {
486
- final ClientState clientState = clientEntry .getValue ().state ;
487
- statelessTasks .addAll (clientState .statelessActiveTasks ());
491
+ private ApplicationState buildApplicationState (final TopologyMetadata topologyMetadata ,
492
+ final Map <UUID , ClientMetadata > clientMetadataMap ,
493
+ final Map <Subtopology , TopicsInfo > topicGroups ,
494
+ final Cluster cluster ) {
495
+ final Map <Subtopology , Set <String >> sourceTopicsByGroup = new HashMap <>();
496
+ final Map <Subtopology , Set <String >> changelogTopicsByGroup = new HashMap <>();
497
+ for (final Map .Entry <Subtopology , TopicsInfo > entry : topicGroups .entrySet ()) {
498
+ final Set <String > sourceTopics = entry .getValue ().sourceTopics ;
499
+ final Set <String > changelogTopics = entry .getValue ().changelogTopics ();
500
+ sourceTopicsByGroup .put (entry .getKey (), sourceTopics );
501
+ changelogTopicsByGroup .put (entry .getKey (), changelogTopics );
502
+ }
503
+
504
+ final Map <TaskId , Set <TopicPartition >> sourcePartitionsForTask =
505
+ partitionGrouper .partitionGroups (sourceTopicsByGroup , cluster );
506
+ final Map <TaskId , Set <TopicPartition >> changelogPartitionsForTask =
507
+ partitionGrouper .partitionGroups (changelogTopicsByGroup , cluster );
508
+
509
+ if (!sourcePartitionsForTask .keySet ().equals (changelogPartitionsForTask .keySet ())) {
510
+ log .error ("Partition grouper returned {} tasks for source topics but {} tasks for changelog topics" ,
511
+ sourcePartitionsForTask .size (), changelogPartitionsForTask .size ());
512
+ throw new TaskAssignmentException ("Partition grouper returned conflicting information about the "
513
+ + "tasks for source topics vs changelog topics." );
488
514
}
489
515
516
+ final Set <TopicPartition > sourceTopicPartitions = new HashSet <>();
517
+ final Set <TopicPartition > nonSourceChangelogTopicPartitions = new HashSet <>();
518
+ for (final Map .Entry <TaskId , Set <TopicPartition >> entry : sourcePartitionsForTask .entrySet ()) {
519
+ final TaskId taskId = entry .getKey ();
520
+ final Set <TopicPartition > taskSourcePartitions = entry .getValue ();
521
+ final Set <TopicPartition > taskChangelogPartitions = changelogPartitionsForTask .get (taskId );
522
+ final Set <TopicPartition > taskNonSourceChangelogPartitions = new HashSet <>(taskChangelogPartitions );
523
+ taskNonSourceChangelogPartitions .removeAll (taskSourcePartitions );
524
+
525
+ sourceTopicPartitions .addAll (taskSourcePartitions );
526
+ nonSourceChangelogTopicPartitions .addAll (taskNonSourceChangelogPartitions );
527
+ }
528
+
529
+ final Map <TopicPartition , Set <String >> racksForSourcePartitions = RackUtils .getRacksForTopicPartition (
530
+ cluster , internalTopicManager , sourceTopicPartitions , false );
531
+ final Map <TopicPartition , Set <String >> racksForChangelogPartitions = RackUtils .getRacksForTopicPartition (
532
+ cluster , internalTopicManager , nonSourceChangelogTopicPartitions , true );
533
+
534
+ final Set <TaskId > logicalTaskIds = unmodifiableSet (sourcePartitionsForTask .keySet ());
535
+ final Set <TaskInfo > logicalTasks = logicalTaskIds .stream ().map (taskId -> {
536
+ final Set <String > stateStoreNames = topologyMetadata
537
+ .stateStoreNameToSourceTopicsForTopology (taskId .topologyName ())
538
+ .keySet ();
539
+ final Set <TopicPartition > sourcePartitions = sourcePartitionsForTask .get (taskId );
540
+ final Set <TopicPartition > changelogPartitions = changelogPartitionsForTask .get (taskId );
541
+ final Map <TopicPartition , Set <String >> racksForTaskPartition = new HashMap <>();
542
+ sourcePartitions .forEach (topicPartition -> {
543
+ racksForTaskPartition .put (topicPartition , racksForSourcePartitions .get (topicPartition ));
544
+ });
545
+ changelogPartitions .forEach (topicPartition -> {
546
+ if (racksForSourcePartitions .containsKey (topicPartition )) {
547
+ racksForTaskPartition .put (topicPartition , racksForSourcePartitions .get (topicPartition ));
548
+ } else {
549
+ racksForTaskPartition .put (topicPartition , racksForChangelogPartitions .get (topicPartition ));
550
+ }
551
+ });
552
+
553
+ return new DefaultTaskInfo (
554
+ taskId ,
555
+ !stateStoreNames .isEmpty (),
556
+ racksForTaskPartition ,
557
+ stateStoreNames ,
558
+ sourcePartitions ,
559
+ changelogPartitions
560
+ );
561
+ }).collect (Collectors .toSet ());
562
+
490
563
return new ApplicationStateImpl (
491
564
assignmentConfigs .toPublicAssignmentConfigs (),
492
- statefulTasks ,
493
- statelessTasks ,
565
+ logicalTasks ,
494
566
clientMetadataMap
495
567
);
496
568
}
0 commit comments