Skip to content

KIP-557: Add Emit On Change Support#8254

Merged
vvcephei merged 38 commits into
apache:trunkfrom
ConcurrencyPractitioner:EMIT-ON-CHANGE
May 12, 2020
Merged

KIP-557: Add Emit On Change Support#8254
vvcephei merged 38 commits into
apache:trunkfrom
ConcurrencyPractitioner:EMIT-ON-CHANGE

Conversation

@ConcurrencyPractitioner

Copy link
Copy Markdown
Contributor

This is the initial draft PR for adding emit on change. For reference of the design document, please see here.

https://cwiki.apache.org/confluence/display/KAFKA/KIP-557%3A+Add+emit+on+change+support+for+Kafka+Streams

Committer Checklist (excluded from commit message)

  • Verify design and implementation
  • Verify test coverage and CI build status
  • Verify documentation (including upgrade notes)

@ConcurrencyPractitioner

Copy link
Copy Markdown
Contributor Author

@vvcephei @cadonna @mjsax Would you recommend this approach for implementing binary equality? I just want some feedback on this, since it seems there are multiple ways to resolve this issue,

@vvcephei

vvcephei commented Mar 9, 2020

Copy link
Copy Markdown
Contributor

test this please

@vvcephei

vvcephei commented Mar 9, 2020

Copy link
Copy Markdown
Contributor

Thanks for this PR @ConcurrencyPractitioner ! I'll take a look at this asap. Just wanted to acknowledge that it's on my plate.

@vvcephei

Copy link
Copy Markdown
Contributor

Just FYI, it looks like the tests couldn't build.

@ConcurrencyPractitioner

Copy link
Copy Markdown
Contributor Author

@vvcephei Acknowledged. My intention wasn't really to get a working model of the code. I was intending this to be a draft of what the code might look like. I just want approval on this approach first before I dive in.

@ConcurrencyPractitioner

Copy link
Copy Markdown
Contributor Author

@vvcephei It looks like casting the store to MeteredTimestampedKeyValueStore doesn't work, as in some cases, the TimestampedKeyValueStore is in fact TimestampedKeyValueStoreReadWriteDecorator. So we will have to abandon this approach. In this case, we will probably need to consider passing in null serdes instead.

@ConcurrencyPractitioner

Copy link
Copy Markdown
Contributor Author

@vvcephei @mjsax @guozhangwang Pinging for initial review.

@vvcephei vvcephei left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ConcurrencyPractitioner , thanks for the PR! I think this looks really close. I'm recommending a few tweaks.

I think you can convert this WIP PR to an actual PR by addressing my comments and adding some additional test cases.

Thanks again!
-John

ConcurrencyPractitioner and others added 6 commits April 2, 2020 08:59
…ls/KTableSourceTest.java

Co-Authored-By: John Roesler <vvcephei@users.noreply.github.com>
…ls/KTableSource.java

Co-Authored-By: John Roesler <vvcephei@users.noreply.github.com>
…/MeteredTimestampedKeyValueStore.java

Co-Authored-By: John Roesler <vvcephei@users.noreply.github.com>
@ConcurrencyPractitioner ConcurrencyPractitioner changed the title KIP-557: Add Emit On Change Support [WIP] KIP-557: Add Emit On Change Support Apr 2, 2020

@vvcephei vvcephei left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. I just noticed one small thing I missed last time.

@mjsax or @cadonna , do you have a chance to take a look at this? I believe this is what we agreed on for the KIP, but want to double-check with you.

Comment on lines +62 to +64
final byte[] serializedValue = wrapped().get(keyBytes(key));
return new RawAndDeserializedValue<V>(serializedValue,
maybeMeasureLatency(() -> outerValue(serializedValue), time, getSensor));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, missed this the last time though. We should also perform the wrapped().get inside the lambda, so that the time to perform the get is included in the latency.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, I will make the change.

@vvcephei

vvcephei commented Apr 3, 2020

Copy link
Copy Markdown
Contributor

test this please

@vvcephei

vvcephei commented Apr 3, 2020

Copy link
Copy Markdown
Contributor

test this please

1 similar comment
@vvcephei

vvcephei commented Apr 3, 2020

Copy link
Copy Markdown
Contributor

test this please

@vvcephei

Copy link
Copy Markdown
Contributor

test this please

2 similar comments
@vvcephei

