Skip to content

KAFKA-20134: Implement TimestampedWindowStoreWithHeaders (N/N)#21581

Merged
frankvicky merged 10 commits intoapache:trunkfrom
frankvicky:KAFKA-20134-6
Mar 6, 2026
Merged

KAFKA-20134: Implement TimestampedWindowStoreWithHeaders (N/N)#21581
frankvicky merged 10 commits intoapache:trunkfrom
frankvicky:KAFKA-20134-6

Conversation

@frankvicky
Copy link
Copy Markdown
Contributor

@frankvicky frankvicky commented Feb 25, 2026

This PR implements the upgrade integration tests for
TimestampedWindowStateStoreWithHeaders introduced in KIP-1271.

The class should be reviewd: HeadersStoreUpgradeIntegrationTest

This should not be merged before #21497

Reviewers: Matthias J. Sax matthias@confluent.io, Alieh Saeedi
asaeedi@confluent.io

@github-actions github-actions Bot added triage PRs from the community streams labels Feb 25, 2026
@frankvicky frankvicky force-pushed the KAFKA-20134-6 branch 2 times, most recently from e5ecbba to 62aefce Compare February 26, 2026 09:29
Copy link
Copy Markdown
Contributor

@aliehsaeedii aliehsaeedii left a comment

Choose a reason for hiding this comment

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

Thanks @frankvicky.

