Skip to content

KAFKA-20413: Make Streams suppress headers-aware#22165

Open
aliehsaeedii wants to merge 6 commits into
apache:trunkfrom
aliehsaeedii:headers_aware_suppress
Open

KAFKA-20413: Make Streams suppress headers-aware#22165
aliehsaeedii wants to merge 6 commits into
apache:trunkfrom
aliehsaeedii:headers_aware_suppress

Conversation

@aliehsaeedii

@aliehsaeedii aliehsaeedii commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

This PR fixes the suppress-buffer part of
KAFKA-20413:
InMemoryTimeOrderedKeyValueChangeBuffer now uses the buffered record's
own headers in serde calls and on eviction.

Testing: Added shouldPropagateHeadersThroughEviction, and
shouldUseRecordHeadersNotProcessorContextHeadersOnPut.

Reviewers: Matthias J. Sax matthias@confluent.io

@github-actions github-actions Bot added triage PRs from the community streams labels Apr 28, 2026
@aliehsaeedii aliehsaeedii changed the title KAFKA-20413:: Make Streams suppress headers-aware KAFKA-20413: Make Streams suppress headers-aware Apr 28, 2026

@UladzislauBlok UladzislauBlok 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.

LGTM

@github-actions github-actions Bot removed the triage PRs from the community label Apr 30, 2026
@github-actions github-actions Bot added the small Small PRs label Jul 10, 2026
@aliehsaeedii
aliehsaeedii force-pushed the headers_aware_suppress branch from aa6f424 to d02cd7f Compare July 10, 2026 13:39

@mjsax mjsax left a comment

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.

The fix is surprisingly simple... Should we add some TTD test (or even integration test) for some e2e verification?

@github-actions github-actions Bot removed the small Small PRs label Jul 13, 2026
aliehsaeedii and others added 3 commits July 14, 2026 00:21
Add shouldPropagateHeadersThroughSuppression to SuppressScenarioTest: it
drives a table/groupBy/count/suppress topology through TopologyTestDriver
with a header-bearing input record and asserts the header survives on the
suppressed output, giving end-to-end coverage of the headers-aware buffer
put/evict path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aliehsaeedii
aliehsaeedii force-pushed the headers_aware_suppress branch from ac1f21d to e1a0a65 Compare July 13, 2026 22:22
@aliehsaeedii

Copy link
Copy Markdown
Contributor Author

The fix is surprisingly simple... Should we add some TTD test (or even integration test) for some e2e verification?

Good point. Added.

@aliehsaeedii

Copy link
Copy Markdown
Contributor Author

Build failed because of unrelated flaky tests!

.count();

valueCounts
.suppress(untilTimeLimit(ZERO, unbounded()))

@mjsax mjsax Jul 15, 2026

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.

I don't think this work correctly. If we set ZERO we flush the suppress buffer right away, and even in the old code, headers would get carried forward via the context... (I just ran this this unmodified on trunk and it also passed).

Instead, we need to set an actual suppress time and pipe two input record. The second record (with larger ts) would flush the suppress buffer emitting the first. W/o the fix, the headers from the second record would carry forward into the result, but with the fix, the correct headers from the suppressed record should be there.

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 verified the reworked scenario (2-record) and it still passes on trunk. The question is whether TTD is propagating/mutating the specific ProcessorRecordContext instance that gets stored inside k1's BufferValue when k1 (1st input record) is buffered? I assume not. I think I must remove the e2e test since it does not prove anything.

@mjsax mjsax added the kip Requires or implements a KIP label Jul 16, 2026
valueCounts
.suppress(untilTimeLimit(ZERO, unbounded()))
.toStream()
.to("output-suppressed", Produced.with(STRING_SERDE, Serdes.Long()));

@mjsax mjsax Jul 16, 2026

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.

I am also wondering, if we should add another process() step before we write to the output topic, to access context.headers() (and maybe add information about it to the record's value, so we can capture it on the output topic).

When the suppress() flush happens while processing the second record, the context should not have been set to the flushed/first record's header I believe, but should be the headers from the second record triggering the flush? Not 100% sure.

time,
serializedKey,
new BufferValue(serializedPriorValue, serialChange.oldValue, serialChange.newValue, recordContext)
new BufferValue(serializedPriorValue, serialChange.oldValue, serialChange.newValue, effectiveContext)

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.

I am confuse the this change -- when we do a put() we need to put the headers for the new record, not the old record, right?

I did more digging and it seems we need a different fix... What the buffer stores is an "oldValue" and a "newValue" but only one record-context for both. Your change is changing what we put into the record context, but I believe that is not what we should change -- the context should be from the currently processes record.

Thus, we actually need to change the serialization format of oldValue and newValue -- atm, we serializer plain value types, but we would need to start using ValueTimestampHeader type (this would also fix another issue that we atm also lose the correct record timestamp via suppress). This allows us to store both header for old and new and get the correct old headers into the output on eviction.

So we need to update keySerde and valueSerde and create a corresponding ValueTimestampHeaders object here that we put into the suppress buffer.

For backward compatibility, we should also do this only if header stores are enable I believe? Otherwise, we would increase users' in-memory consumption significantly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-approved kip Requires or implements a KIP streams

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants