-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-3902 #1556
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
KAFKA-3902 #1556
Changes from 19 commits
3827d91
e07388b
3563044
721e00d
0a93d16
1919fa5
aa036cb
a607e24
e6beae8
a8f9ef7
7c277cf
73479d3
b039e4e
356b0a6
66aada0
b2f5c06
0fe41c8
5b19a71
c1e4ddf
117c661
525d1b4
1dccdba
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 |
|---|---|---|
|
|
@@ -77,6 +77,9 @@ public void process(K key, Change<V> change) { | |
| V newValue = computeValue(key, change.newValue); | ||
| V oldValue = sendOldValues ? computeValue(key, change.oldValue) : null; | ||
|
|
||
| if (sendOldValues && oldValue == null && newValue == null) | ||
|
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. Just to double-check because the JIRA ticket is a bit difficult to follow: Is this single condition sufficient to cover the following discussion/flow in the JIRA ticket?
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. With /cc @guozhangwang
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. Yes it covers all cases above, which can be summarized as |
||
| return; // unnecessary to forward here. | ||
|
|
||
| context().forward(key, new Change<>(newValue, oldValue)); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,10 @@ | |
| import org.apache.kafka.streams.kstream.Predicate; | ||
| import org.apache.kafka.test.KStreamTestDriver; | ||
| import org.apache.kafka.test.MockProcessorSupplier; | ||
| import org.apache.kafka.test.MockReducer; | ||
| import org.apache.kafka.streams.KeyValue; | ||
| import org.apache.kafka.streams.kstream.ValueMapper; | ||
| import org.apache.kafka.streams.kstream.KeyValueMapper; | ||
| import org.apache.kafka.test.TestUtils; | ||
| import org.junit.After; | ||
| import org.junit.Before; | ||
|
|
@@ -254,24 +258,68 @@ public boolean test(String key, Integer value) { | |
| driver.process(topic1, "C", 1); | ||
|
|
||
| proc1.checkAndClearProcessResult("A:(1<-null)", "B:(1<-null)", "C:(1<-null)"); | ||
| proc2.checkAndClearProcessResult("A:(null<-null)", "B:(null<-null)", "C:(null<-null)"); | ||
| proc2.checkEmptyAndClearProcessResult(); // we got nothing since all inputs are odd or filtered out | ||
|
|
||
| driver.process(topic1, "A", 2); | ||
| driver.process(topic1, "B", 2); | ||
|
|
||
| proc1.checkAndClearProcessResult("A:(2<-1)", "B:(2<-1)"); | ||
| proc2.checkAndClearProcessResult("A:(2<-null)", "B:(2<-null)"); | ||
| proc2.checkAndClearProcessResult("A:(2<-null)", "B:(2<-null)"); // we are informed of 2 making it in for both A and B | ||
|
|
||
| driver.process(topic1, "A", 3); | ||
|
|
||
| proc1.checkAndClearProcessResult("A:(3<-2)"); | ||
| proc2.checkAndClearProcessResult("A:(null<-2)"); | ||
| proc2.checkAndClearProcessResult("A:(null<-2)"); // no change for B but A is deleted | ||
|
|
||
| driver.process(topic1, "A", null); | ||
| driver.process(topic1, "B", null); | ||
|
|
||
| proc1.checkAndClearProcessResult("A:(null<-3)", "B:(null<-2)"); | ||
| proc2.checkAndClearProcessResult("A:(null<-null)", "B:(null<-2)"); | ||
| proc2.checkAndClearProcessResult("B:(null<-2)"); // B is deleted from source Table1 | ||
| } | ||
|
|
||
| @Test | ||
| public void testSkipNullOnMaterialization() throws IOException { | ||
| // do not explicitly set enableSendingOldValues, which is considered a cheat. Let a further downstream stateful operator trigger it instead. | ||
| // This test may well need review/deletion with future materialization changes, should materialization occur more frequently for instance. | ||
| KStreamBuilder builder = new KStreamBuilder(); | ||
|
|
||
| String topic1 = "topic1"; | ||
|
|
||
| KTableImpl<String, String, String> table1 = | ||
| (KTableImpl<String, String, String>) builder.table(stringSerde, stringSerde, topic1); | ||
| KTableImpl<String, String, String> table2 = (KTableImpl<String, String, String>) table1.filter( | ||
| new Predicate<String, String>() { | ||
| @Override | ||
| public boolean test(String key, String value) { | ||
| return value.compareToIgnoreCase("accept") == 0; | ||
|
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. Could use
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. will do. |
||
| } | ||
| }).mapValues( | ||
| new ValueMapper<String, String>() { | ||
|
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. Is this
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. Taking it out, but keeping the groupBy that follows. |
||
| @Override | ||
| public String apply(String value) { | ||
| return value; | ||
| } | ||
| }).groupBy(new KeyValueMapper<String, String, KeyValue<String, String>>() { | ||
| @Override | ||
| public KeyValue<String, String> apply(String first, String second) { | ||
| return (second == null || first.compareTo(second) <= 0) ? new KeyValue<>(first, first) : new KeyValue(second, second); | ||
|
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. Just use
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 can make groupBy as following, not testing meaningfully first, second (we don't care, do we?)
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 said it incorrectly before, and I actually meant that you can use |
||
| } | ||
| }).reduce(MockReducer.STRING_ADDER, MockReducer.STRING_REMOVER, "mock-result"); | ||
|
|
||
| MockProcessorSupplier<String, String> proc1 = new MockProcessorSupplier<>(); | ||
| MockProcessorSupplier<String, String> proc2 = new MockProcessorSupplier<>(); | ||
|
|
||
| builder.addProcessor("proc1", proc1, table1.name); | ||
| builder.addProcessor("proc2", proc2, table2.name); | ||
|
|
||
| driver = new KStreamTestDriver(builder, stateDir, stringSerde, stringSerde); | ||
|
|
||
| driver.process(topic1, "A", "reject"); | ||
| driver.process(topic1, "B", "reject"); | ||
| driver.process(topic1, "C", "reject"); | ||
|
|
||
| proc1.checkAndClearProcessResult("A:(reject<-null)", "B:(reject<-null)", "C:(reject<-null)"); | ||
| proc2.checkEmptyAndClearProcessResult(); // we got nothing since no input matches (though enableSendingOldValues is not set by test on table2 explicitly) | ||
| } | ||
| } | ||
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.
Could we add a check on the original
changevalue thatnewValueandoldValuecannot be both null?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.
You mean something semantically equivalent to
if (change.oldValue == null && change.newValue == null) return;
or rather
if (sendOldValues && change.oldValue == null && change.newValue == null) return;
I'd like to understand whether you're addressing case 2 alone or case 2 and 3. Once I am clear on what you mean, I can make change and test.
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.
Actually never mind, I was originally thinking that after this patch we should
if (change.oldValue == null && change.newValue == null) throw StreamsException, since it should not happen; but I just realized a mapValues operator can still make both values to be null and pass it to the downstream filter.