public void put(final K key, final ValueTimestampHeaders<V> value, final long windowStartTimestamp) {
Objects.requireNonNull(key, "key cannot be null");
final Headers headers = value.headers() == null ? new RecordHeaders() : value.headers();
final Headers headers = value == null || value.headers() == null ? new RecordHeaders() : value.headers();
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.

Should we do that in other metered classes as well? Did you @frankvicky notice any issue when testing here?

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.

I introduced this change when I was writing migration test. iterator will fail due to value is null.

* Iterator adapter for WindowStoreIterator that converts timestamp-only values
* to timestamp-with-headers format by adding empty headers.
*/
private static class TimestampedToHeadersWindowStoreIteratorAdapter implements WindowStoreIterator<byte[]> {
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.

naming: I 'm not sure but maybe TimestampedWindowToHeadersWindowStoreIteratorAdapter?
this name is longer but more descriptive.

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.

If you compare this iterator with WindowToTimestampedWindowIteratorAdapter, should it extend TimestampedToHeadersIteratorAdapter?

@Override
public KeyValueIterator<Windowed<Bytes>, byte[]> backwardFetchAll(final long timeFrom, final long timeTo) {
return new TimestampedToHeadersIteratorAdapter<>(store.backwardFetchAll(timeFrom, timeTo));
}
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.

What about public KeyValueIterator<Windowed<Bytes>, byte[]> backwardFetchAll(final Instant timeFrom, final Instant timeTo) throws IllegalArgumentException {...}?

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.

Please verify that all relevant methods have Instant-based overloads, consistent with WindowToTimestampedWindowByteStoreAdapter.java.

public KeyValueIterator<Windowed<Bytes>, byte[]> fetchAll(final long timeFrom, final long timeTo) {
return new TimestampedToHeadersIteratorAdapter<>(store.fetchAll(timeFrom, timeTo));
}

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.

What about public KeyValueIterator<Windowed<Bytes>, byte[]> fetchAll(final Instant timeFrom, final Instant timeTo) throws IllegalArgumentException {...}?

}

/**
* Tests migration from TimestampedWindowStore (v2) to TimestampedWindowStoreWithHeaders (v3).
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.

What are v2 and v3?

* This is a true migration where both supplier and builder are upgraded.
*/
private void shouldMigrateTimestampedWindowStoreToTimestampedWindowStoreWithHeaders(final boolean persistentStore) throws Exception {
// Phase 1: Run with old v2 store
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.

ditto

final StreamsBuilder newBuilder = new StreamsBuilder();
newBuilder.addStateStore(
Stores.timestampedWindowStoreWithHeadersBuilder(
Stores.persistentTimestampedWindowStore(WINDOW_STORE_NAME, Duration.ofMillis(RETENTION_MS), Duration.ofMillis(WINDOW_SIZE_MS), false), // v2 supplier!
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.

v2?

kafkaStreams.close();
kafkaStreams = null;

// Restart with v3 builder BUT v2 supplier (proxy/adapter mode)
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.

I'm not sure if we ever had this terminology (v2, v3`).


@Test
public void shouldMigrateTimestampedWindowStoreToTimestampedWindowStoreWithHeaders() throws Exception {
// Phase 1: Run with TimestampedWindowStore (v2)
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.

ditto

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.

Didn't we check this upgrade in HeadersUpgradeIntegrationTest?

store.put(record.key(), ValueTimestampHeaders.make(record.value(), record.timestamp(), record.headers()));
}
}

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.

Should we add downgrade tests as well?

@github-actions github-actions Bot removed the triage PRs from the community label Feb 27, 2026
}

@Test
public void shouldFailDowngradeFromTimestampedWindowStoreWithHeadersToTimestampedWindowStore() throws Exception {
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.

Did you look into StoreUpgradIntegrationTest#shouldFailDowngradeFromTimestampedToRegularKeyValueStore

Might be simpler to follow this pattern?

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.

IIUC, we could not follow the same pattern since the exception has been swallowed.

} catch (final Exception ex) {
// ignore
}

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.

😲 -- But it this correct to just swallow the exception???

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.

Did some digging and it's like this since 2016, when this code was added... (#2166 -> add Segments.java -> was later refactored and the code was move into AbstractSegments.java)

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.

Should we raise a ticket for this?

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.

I suppose we should throw the ProcessorStateException here. In this way, we could align the behavior between kv store and window store and also simpify the downgrade test case.

@github-actions github-actions Bot added the tests Test fixes (including flaky tests) label Mar 3, 2026
}
// Otherwise (null or corrupted data), the downgrade failed as expected
} catch (final Exception e) {
// Exception during read is expected - indicates format mismatch
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.

We should verify the expected error message, to avoid that we catch some other exception which is unrelated to the test.

final ValueAndTimestamp<String> result = store.fetch("key1", windowStart);

// If we can read the data correctly, the test should fail
if (result != null && result.value().equals("value1") && result.timestamp() == (baseTime + 100)) {
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.

It seems we have two code path that make the test pass? We reach this point, but "Fail" to verify the data, vs we crash with an Exception. Wondering why we need both? Would we not expect the test to either always crash with and exception (and passed), or always not crash but reach this code and just fail to verify?

If not, what is the race condition which influence which code path this test takes?

@mjsax
Copy link
Copy Markdown
Member

mjsax commented Mar 5, 2026

There is failing test -- was the change to AbstractSegment not correct after all?

@mjsax
Copy link
Copy Markdown
Member

mjsax commented Mar 5, 2026

I looked into this, and think the tests are actually incorrect -- but it was never detected because we swallow the exception... Did PR to change this: #21636

@mjsax
Copy link
Copy Markdown
Member

mjsax commented Mar 6, 2026

We can rebase this PR -- feel free to merge if CI passes after rebasing.

@mjsax mjsax added the kip Requires or implements a KIP label Mar 6, 2026
@frankvicky frankvicky merged commit 5fc5d75 into apache:trunk Mar 6, 2026
23 checks passed
gabriellefu pushed a commit to gabriellefu/kafka that referenced this pull request Mar 30, 2026
…e#21581)

This PR implements the upgrade integration tests for
`TimestampedWindowStateStoreWithHeaders` introduced in KIP-1271.

The class should be reviewd:  `HeadersStoreUpgradeIntegrationTest`

This should not be merged before apache#21497

Reviewers: Matthias J. Sax <matthias@confluent.io>, Alieh Saeedi
<asaeedi@confluent.io>
Shekharrajak pushed a commit to Shekharrajak/kafka that referenced this pull request Mar 31, 2026
…e#21581)

This PR implements the upgrade integration tests for
`TimestampedWindowStateStoreWithHeaders` introduced in KIP-1271.

The class should be reviewd:  `HeadersStoreUpgradeIntegrationTest`

This should not be merged before apache#21497

Reviewers: Matthias J. Sax <matthias@confluent.io>, Alieh Saeedi
<asaeedi@confluent.io>
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 tests Test fixes (including flaky tests)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants