-
Notifications
You must be signed in to change notification settings - Fork 18
Add Memq Metadata Client #134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ArtemTetenkin
wants to merge
4
commits into
4.0
Choose a base branch
from
memq_metadata_client
base: 4.0
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
26 changes: 26 additions & 0 deletions
26
...rc/main/java/com/pinterest/psc/config/PscMetadataClientToMemqConsumerConfigConverter.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package com.pinterest.psc.config; | ||
|
|
||
| import com.pinterest.memq.client.commons.ConsumerConfigs; | ||
| import com.pinterest.psc.common.TopicUri; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import java.util.Properties; | ||
|
|
||
| public class PscMetadataClientToMemqConsumerConfigConverter extends PscMetadataClientToBackendMetatadataClientConfigCoverter { | ||
| @Override | ||
| protected Map<String, String> getConfigConverterMap() { | ||
| return new HashMap<String, String>() { | ||
| private static final long serialVersionUID = 1L; | ||
|
|
||
| { | ||
| put(PscConfiguration.PSC_METADATA_CLIENT_ID, ConsumerConfigs.CLIENT_ID); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| @Override | ||
| public Properties convert(PscConfigurationInternal pscConfigurationInternal, TopicUri topicUri) { | ||
| return super.convert(pscConfigurationInternal, topicUri); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
234 changes: 234 additions & 0 deletions
234
psc/src/main/java/com/pinterest/psc/metadata/client/memq/PscMemqMetadataClient.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,234 @@ | ||
| package com.pinterest.psc.metadata.client.memq; | ||
|
|
||
| import com.pinterest.memq.client.commons.ConsumerConfigs; | ||
| import com.pinterest.memq.client.commons.serde.ByteArrayDeserializer; | ||
| import com.pinterest.memq.client.consumer.MemqConsumer; | ||
| import com.pinterest.psc.common.BaseTopicUri; | ||
| import com.pinterest.psc.common.TopicRn; | ||
| import com.pinterest.psc.common.TopicUri; | ||
| import com.pinterest.psc.common.TopicUriPartition; | ||
| import com.pinterest.psc.config.PscConfigurationInternal; | ||
| import com.pinterest.psc.config.PscMetadataClientToMemqConsumerConfigConverter; | ||
| import com.pinterest.psc.consumer.memq.MemqTopicUri; | ||
| import com.pinterest.psc.environment.Environment; | ||
| import com.pinterest.psc.exception.startup.ConfigurationException; | ||
| import com.pinterest.psc.logging.PscLogger; | ||
| import com.pinterest.psc.metadata.MetadataUtils; | ||
| import com.pinterest.psc.metadata.TopicUriMetadata; | ||
| import com.pinterest.psc.metadata.client.PscBackendMetadataClient; | ||
| import com.pinterest.psc.metadata.client.PscMetadataClient; | ||
|
|
||
| import java.io.IOException; | ||
| import java.time.Duration; | ||
| import java.util.ArrayList; | ||
| import java.util.Collection; | ||
| import java.util.HashMap; | ||
| import java.util.HashSet; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Properties; | ||
| import java.util.Set; | ||
| import java.util.concurrent.ExecutionException; | ||
| import java.util.concurrent.TimeoutException; | ||
|
|
||
| /** | ||
| * A Memq-specific implementation of the {@link PscBackendMetadataClient}. | ||
| * Uses a {@link MemqConsumer} to query metadata since Memq does not have a dedicated admin client. | ||
| */ | ||
| public class PscMemqMetadataClient extends PscBackendMetadataClient { | ||
|
|
||
| private static final PscLogger logger = PscLogger.getLogger(PscMemqMetadataClient.class); | ||
| protected MemqConsumer<byte[], byte[]> memqConsumer; | ||
|
|
||
| @Override | ||
| public void initialize( | ||
| TopicUri topicUri, | ||
| Environment env, | ||
| PscConfigurationInternal pscConfigurationInternal | ||
| ) throws ConfigurationException { | ||
| super.initialize(topicUri, env, pscConfigurationInternal); | ||
| Properties properties = new PscMetadataClientToMemqConsumerConfigConverter() | ||
| .convert(pscConfigurationInternal, topicUri); | ||
| properties.setProperty(ConsumerConfigs.BOOTSTRAP_SERVERS, discoveryConfig.getConnect()); | ||
| properties.setProperty(ConsumerConfigs.CLIENT_ID, | ||
| pscConfigurationInternal.getMetadataClientId()); | ||
| properties.setProperty(ConsumerConfigs.GROUP_ID, | ||
| pscConfigurationInternal.getMetadataClientId()); | ||
| properties.setProperty(ConsumerConfigs.KEY_DESERIALIZER_CLASS_KEY, | ||
| ByteArrayDeserializer.class.getName()); | ||
| properties.put(ConsumerConfigs.KEY_DESERIALIZER_CLASS_CONFIGS_KEY, new Properties()); | ||
| properties.setProperty(ConsumerConfigs.VALUE_DESERIALIZER_CLASS_KEY, | ||
| ByteArrayDeserializer.class.getName()); | ||
| properties.put(ConsumerConfigs.VALUE_DESERIALIZER_CLASS_CONFIGS_KEY, new Properties()); | ||
| properties.setProperty(ConsumerConfigs.DIRECT_CONSUMER, "false"); | ||
| try { | ||
| this.memqConsumer = new MemqConsumer<>(properties); | ||
| } catch (Exception e) { | ||
| throw new ConfigurationException("Failed to create Memq consumer for metadata client", e); | ||
| } | ||
| logger.info("Initialized PscMemqMetadataClient with properties: " + properties); | ||
| } | ||
|
|
||
| @Override | ||
| public List<TopicRn> listTopicRns(Duration duration) | ||
| throws ExecutionException, InterruptedException, TimeoutException { | ||
| throw new UnsupportedOperationException( | ||
| "[Memq] Listing all topics is not supported by the Memq backend."); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is needed - we'll update MemQ consumer |
||
| } | ||
|
|
||
| @Override | ||
| public Map<TopicUri, TopicUriMetadata> describeTopicUris( | ||
| Collection<TopicUri> topicUris, | ||
| Duration duration | ||
| ) throws ExecutionException, InterruptedException, TimeoutException { | ||
| Map<TopicUri, TopicUriMetadata> result = new HashMap<>(); | ||
| for (TopicUri tu : topicUris) { | ||
| subscribe(tu.getTopic()); | ||
| List<Integer> partitions = memqConsumer.getPartition(); | ||
| List<TopicUriPartition> topicUriPartitions = new ArrayList<>(); | ||
| for (int partition : partitions) { | ||
| topicUriPartitions.add(createMemqTopicUriPartition(tu, partition)); | ||
| } | ||
| result.put(tu, new TopicUriMetadata(tu, topicUriPartitions)); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| @Override | ||
| public Map<TopicUriPartition, Long> listOffsets( | ||
| Map<TopicUriPartition, PscMetadataClient.MetadataClientOption> topicUriPartitionsAndOptions, | ||
| Duration duration | ||
| ) throws ExecutionException, InterruptedException, TimeoutException { | ||
| Map<String, Set<Integer>> earliestByTopic = new HashMap<>(); | ||
| Map<String, Set<Integer>> latestByTopic = new HashMap<>(); | ||
|
|
||
| for (Map.Entry<TopicUriPartition, PscMetadataClient.MetadataClientOption> entry : | ||
| topicUriPartitionsAndOptions.entrySet()) { | ||
| TopicUriPartition tup = entry.getKey(); | ||
| String topic = tup.getTopicUri().getTopic(); | ||
|
|
||
| if (entry.getValue() == PscMetadataClient.MetadataClientOption.OFFSET_SPEC_EARLIEST) { | ||
| earliestByTopic.computeIfAbsent(topic, k -> new HashSet<>()).add(tup.getPartition()); | ||
| } else if (entry.getValue() == PscMetadataClient.MetadataClientOption.OFFSET_SPEC_LATEST) { | ||
| latestByTopic.computeIfAbsent(topic, k -> new HashSet<>()).add(tup.getPartition()); | ||
| } else { | ||
| throw new IllegalArgumentException( | ||
| "Unsupported MetadataClientOption for listOffsets(): " + entry.getValue()); | ||
| } | ||
| } | ||
|
|
||
| Map<TopicUriPartition, Long> result = new HashMap<>(); | ||
| Set<String> allTopics = new HashSet<>(); | ||
| allTopics.addAll(earliestByTopic.keySet()); | ||
| allTopics.addAll(latestByTopic.keySet()); | ||
|
|
||
| for (String topic : allTopics) { | ||
| subscribe(topic); | ||
|
|
||
| Set<Integer> earliestPartitions = earliestByTopic.getOrDefault(topic, new HashSet<>()); | ||
| if (!earliestPartitions.isEmpty()) { | ||
| Map<Integer, Long> offsets = memqConsumer.getEarliestOffsets(earliestPartitions); | ||
| for (Map.Entry<Integer, Long> e : offsets.entrySet()) { | ||
| TopicRn topicRn = MetadataUtils.createTopicRn(topicUri, topic); | ||
| result.put(createMemqTopicUriPartition(topicRn, e.getKey()), e.getValue()); | ||
| } | ||
| } | ||
|
|
||
| Set<Integer> latestPartitions = latestByTopic.getOrDefault(topic, new HashSet<>()); | ||
| if (!latestPartitions.isEmpty()) { | ||
| Map<Integer, Long> offsets = memqConsumer.getLatestOffsets(latestPartitions); | ||
| for (Map.Entry<Integer, Long> e : offsets.entrySet()) { | ||
| TopicRn topicRn = MetadataUtils.createTopicRn(topicUri, topic); | ||
| result.put(createMemqTopicUriPartition(topicRn, e.getKey()), e.getValue()); | ||
| } | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| @Override | ||
| public Map<TopicUriPartition, Long> listOffsetsForTimestamps( | ||
| Map<TopicUriPartition, Long> topicUriPartitionsAndTimes, | ||
| Duration duration | ||
| ) throws ExecutionException, InterruptedException, TimeoutException { | ||
| Map<String, Map<Integer, Long>> timestampsByTopic = new HashMap<>(); | ||
|
|
||
| for (Map.Entry<TopicUriPartition, Long> entry : topicUriPartitionsAndTimes.entrySet()) { | ||
| TopicUriPartition tup = entry.getKey(); | ||
| String topic = tup.getTopicUri().getTopic(); | ||
| timestampsByTopic.computeIfAbsent(topic, k -> new HashMap<>()) | ||
| .put(tup.getPartition(), entry.getValue()); | ||
| } | ||
|
|
||
| Map<TopicUriPartition, Long> result = new HashMap<>(); | ||
| for (Map.Entry<String, Map<Integer, Long>> entry : timestampsByTopic.entrySet()) { | ||
| String topic = entry.getKey(); | ||
| subscribe(topic); | ||
| Map<Integer, Long> offsets = memqConsumer.offsetsOfTimestamps(entry.getValue()); | ||
| for (Map.Entry<Integer, Long> offsetEntry : offsets.entrySet()) { | ||
| TopicRn topicRn = MetadataUtils.createTopicRn(topicUri, topic); | ||
| result.put( | ||
| createMemqTopicUriPartition(topicRn, offsetEntry.getKey()), | ||
| offsetEntry.getValue() | ||
| ); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| @Override | ||
| public Map<TopicUriPartition, Long> listOffsetsForConsumerGroup( | ||
| String consumerGroupId, | ||
| Collection<TopicUriPartition> topicUriPartitions, | ||
| Duration duration | ||
| ) throws ExecutionException, InterruptedException, TimeoutException { | ||
| Map<String, Set<Integer>> partitionsByTopic = new HashMap<>(); | ||
| for (TopicUriPartition tup : topicUriPartitions) { | ||
| String topic = tup.getTopicUri().getTopic(); | ||
| partitionsByTopic.computeIfAbsent(topic, k -> new HashSet<>()).add(tup.getPartition()); | ||
| } | ||
|
|
||
| Map<TopicUriPartition, Long> result = new HashMap<>(); | ||
| for (Map.Entry<String, Set<Integer>> entry : partitionsByTopic.entrySet()) { | ||
| String topic = entry.getKey(); | ||
| subscribe(topic); | ||
| for (int partition : entry.getValue()) { | ||
| long committedOffset = memqConsumer.committed(partition); | ||
| if (committedOffset == -1L) { | ||
| logger.warn( | ||
| "Consumer group {} has no committed offset for topic {} partition {}", | ||
| consumerGroupId, topic, partition | ||
| ); | ||
| continue; | ||
| } | ||
| TopicRn topicRn = MetadataUtils.createTopicRn(topicUri, topic); | ||
| result.put(createMemqTopicUriPartition(topicRn, partition), committedOffset); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| @Override | ||
| public void close() throws IOException { | ||
| if (memqConsumer != null) | ||
| memqConsumer.close(); | ||
| logger.info("Closed PscMemqMetadataClient"); | ||
| } | ||
|
|
||
| private void subscribe(String topic) throws ExecutionException { | ||
| try { | ||
| memqConsumer.subscribe(topic); | ||
| } catch (Exception e) { | ||
| throw new ExecutionException("Failed to subscribe to Memq topic " + topic, e); | ||
| } | ||
| } | ||
|
|
||
| private TopicUriPartition createMemqTopicUriPartition(TopicRn topicRn, int partition) { | ||
| return new TopicUriPartition( | ||
| new MemqTopicUri(new BaseTopicUri(topicUri.getProtocol(), topicRn)), partition); | ||
| } | ||
|
|
||
| private TopicUriPartition createMemqTopicUriPartition(TopicUri topicUri, int partition) { | ||
| return new TopicUriPartition(new MemqTopicUri(topicUri), partition); | ||
| } | ||
| } | ||
37 changes: 37 additions & 0 deletions
37
psc/src/main/java/com/pinterest/psc/metadata/creation/PscMemqMetadataClientCreator.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package com.pinterest.psc.metadata.creation; | ||
|
|
||
| import com.pinterest.psc.common.PscUtils; | ||
| import com.pinterest.psc.common.TopicUri; | ||
| import com.pinterest.psc.config.PscConfigurationInternal; | ||
| import com.pinterest.psc.consumer.memq.MemqTopicUri; | ||
| import com.pinterest.psc.environment.Environment; | ||
| import com.pinterest.psc.exception.startup.ConfigurationException; | ||
| import com.pinterest.psc.exception.startup.TopicUriSyntaxException; | ||
| import com.pinterest.psc.logging.PscLogger; | ||
| import com.pinterest.psc.metadata.client.memq.PscMemqMetadataClient; | ||
|
|
||
| /** | ||
| * A class that creates a {@link com.pinterest.psc.metadata.client.PscBackendMetadataClient} for Memq. | ||
| */ | ||
| @PscMetadataClientCreatorPlugin(backend = PscUtils.BACKEND_TYPE_MEMQ, priority = 1) | ||
| public class PscMemqMetadataClientCreator extends PscBackendMetadataClientCreator { | ||
|
|
||
| private static final PscLogger logger = PscLogger.getLogger(PscMemqMetadataClientCreator.class); | ||
|
|
||
| @Override | ||
| public PscMemqMetadataClient create(Environment env, PscConfigurationInternal pscConfigurationInternal, TopicUri clusterUri) throws ConfigurationException { | ||
| logger.info("Creating Memq metadata client for clusterUri: " + clusterUri); | ||
| PscMemqMetadataClient pscMemqMetadataClient = new PscMemqMetadataClient(); | ||
| pscMemqMetadataClient.initialize( | ||
| clusterUri, | ||
| env, | ||
| pscConfigurationInternal | ||
| ); | ||
| return pscMemqMetadataClient; | ||
| } | ||
|
|
||
| @Override | ||
| public TopicUri validateBackendTopicUri(TopicUri topicUri) throws TopicUriSyntaxException { | ||
| return MemqTopicUri.validate(topicUri); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.