KAFKA-20787: Implement KIP-1340: Expose Both Mapped Key and Stream Key in Streams-GlobalKTable Joins#22807
KAFKA-20787: Implement KIP-1340: Expose Both Mapped Key and Stream Key in Streams-GlobalKTable Joins#22807lucliu1108 wants to merge 6 commits into
Conversation
mjsax
left a comment
There was a problem hiding this comment.
Made an initial pass -- did not look into test code yet.
| private final ValueJoinerWithKey<? super K1, ? super V1, ? super V2, ? extends VOut> joiner; | ||
| private final KeyValueMapper<? super K1, ? super V1, ? extends K2> mapper; | ||
| private final KTableValueGetterSupplier<TableKey, TableValue> valueGetterSupplier; | ||
| private final ValueJoinerWithMappedAndStreamKey<? super TableKey, ? super StreamKey, ? super StreamValue, ? super TableValue, ? extends VOut> joiner; |
There was a problem hiding this comment.
I am just wondering about the order or generic types? As it's a "stream-globalTable" join, it's a little bit odd that we start with TableKey?
I think, we have two options?
- first steam types, second table types:
<StreamKey, StreamValue, TableKey, TableValue, VOut> - first both key, seconds both values:
<StreamKey, TableKey, StreamValue, TableValue, VOut>
Thoughts? Sorry, that we missed this during KIP discussion, but easy to still change and update the KIP.
| import static org.apache.kafka.streams.processor.internals.metrics.TaskMetrics.droppedRecordsSensor; | ||
| import static org.apache.kafka.streams.state.ValueTimestampHeaders.getValueOrNull; | ||
|
|
||
| class KStreamGlobalKTableJoinProcessor<StreamKey, StreamValue, TableKey, TableValue, VOut> |
There was a problem hiding this comment.
It seems this new class duplicates KStreamKTableJoinProcessor to 99%, with the key difference of calling joiner.apply(mappedKey, record.key(), record.value(), value2) (different class with an additional argument).
I am wondering, if we should instead change KStreamKTableJoinProcessor and let is use ValueJoinerWithMappedAndStreamKey? And for the case when the user passed ValueJoinerWithKey, we wrap it with a ValueJoinerWithMappedAndStreamKey as an "adaptor" which implements apply(...) as
VR apply(final K1 mappedKey, final K2 streamKey, final V1 value1, final V2 value2) {
return valueJoinerWithKey.apply(streamKey, value1, value2);
}
just dropping the additional mappedKey parameter. I believe, we do the same already to bridge between ValueJoiner and ValueJoinerWithKey -- uses can pass either one into a join, and the join Processor uses ValueJoinerWithKey so we have a single impl, and a ValueJoiner would be wrapped similarly, dropping the key argument.
| * the join key via {@code keySelector}, so {@code readOnlyKey} does <b>not</b> necessarily | ||
| * match the key of the {@link GlobalKTable} record being joined. | ||
| * | ||
| * @deprecated Use {@link #join(GlobalKTable, KeyValueMapper, ValueJoinerWithMappedAndStreamKey)} |
There was a problem hiding this comment.
| * @deprecated Use {@link #join(GlobalKTable, KeyValueMapper, ValueJoinerWithMappedAndStreamKey)} | |
| * @deprecated Since 4.4. Use {@link #join(GlobalKTable, KeyValueMapper, ValueJoinerWithMappedAndStreamKey)} |
| * match the key of the {@link GlobalKTable} record being joined. | ||
| * | ||
| * @deprecated Use {@link #join(GlobalKTable, KeyValueMapper, ValueJoinerWithMappedAndStreamKey)} | ||
| * instead, which exposes both the mapped join key and the stream record's key. |
There was a problem hiding this comment.
| * instead, which exposes both the mapped join key and the stream record's key. | |
| * instead. |
I would keep it simple
| * <b>Warning:</b> {@code readOnlyKey} is the {@code KStream} record's key, <b>not</b> | ||
| * the join key produced by {@code keySelector}. Unlike {@link #join(KTable, ValueJoinerWithKey) | ||
| * KStream-KTable} and {@link #join(KStream, ValueJoinerWithKey, JoinWindows) KStream-KStream} | ||
| * joins — where the stream key <em>is</em> the join key — {@link GlobalKTable} joins derive |
There was a problem hiding this comment.
| * joins — where the stream key <em>is</em> the join key — {@link GlobalKTable} joins derive | |
| * joins — where the stream key <em>is</em> the join key — {@link GlobalKTable} joins derive |
There was a problem hiding this comment.
Not sure if an actuallm-dash would render as expected... using html markup seems to be better? Or remove m-dash and replace with something else.
| <GlobalKey, GlobalValue, VOut> KStream<K, VOut> join(final GlobalKTable<GlobalKey, GlobalValue> globalTable, | ||
| final KeyValueMapper<? super K, ? super V, ? extends GlobalKey> keySelector, | ||
| final ValueJoinerWithKey<? super K, ? super V, ? super GlobalValue, ? extends VOut> joiner); | ||
|
|
||
| /** | ||
| * As {@link #join(GlobalKTable, KeyValueMapper, ValueJoiner)}, but the joiner receives both | ||
| * the mapped join key (used to look up the {@link GlobalKTable} value) and the {@link KStream} record key. |
There was a problem hiding this comment.
We should try to use a unify JavaDocs style. Existing style is:
/**
* See {@link #join(GlobalKTable, KeyValueMapper, ValueJoiner)}.
*
* <maybe explain important differences>
*/
We want to avoid duplicating the same description, so we put a full description into the "simples" variant, and point to overloads. So maybe we need to update the JavaDoc of join(GlobalKTable, KeyValueMapper, ValueJoiner) which currently point to the now deprecated method and let it point to the new one?
If you need read access to the {@code KStream} key, use {@link #join(GlobalKTable, KeyValueMapper, ValueJoinerWithKey)}.
-> change to:
If you need read access to the {@code KStream} key or {@link KTable} key (which is the same as the extract join key), use {@link #join(GlobalKTable, KeyValueMapper, ValueJoinerWithMappedAndStreamKey)}.
Similar for leftJoin() case.
| * @param <VR> the type of the joined result value | ||
| */ | ||
| @FunctionalInterface | ||
| public interface ValueJoinerWithMappedAndStreamKey<K1, K2, V1, V2, VR> { |
There was a problem hiding this comment.
Let's use StreamKey, StreamValue etc similar to other interfaces...
Summary
This PR implements KIP-1340. Prior to this kIP,
ValueJoinerWithKey-based stream-globalKTable join pass theKStreamrecord key as thereadOnlyKey, so users had no way to access the join key produced by theirKeyValueMapperinside the joiner without recomputing it.This change adds a new joiner interface and new overrides of existing join/leftJoin methods to allow passing in both the mappedKey and the streamKey.
New Public API
ValueJoinerWithMappedAndStreamKey<K1, K2, V1, V2, VR>A new functional interface with:
4 new KStream join/leftJoin overloads
They accept the new
ValueJoinerWithMappedAndStreamKeyjoiner instead ofValueJoinerWithKey:Deprecations
The 4 existing
ValueJoinerWithKey-based join/leftjoin for stream-globalKtable are marked as@Deprecated.Reviewers: Matthias J. Sax matthias@confluent.io