Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -309,18 +309,37 @@ private void createOffsetSyncsTopic() {
// visible for testing
void computeAndCreateTopicPartitions()
throws InterruptedException, ExecutionException {
Map<String, Long> partitionCounts = knownSourceTopicPartitions.stream()
Map<String, Long> topicToParitionCount = knownSourceTopicPartitions.stream()
.collect(Collectors.groupingBy(TopicPartition::topic, Collectors.counting())).entrySet().stream()
.collect(Collectors.toMap(x -> formatRemoteTopic(x.getKey()), Entry::getValue));
Set<String> knownTargetTopics = toTopics(knownTargetTopicPartitions);
List<NewTopic> newTopics = partitionCounts.entrySet().stream()
.filter(x -> !knownTargetTopics.contains(x.getKey()))
.map(x -> new NewTopic(x.getKey(), x.getValue().intValue(), (short) replicationFactor))

// get the set of topics that are not present on the target
Set<String> topicsToCreate = topicToParitionCount.keySet().stream()
.filter(topic -> !knownTargetTopics.contains(topic))
.collect(Collectors.toSet());
if (topicsToCreate.isEmpty())
return;

// get corresponding topic configurations from the source
Map<String, Config> configs = describeTopicConfigs(topicsToCreate);
Comment thread
dhruvilshah3 marked this conversation as resolved.
Outdated

// construct collections for new topics and partitions
List<NewTopic> newTopics = topicToParitionCount.entrySet().stream()
.filter(topicAndPartitionCount -> !knownTargetTopics.contains(topicAndPartitionCount.getKey()))
.map(topicAndPartitionCount -> {
String topicName = topicAndPartitionCount.getKey();
Map<String, String> topicConfigs = configToMap(configs.get(topicName));
return new NewTopic(topicName, topicAndPartitionCount.getValue().intValue(), (short) replicationFactor)
.configs(topicConfigs);
})
.collect(Collectors.toList());
Map<String, NewPartitions> newPartitions = partitionCounts.entrySet().stream()
Map<String, NewPartitions> newPartitions = topicToParitionCount.entrySet().stream()
.filter(x -> knownTargetTopics.contains(x.getKey()))
.collect(Collectors.toMap(Entry::getKey, x -> NewPartitions.increaseTo(x.getValue().intValue())));
createTopicPartitions(partitionCounts, newTopics, newPartitions);

// create topic partitions on target
createTopicPartitions(topicToParitionCount, newTopics, newPartitions);
}

// visible for testing
Expand Down Expand Up @@ -359,6 +378,11 @@ private static Collection<TopicDescription> describeTopics(AdminClient adminClie
return adminClient.describeTopics(topics).all().get().values();
}

static Map<String, String> configToMap(Config config) {
return config.entries().stream()
.collect(Collectors.toMap(ConfigEntry::name, ConfigEntry::value));
}

@SuppressWarnings("deprecation")
// use deprecated alterConfigs API for broker compatibility back to 0.11.0
private void updateTopicConfigs(Map<String, Config> topicConfigs)
Expand Down Expand Up @@ -390,7 +414,7 @@ private static Stream<TopicPartition> expandTopicDescription(TopicDescription de
.map(x -> new TopicPartition(topic, x.partition()));
}

private Map<String, Config> describeTopicConfigs(Set<String> topics)
Map<String, Config> describeTopicConfigs(Set<String> topics)
throws InterruptedException, ExecutionException {
Set<ConfigResource> resources = topics.stream()
.map(x -> new ConfigResource(ConfigResource.Type.TOPIC, x))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.apache.kafka.connect.mirror;

import kafka.log.LogConfig;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.acl.AccessControlEntry;
import org.apache.kafka.common.acl.AclBinding;
Expand Down Expand Up @@ -183,9 +184,15 @@ public void testRefreshTopicPartitions() throws Exception {
connector.initialize(mock(ConnectorContext.class));
connector = spy(connector);

Config topicConfig = new Config(Arrays.asList(
new ConfigEntry(LogConfig.CleanupPolicyProp(), "compact"),
new ConfigEntry(LogConfig.SegmentBytesProp(), "100")));
Map<String, Config> configs = Collections.singletonMap("source.topic", topicConfig);

List<TopicPartition> sourceTopicPartitions = Collections.singletonList(new TopicPartition("topic", 0));
doReturn(sourceTopicPartitions).when(connector).findSourceTopicPartitions();
doReturn(Collections.emptyList()).when(connector).findTargetTopicPartitions();
doReturn(configs).when(connector).describeTopicConfigs(Collections.singleton("source.topic"));
doNothing().when(connector).createTopicPartitions(any(), any(), any());

connector.refreshTopicPartitions();
Expand All @@ -194,7 +201,12 @@ public void testRefreshTopicPartitions() throws Exception {

Map<String, Long> expectedPartitionCounts = new HashMap<>();
expectedPartitionCounts.put("source.topic", 1L);
List<NewTopic> expectedNewTopics = Arrays.asList(new NewTopic("source.topic", 1, (short) 0));
Map<String, String> configMap = MirrorSourceConnector.configToMap(topicConfig);
assertEquals(2, configMap.size());

List<NewTopic> expectedNewTopics = Arrays.asList(
new NewTopic("source.topic", 1, (short) 0)
.configs(configMap));

verify(connector, times(2)).computeAndCreateTopicPartitions();
verify(connector, times(2)).createTopicPartitions(
Expand All @@ -217,10 +229,17 @@ public void testRefreshTopicPartitionsTopicOnTargetFirst() throws Exception {
connector.initialize(mock(ConnectorContext.class));
connector = spy(connector);

Config topicConfig = new Config(Arrays.asList(
new ConfigEntry(LogConfig.CleanupPolicyProp(), "compact"),
new ConfigEntry(LogConfig.SegmentBytesProp(), "100")));
Map<String, Config> configs = Collections.singletonMap("source.topic", topicConfig);

List<TopicPartition> sourceTopicPartitions = Collections.emptyList();
List<TopicPartition> targetTopicPartitions = Collections.singletonList(new TopicPartition("source.topic", 0));
doReturn(sourceTopicPartitions).when(connector).findSourceTopicPartitions();
doReturn(targetTopicPartitions).when(connector).findTargetTopicPartitions();
doReturn(configs).when(connector).describeTopicConfigs(Collections.singleton("source.topic"));
doReturn(Collections.emptyMap()).when(connector).describeTopicConfigs(Collections.emptySet());
doNothing().when(connector).createTopicPartitions(any(), any(), any());

// partitions appearing on the target cluster should not cause reconfiguration
Expand All @@ -234,6 +253,5 @@ public void testRefreshTopicPartitionsTopicOnTargetFirst() throws Exception {
// when partitions are added to the source cluster, reconfiguration is triggered
connector.refreshTopicPartitions();
verify(connector, times(1)).computeAndCreateTopicPartitions();

}
}