-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-8254: Pass Changelog as Topic in Suppress Serdes #6602
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 7 commits
f8ad73d
e1642ba
19127de
4c2a457
a9aa4fa
c26718a
a51c4fc
7414ed0
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 |
|---|---|---|
|
|
@@ -384,18 +384,26 @@ public KStream<K, V> peek(final ForeachAction<? super K, ? super V> action) { | |
|
|
||
| @Override | ||
| public KStream<K, V> through(final String topic) { | ||
| return through(topic, Produced.with(null, null, null)); | ||
| return through(topic, Produced.with(keySerde, valSerde, null)); | ||
|
vvcephei marked this conversation as resolved.
|
||
| } | ||
|
|
||
| @Override | ||
| public KStream<K, V> through(final String topic, final Produced<K, V> produced) { | ||
| Objects.requireNonNull(topic, "topic can't be null"); | ||
| Objects.requireNonNull(produced, "Produced can't be null"); | ||
| final ProducedInternal<K, V> producedInternal = new ProducedInternal<>(produced); | ||
| if (producedInternal.keySerde() == null) { | ||
| producedInternal.withKeySerde(keySerde); | ||
| } | ||
| if (producedInternal.valueSerde() == null) { | ||
| producedInternal.withValueSerde(valSerde); | ||
| } | ||
| to(topic, producedInternal); | ||
| return builder.stream( | ||
| Collections.singleton(topic), | ||
| new ConsumedInternal<>( | ||
| producedInternal.keySerde() != null ? producedInternal.keySerde() : keySerde, | ||
| producedInternal.valueSerde() != null ? producedInternal.valueSerde() : valSerde, | ||
| producedInternal.keySerde(), | ||
| producedInternal.valueSerde(), | ||
|
Member
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. Why this rewrite? (Just curious.)
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. it's just no longer necessary, since we're already setting the key/value serdes if they were null in
Member
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. Sure. The question was about both changes in combination. What is it better to split out compare to just do a one liner? |
||
| new FailOnInvalidTimestamp(), | ||
| null | ||
| ) | ||
|
|
@@ -404,26 +412,40 @@ public KStream<K, V> through(final String topic, final Produced<K, V> produced) | |
|
|
||
| @Override | ||
| public void to(final String topic) { | ||
| to(topic, Produced.with(null, null, null)); | ||
| to(topic, Produced.with(keySerde, valSerde, null)); | ||
| } | ||
|
|
||
| @Override | ||
| public void to(final String topic, final Produced<K, V> produced) { | ||
| Objects.requireNonNull(topic, "topic can't be null"); | ||
| Objects.requireNonNull(produced, "Produced can't be null"); | ||
| to(new StaticTopicNameExtractor<>(topic), new ProducedInternal<>(produced)); | ||
| final ProducedInternal<K, V> producedInternal = new ProducedInternal<>(produced); | ||
| if (producedInternal.keySerde() == null) { | ||
| producedInternal.withKeySerde(keySerde); | ||
| } | ||
| if (producedInternal.valueSerde() == null) { | ||
| producedInternal.withValueSerde(valSerde); | ||
| } | ||
| to(new StaticTopicNameExtractor<>(topic), producedInternal); | ||
| } | ||
|
|
||
| @Override | ||
| public void to(final TopicNameExtractor<K, V> topicExtractor) { | ||
| to(topicExtractor, Produced.with(null, null, null)); | ||
| to(topicExtractor, Produced.with(keySerde, valSerde, null)); | ||
| } | ||
|
|
||
| @Override | ||
| public void to(final TopicNameExtractor<K, V> topicExtractor, final Produced<K, V> produced) { | ||
| Objects.requireNonNull(topicExtractor, "topic extractor can't be null"); | ||
| Objects.requireNonNull(produced, "Produced can't be null"); | ||
| to(topicExtractor, new ProducedInternal<>(produced)); | ||
| final ProducedInternal<K, V> producedInternal = new ProducedInternal<>(produced); | ||
| if (producedInternal.keySerde() == null) { | ||
| producedInternal.withKeySerde(keySerde); | ||
| } | ||
| if (producedInternal.valueSerde() == null) { | ||
| producedInternal.withValueSerde(valSerde); | ||
| } | ||
| to(topicExtractor, producedInternal); | ||
| } | ||
|
|
||
| private void to(final TopicNameExtractor<K, V> topicExtractor, final ProducedInternal<K, V> produced) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,8 +19,6 @@ | |
| import org.apache.kafka.clients.consumer.ConsumerRecord; | ||
| import org.apache.kafka.common.metrics.Sensor; | ||
| import org.apache.kafka.common.serialization.Serde; | ||
| import org.apache.kafka.common.utils.Bytes; | ||
| import org.apache.kafka.streams.KeyValue; | ||
| import org.apache.kafka.streams.errors.StreamsException; | ||
| import org.apache.kafka.streams.kstream.internals.Change; | ||
| import org.apache.kafka.streams.kstream.internals.FullChangeSerde; | ||
|
|
@@ -30,7 +28,6 @@ | |
| import org.apache.kafka.streams.processor.ProcessorContext; | ||
| import org.apache.kafka.streams.processor.internals.InternalProcessorContext; | ||
| import org.apache.kafka.streams.processor.internals.ProcessorRecordContext; | ||
| import org.apache.kafka.streams.state.internals.ContextualRecord; | ||
| import org.apache.kafka.streams.state.internals.TimeOrderedKeyValueBuffer; | ||
|
|
||
| import static java.util.Objects.requireNonNull; | ||
|
|
@@ -44,22 +41,14 @@ public class KTableSuppressProcessor<K, V> implements Processor<K, Change<V>> { | |
| private final boolean safeToDropTombstones; | ||
| private final String storeName; | ||
|
|
||
| private TimeOrderedKeyValueBuffer buffer; | ||
| private TimeOrderedKeyValueBuffer<K, Change<V>> buffer; | ||
| private InternalProcessorContext internalProcessorContext; | ||
| private Sensor suppressionEmitSensor; | ||
| private Serde<K> keySerde; | ||
| private FullChangeSerde<V> valueSerde; | ||
|
|
||
| private long observedStreamTime = ConsumerRecord.NO_TIMESTAMP; | ||
|
|
||
| public KTableSuppressProcessor(final SuppressedInternal<K> suppress, | ||
| final String storeName, | ||
| final Serde<K> keySerde, | ||
| final FullChangeSerde<V> valueSerde) { | ||
| public KTableSuppressProcessor(final SuppressedInternal<K> suppress, final String storeName) { | ||
| this.storeName = storeName; | ||
| requireNonNull(suppress); | ||
| this.keySerde = keySerde; | ||
| this.valueSerde = valueSerde; | ||
| maxRecords = suppress.bufferConfig().maxRecords(); | ||
| maxBytes = suppress.bufferConfig().maxBytes(); | ||
| suppressDurationMillis = suppress.timeToWaitForMoreEvents().toMillis(); | ||
|
|
@@ -74,9 +63,8 @@ public void init(final ProcessorContext context) { | |
| internalProcessorContext = (InternalProcessorContext) context; | ||
| suppressionEmitSensor = Sensors.suppressionEmitSensor(internalProcessorContext); | ||
|
|
||
| keySerde = keySerde == null ? (Serde<K>) context.keySerde() : keySerde; | ||
| valueSerde = valueSerde == null ? FullChangeSerde.castOrWrap(context.valueSerde()) : valueSerde; | ||
| buffer = requireNonNull((TimeOrderedKeyValueBuffer) context.getStateStore(storeName)); | ||
| buffer = requireNonNull((TimeOrderedKeyValueBuffer<K, Change<V>>) context.getStateStore(storeName)); | ||
| buffer.setSerdesIfNull((Serde<K>) context.keySerde(), FullChangeSerde.castOrWrap((Serde<V>) context.valueSerde())); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -88,12 +76,7 @@ public void process(final K key, final Change<V> value) { | |
|
|
||
| private void buffer(final K key, final Change<V> value) { | ||
| final long bufferTime = bufferTimeDefinition.time(internalProcessorContext, key); | ||
| final ProcessorRecordContext recordContext = internalProcessorContext.recordContext(); | ||
|
|
||
| final Bytes serializedKey = Bytes.wrap(keySerde.serializer().serialize(null, key)); | ||
| final byte[] serializedValue = valueSerde.serializer().serialize(null, value); | ||
|
|
||
| buffer.put(bufferTime, serializedKey, new ContextualRecord(serializedValue, recordContext)); | ||
| buffer.put(bufferTime, key, value, internalProcessorContext.recordContext()); | ||
| } | ||
|
|
||
| private void enforceConstraints() { | ||
|
|
@@ -114,6 +97,11 @@ private void enforceConstraints() { | |
| buffer.numRecords(), maxRecords, | ||
| buffer.bufferSize(), maxBytes | ||
| )); | ||
| default: | ||
|
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 is to fix a warning about the switch being incomplete. |
||
| throw new UnsupportedOperationException( | ||
| "The bufferFullStrategy [" + bufferFullStrategy + | ||
| "] is not implemented. This is a bug in Kafka Streams." | ||
| ); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -122,14 +110,12 @@ private boolean overCapacity() { | |
| return buffer.numRecords() > maxRecords || buffer.bufferSize() > maxBytes; | ||
| } | ||
|
|
||
| private void emit(final KeyValue<Bytes, ContextualRecord> toEmit) { | ||
| final Change<V> value = valueSerde.deserializer().deserialize(null, toEmit.value.value()); | ||
| if (shouldForward(value)) { | ||
| private void emit(final TimeOrderedKeyValueBuffer.Eviction<K, Change<V>> toEmit) { | ||
| if (shouldForward(toEmit.value())) { | ||
| final ProcessorRecordContext prevRecordContext = internalProcessorContext.recordContext(); | ||
| internalProcessorContext.setRecordContext(toEmit.value.recordContext()); | ||
| internalProcessorContext.setRecordContext(toEmit.recordContext()); | ||
| try { | ||
| final K key = keySerde.deserializer().deserialize(null, toEmit.key.get()); | ||
| internalProcessorContext.forward(key, value); | ||
| internalProcessorContext.forward(toEmit.key(), toEmit.value()); | ||
| suppressionEmitSensor.record(); | ||
| } finally { | ||
| internalProcessorContext.setRecordContext(prevRecordContext); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,11 +29,11 @@ | |
|
|
||
| public class ProcessorRecordContext implements RecordContext { | ||
|
|
||
| long timestamp; | ||
| final long offset; | ||
| final String topic; | ||
| final int partition; | ||
| final Headers headers; | ||
| private long timestamp; | ||
| private final long offset; | ||
| private final String topic; | ||
| private final int partition; | ||
| private final Headers headers; | ||
|
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. some on-the-side cleanup of |
||
|
|
||
| public ProcessorRecordContext(final long timestamp, | ||
| final long offset, | ||
|
|
@@ -48,13 +48,6 @@ public ProcessorRecordContext(final long timestamp, | |
| this.headers = headers; | ||
| } | ||
|
|
||
| public ProcessorRecordContext(final long timestamp, | ||
| final long offset, | ||
| final int partition, | ||
| final String topic) { | ||
| this(timestamp, offset, partition, topic, null); | ||
| } | ||
|
|
||
| public void setTimestamp(final long timestamp) { | ||
| this.timestamp = timestamp; | ||
| } | ||
|
|
@@ -225,9 +218,13 @@ public boolean equals(final Object o) { | |
| Objects.equals(headers, that.headers); | ||
| } | ||
|
|
||
| /** | ||
| * Equality is implemented in support of tests, *not* for use in Hash collections, since this class is mutable. | ||
| */ | ||
| @Deprecated | ||
|
Member
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. No need to deprecate internal method. If we want to remove it, we should just do it.
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. We can't remove I implemented it because implementing
Member
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. What I meant by "remove" was, to remove the overwrite :) Thanks to clarify why you want to overwrite and throw. But this does not mean that the method is deprecated... Using the annotation implies that we want to remove this overwrite in the future was is miss leading. I would prefer to remove the annotation. \cc @guozhangwang @bbejeck
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. I think that throwing the exception in its override implementation is sufficient for future bug-proof, as regarding to documentation I feel neutral either way.
Member
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. We should still add JavaDocs and explain that we throw and why. But deprecation != documentation IMHO. |
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(timestamp, offset, topic, partition, headers); | ||
| throw new UnsupportedOperationException("ProcessorRecordContext is unsafe for use in Hash collections"); | ||
| } | ||
|
|
||
| @Override | ||
|
|
||
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.
Forcing all callers to use
castOrWrap, which propagatesnull(aka, no serde is defined). See the usage inKTableImpl.