-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-9925: decorate pseudo-topics with app id #8574
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,16 +25,17 @@ | |
|
|
||
| import java.nio.ByteBuffer; | ||
| import java.util.Objects; | ||
| import java.util.function.Supplier; | ||
|
|
||
| public class SubscriptionWrapperSerde<K> implements Serde<SubscriptionWrapper<K>> { | ||
| private final SubscriptionWrapperSerializer<K> serializer; | ||
| private final SubscriptionWrapperDeserializer<K> deserializer; | ||
|
|
||
| public SubscriptionWrapperSerde(final String primaryKeySerializationPseudoTopic, | ||
| public SubscriptionWrapperSerde(final Supplier<String> primaryKeySerializationPseudoTopicSupplier, | ||
| final Serde<K> primaryKeySerde) { | ||
| serializer = new SubscriptionWrapperSerializer<>(primaryKeySerializationPseudoTopic, | ||
| serializer = new SubscriptionWrapperSerializer<>(primaryKeySerializationPseudoTopicSupplier, | ||
| primaryKeySerde == null ? null : primaryKeySerde.serializer()); | ||
| deserializer = new SubscriptionWrapperDeserializer<>(primaryKeySerializationPseudoTopic, | ||
| deserializer = new SubscriptionWrapperDeserializer<>(primaryKeySerializationPseudoTopicSupplier, | ||
| primaryKeySerde == null ? null : primaryKeySerde.deserializer()); | ||
| } | ||
|
|
||
|
|
@@ -51,12 +52,13 @@ public Deserializer<SubscriptionWrapper<K>> deserializer() { | |
| private static class SubscriptionWrapperSerializer<K> | ||
| implements Serializer<SubscriptionWrapper<K>>, WrappingNullableSerializer<SubscriptionWrapper<K>, K> { | ||
|
|
||
| private final String primaryKeySerializationPseudoTopic; | ||
| private final Supplier<String> primaryKeySerializationPseudoTopicSupplier; | ||
| private String primaryKeySerializationPseudoTopic = null; | ||
| private Serializer<K> primaryKeySerializer; | ||
|
|
||
| SubscriptionWrapperSerializer(final String primaryKeySerializationPseudoTopic, | ||
| SubscriptionWrapperSerializer(final Supplier<String> primaryKeySerializationPseudoTopicSupplier, | ||
| final Serializer<K> primaryKeySerializer) { | ||
| this.primaryKeySerializationPseudoTopic = primaryKeySerializationPseudoTopic; | ||
| this.primaryKeySerializationPseudoTopicSupplier = primaryKeySerializationPseudoTopicSupplier; | ||
| this.primaryKeySerializer = primaryKeySerializer; | ||
| } | ||
|
|
||
|
|
@@ -76,6 +78,10 @@ public byte[] serialize(final String ignored, final SubscriptionWrapper<K> data) | |
| throw new UnsupportedVersionException("SubscriptionWrapper version is larger than maximum supported 0x7F"); | ||
| } | ||
|
|
||
| if (primaryKeySerializationPseudoTopic == null) { | ||
| primaryKeySerializationPseudoTopic = primaryKeySerializationPseudoTopicSupplier.get(); | ||
| } | ||
|
Comment on lines
+81
to
+83
Contributor
Author
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 (and below) is a bit awkward. Our requirement is not to call the supplier until after the app starts, but we can call it any time after the app starts. The natural place would be in I'm side-stepping the problem here by just invoking the supplier when we first need to use it, which is also at run time. |
||
|
|
||
| final byte[] primaryKeySerializedData = primaryKeySerializer.serialize( | ||
| primaryKeySerializationPseudoTopic, | ||
| data.getPrimaryKey() | ||
|
|
@@ -106,12 +112,13 @@ public byte[] serialize(final String ignored, final SubscriptionWrapper<K> data) | |
| private static class SubscriptionWrapperDeserializer<K> | ||
| implements Deserializer<SubscriptionWrapper<K>>, WrappingNullableDeserializer<SubscriptionWrapper<K>, K> { | ||
|
|
||
| private final String primaryKeySerializationPseudoTopic; | ||
| private final Supplier<String> primaryKeySerializationPseudoTopicSupplier; | ||
| private String primaryKeySerializationPseudoTopic = null; | ||
| private Deserializer<K> primaryKeyDeserializer; | ||
|
|
||
| SubscriptionWrapperDeserializer(final String primaryKeySerializationPseudoTopic, | ||
| SubscriptionWrapperDeserializer(final Supplier<String> primaryKeySerializationPseudoTopicSupplier, | ||
| final Deserializer<K> primaryKeyDeserializer) { | ||
| this.primaryKeySerializationPseudoTopic = primaryKeySerializationPseudoTopic; | ||
| this.primaryKeySerializationPseudoTopicSupplier = primaryKeySerializationPseudoTopicSupplier; | ||
| this.primaryKeyDeserializer = primaryKeyDeserializer; | ||
| } | ||
|
|
||
|
|
@@ -144,6 +151,11 @@ public SubscriptionWrapper<K> deserialize(final String ignored, final byte[] dat | |
|
|
||
| final byte[] primaryKeyRaw = new byte[data.length - lengthSum]; //The remaining data is the serialized pk | ||
| buf.get(primaryKeyRaw, 0, primaryKeyRaw.length); | ||
|
|
||
| if (primaryKeySerializationPseudoTopic == null) { | ||
| primaryKeySerializationPseudoTopic = primaryKeySerializationPseudoTopicSupplier.get(); | ||
| } | ||
|
|
||
| final K primaryKey = primaryKeyDeserializer.deserialize(primaryKeySerializationPseudoTopic, | ||
| primaryKeyRaw); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1224,6 +1224,10 @@ private List<String> maybeDecorateInternalSourceTopics(final Collection<String> | |
| return decoratedTopics; | ||
| } | ||
|
|
||
| public String decoratePseudoTopic(final String topic) { | ||
|
Contributor
Author
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. I'm adding a new public method for our specific use case here, to document that we should only need to invoke this method publicly for "pseudo" topics. |
||
| return decorateTopic(topic); | ||
| } | ||
|
|
||
| private String decorateTopic(final String topic) { | ||
| if (applicationId == null) { | ||
| throw new TopologyException("there are internal topics and " | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,7 +33,6 @@ | |
| import org.apache.kafka.streams.state.KeyValueStore; | ||
| import org.apache.kafka.streams.utils.UniqueTopicSerdeScope; | ||
| import org.apache.kafka.test.TestUtils; | ||
| import org.hamcrest.CoreMatchers; | ||
| import org.junit.Rule; | ||
| import org.junit.Test; | ||
| import org.junit.rules.TestName; | ||
|
|
@@ -179,8 +178,9 @@ public void shouldWorkWithDefaultAndProducedSerdes() { | |
|
|
||
| @Test | ||
| public void shouldUseExpectedTopicsWithSerde() { | ||
| final String applicationId = "ktable-ktable-joinOnForeignKey"; | ||
| final Properties streamsConfig = mkProperties(mkMap( | ||
| mkEntry(StreamsConfig.APPLICATION_ID_CONFIG, "ktable-ktable-joinOnForeignKey"), | ||
| mkEntry(StreamsConfig.APPLICATION_ID_CONFIG, applicationId), | ||
| mkEntry(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "asdf:0000"), | ||
| mkEntry(StreamsConfig.STATE_DIR_CONFIG, TestUtils.tempDirectory().getPath()) | ||
| )); | ||
|
|
@@ -191,12 +191,12 @@ public void shouldUseExpectedTopicsWithSerde() { | |
| final KTable<String, String> left = builder.table( | ||
| LEFT_TABLE, | ||
| Consumed.with(serdeScope.decorateSerde(Serdes.String(), streamsConfig, true), | ||
| serdeScope.decorateSerde(Serdes.String(), streamsConfig, false)) | ||
| serdeScope.decorateSerde(Serdes.String(), streamsConfig, false)) | ||
| ); | ||
| final KTable<String, String> right = builder.table( | ||
| RIGHT_TABLE, | ||
| Consumed.with(serdeScope.decorateSerde(Serdes.String(), streamsConfig, true), | ||
| serdeScope.decorateSerde(Serdes.String(), streamsConfig, false)) | ||
| serdeScope.decorateSerde(Serdes.String(), streamsConfig, false)) | ||
| ); | ||
|
|
||
| left.join( | ||
|
|
@@ -218,19 +218,19 @@ public void shouldUseExpectedTopicsWithSerde() { | |
| } | ||
| // verifying primarily that no extra pseudo-topics were used, but it's nice to also verify the rest of the | ||
| // topics our serdes serialize data for | ||
| assertThat(serdeScope.registeredTopics(), CoreMatchers.is(mkSet( | ||
| assertThat(serdeScope.registeredTopics(), is(mkSet( | ||
| // expected pseudo-topics | ||
| "KTABLE-FK-JOIN-SUBSCRIPTION-REGISTRATION-0000000006-topic-fk--key", | ||
| "KTABLE-FK-JOIN-SUBSCRIPTION-REGISTRATION-0000000006-topic-pk--key", | ||
| "KTABLE-FK-JOIN-SUBSCRIPTION-REGISTRATION-0000000006-topic-vh--value", | ||
| applicationId + "-KTABLE-FK-JOIN-SUBSCRIPTION-REGISTRATION-0000000006-topic-fk--key", | ||
| applicationId + "-KTABLE-FK-JOIN-SUBSCRIPTION-REGISTRATION-0000000006-topic-pk--key", | ||
| applicationId + "-KTABLE-FK-JOIN-SUBSCRIPTION-REGISTRATION-0000000006-topic-vh--value", | ||
|
Comment on lines
222
to
+225
Contributor
Author
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 verifies the fix: the pseudo topics should also be prefixed. I should have noticed before that they weren't. |
||
| // internal topics | ||
| "ktable-ktable-joinOnForeignKey-KTABLE-FK-JOIN-SUBSCRIPTION-REGISTRATION-0000000006-topic--key", | ||
| "ktable-ktable-joinOnForeignKey-KTABLE-FK-JOIN-SUBSCRIPTION-RESPONSE-0000000014-topic--key", | ||
| "ktable-ktable-joinOnForeignKey-KTABLE-FK-JOIN-SUBSCRIPTION-RESPONSE-0000000014-topic--value", | ||
| "ktable-ktable-joinOnForeignKey-left_table-STATE-STORE-0000000000-changelog--key", | ||
| "ktable-ktable-joinOnForeignKey-left_table-STATE-STORE-0000000000-changelog--value", | ||
| "ktable-ktable-joinOnForeignKey-right_table-STATE-STORE-0000000003-changelog--key", | ||
| "ktable-ktable-joinOnForeignKey-right_table-STATE-STORE-0000000003-changelog--value", | ||
| applicationId + "-KTABLE-FK-JOIN-SUBSCRIPTION-REGISTRATION-0000000006-topic--key", | ||
| applicationId + "-KTABLE-FK-JOIN-SUBSCRIPTION-RESPONSE-0000000014-topic--key", | ||
| applicationId + "-KTABLE-FK-JOIN-SUBSCRIPTION-RESPONSE-0000000014-topic--value", | ||
| applicationId + "-left_table-STATE-STORE-0000000000-changelog--key", | ||
| applicationId + "-left_table-STATE-STORE-0000000000-changelog--value", | ||
| applicationId + "-right_table-STATE-STORE-0000000003-changelog--key", | ||
| applicationId + "-right_table-STATE-STORE-0000000003-changelog--value", | ||
| // output topics | ||
| "output-topic--key", | ||
| "output-topic--value" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hopefully, this explains what's going on here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understood