Copy link
Copy Markdown
Contributor

test this please

@vvcephei

Copy link
Copy Markdown
Contributor

test this please

@vvcephei

Copy link
Copy Markdown
Contributor

retest this please

@ConcurrencyPractitioner

Copy link
Copy Markdown
Contributor Author

@vvcephei Looks like I forgot to push. You can retrigger the tests after this one.

@ConcurrencyPractitioner

Copy link
Copy Markdown
Contributor Author

@vvcephei Alright, all tests passed (looks like there were some flakies) and all comments addressed.

@ConcurrencyPractitioner

Copy link
Copy Markdown
Contributor Author

@vvcephei Just pinging. I looked into the flakies, and it turns out they were different test failures for each build. So its the equivalent of an all green.

@vvcephei

Copy link
Copy Markdown
Contributor

Retest this please

@vvcephei

Copy link
Copy Markdown
Contributor

Sorry, for the delay @ConcurrencyPractitioner . I have been insanely busy. I really appreciate your patience.

I'm giving it a final pass now.

@vvcephei vvcephei left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, @ConcurrencyPractitioner ,

Again, I'm really sorry there have been so many delays. I have just a few very quick remarks. If you don't have time to address them (or don't care to wait on me anymore ;) ), I can also do it while merging.

Thanks again!

Comment on lines +211 to +213
assertFalse(metered.putIfDifferentValues(key,
newValueAndTimestamp,
encodedOldValue));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry to say, one more thing slipped by me before. We should verify(inner) at the end of both of these tests. It should actually fail for shouldNotPutIfSameValuesAndGreaterTimestamp because we should not call inner.put in this case. To fix that, we would just delete L202, where we set up the mock for inner.put. Then, the mock would be initialized to expect no calls, and the verification would fail if we did call it.

final byte[] encodedOldValue = stringSerde.serializer().serialize("TOPIC", valueAndTimestamp);

final ValueAndTimestamp<String> outOfOrderValueAndTimestamp = ValueAndTimestamp.make("value", 95L);
assertTrue(metered.putIfDifferentValues(key, outOfOrderValueAndTimestamp, encodedOldValue));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned in the earlier comment, we're missing a verify(inner) call at the end of this test.

* if the timestamp of right is less than left (indicating out of order record)
* false otherwise
*/
public static boolean maskTimestampAndCompareValues(final byte[] left, final byte[] right) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, upon reading that last test, I realized that I previously overlooked when you added the timestamp comparison to this method. We should change the method name for maintainability. It no longer just "masks the timestamp and compares the values". Can we instead call it "compareValuesAndCheckForIncreasingTimestamp" or something? I can almost guarantee that one or more people will be badly misled by the current method name.

@ConcurrencyPractitioner

Copy link
Copy Markdown
Contributor Author

@vvcephei Alright, cool. Got those last comments addressed.

@vvcephei

Copy link
Copy Markdown
Contributor

Unrelated test failures:

kafka.api.SslConsumerTest.testCoordinatorFailover

org.apache.kafka.streams.integration.EosBetaUpgradeIntegrationTest.shouldUpgradeFromEosAlphaToEosBeta[true]

kafka.api.ConsumerBounceTest.testClose

org.apache.kafka.connect.mirror.MirrorConnectorsIntegrationTest.testReplication

@vvcephei

Copy link
Copy Markdown
Contributor

Hey @ConcurrencyPractitioner , I just pushed a very minor test fix. I'm verifying it locally before merging.

@vvcephei
vvcephei merged commit f54cece into apache:trunk May 12, 2020
@vvcephei

Copy link
Copy Markdown
Contributor

Thanks for the awesome contribution, @ConcurrencyPractitioner !

Kvicii added a commit to Kvicii/kafka that referenced this pull request May 14, 2020
* 'trunk' of github.com:apache/kafka:
  MINOR: add option to rebuild source for system tests (apache#6656)
  KAFKA-9850 Move KStream#repartition operator validation during Topolo… (apache#8550)
  MINOR: Add a duplicate() method to Message classes (apache#8556)
  KAFKA-9966: add internal assignment listener to stabilize eos-beta upgrade test (apache#8648)
  MINOR: Replace null with an actual value for timestamp field in InsertField SMT unit tests (apache#8649)
  MINOR: Fix ProcessorContext JavaDocs and stream-time computation (apache#8603)
  MINOR: improve tests for TopologyTestDriver (apache#8631)
  KAFKA-9821: consolidate Streams rebalance triggering mechanisms (apache#8596)
  KAFKA-9669; Loosen validation of inner offsets for older message formats (apache#8647)
  KAFKA-8770: KIP-557: Drop idempotent KTable source updates (apache#8254)
  MINOR: Remove allow concurrent test (apache#8641)
if (ValueAndTimestampSerializer.compareValuesAndCheckForIncreasingTimestamp(oldSerializedValue, newSerializedValue)) {
return false;
} else {
wrapped().put(keyBytes(key), newSerializedValue);

@rodesai rodesai May 14, 2020

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ConcurrencyPractitioner @vvcephei I'm trying to understand this to debug some broken tests in ksql. Couple questions:

When the timestamp of the newer value is lower (ignoring the value), why do we want to put the new value into the store? Surely the store should have the value with the newer timestamp? Otherwise we could wind up with a corrupt store.

Don't we still want to put the value in the store (even if we don't forward it on to the next context) if the values are the same but the timestamp is newer? Otherwise if we get an out-of-order update with a different value, but a timestamp in between the rows with the same value, we'd incorrectly put that value into the store, e.g. the following updates:

TS: 1, K: X, V: A
TS: 3, K: X, V: A
TS: 2, K: X, V: B

would result in the table containing K: X, V: B, which is wrong.

@cadonna cadonna May 14, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why when the timestamp of the newer value is lower, do we want to put the new value into the store? Surely the store should have the value with the newer timestamp? Otherwise we could wind up with a corrupt store.

This behavior was there also before this PR. If a out-of-order record is encountered, a log message was written, but the record was nevertheless put into the state store (cf.

store.put(key, ValueAndTimestamp.make(value, context().timestamp()));
). The only thing that changed is that if the serialized value of the new record is equal to the serialized value of the old value and the timestamp of the new record is equal or newer, we drop the record because it is a idempotent update.
Could you elaborate on why a store should get corrupted because of this?

would result in the table containing K: X, V: B, which is wrong.

As said above, this behavior should not have been changed.

@cadonna cadonna May 14, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we still want to put the value in the store (even if we don't forward it on to the next context) if the values are the same but the timestamp is newer?

If we just put the value in the store but did not forward it, then the store would actually be corrupted, because the local state would not be consistent with downstream anymore.

Not putting a record with the same value but a newer timestamp in the store and not forwarding it was the main point of this KIP.

jwijgerd pushed a commit to buxapp/kafka that referenced this pull request May 14, 2020
Drops idempotent updates from KTable source operators.
Specifically, drop updates in which the value is unchanged,
and the timestamp is the same or larger.

Implements: KIP-557
Reviewers: Bruno Cadonna <bruno@confluent.io>, John Roesler <vvcephei@apache.org>
guozhangwang added a commit that referenced this pull request Jun 10, 2020
The latest commit #8254 on this test deleted all topics after each test, but the topic was actually shared among tests before. And after that we are relying on the less-reliable auto-topic generation to get the topic which makes the test flaky.

I'm now using different topics for different tests, also setting the app.id for tests differently.

Reviewers: Boyang Chen <boyang@confluent.io>, A. Sophie Blee-Goldman <sophie@confluent.io>, Matthias J. Sax <matthias@confluent.io>
guozhangwang added a commit that referenced this pull request Jun 10, 2020
The latest commit #8254 on this test deleted all topics after each test, but the topic was actually shared among tests before. And after that we are relying on the less-reliable auto-topic generation to get the topic which makes the test flaky.

I'm now using different topics for different tests, also setting the app.id for tests differently.

Reviewers: Boyang Chen <boyang@confluent.io>, A. Sophie Blee-Goldman <sophie@confluent.io>, Matthias J. Sax <matthias@confluent.io>
@mjsax mjsax added the kip Requires or implements a KIP label Jun 12, 2020
jwijgerd pushed a commit to buxapp/kafka that referenced this pull request Aug 17, 2020
Drops idempotent updates from KTable source operators.
Specifically, drop updates in which the value is unchanged,
and the timestamp is the same or larger.

Implements: KIP-557
Reviewers: Bruno Cadonna <bruno@confluent.io>, John Roesler <vvcephei@apache.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kip Requires or implements a KIP streams

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants