[server] Attach vtp protocol-schema header on DoL stamp segment-start record#2808
Merged
sushantmane merged 1 commit intoMay 20, 2026
Merged
Conversation
… record
VeniceWriter.sendDoLStamp produces its record with
EmptyPubSubMessageHeaders.SINGLETON
directly, bypassing the getHeaders() helper that attaches the vtp
protocol-schema
header on a segment-start record. Every DoL stamp lands on the wire with empty
headers - and when a STANDBY -> LEADER transition writes a DoL stamp at
offset 0
of a brand-new version topic, a forward-compat consumer whose jar predates the
current KME protocol version has no on-record schema to bootstrap with. It
throws:
Received Protocol Version 'N' which is not supported by
KafkaValueSerializer.
The only supported Protocol Versions are: {1,...,N-1}
The bug was latent until KME v14 was activated (PR linkedin#2780). A consumer running
an
older jar (only supports v1..v13) reading from offset 0 of a fresh version
topic
now hits a v14-encoded DoL stamp with no headers and no way to bootstrap.
Fix: route sendDoLStamp through getHeaders(producerMetadata, false, null,
EmptyPubSubMessageHeaders.SINGLETON), same pattern sendHeartbeat already uses.
DoL stamps always carry segmentNumber=0 and messageSequenceNumber=0 (set in
getDoLStampKME), so needVtpHeader is true and vtp is attached.
Testing Done:
- New unit test VeniceWriterUnitTest.testSendDoLStampCarriesVtpHeader captures
the headers passed to the producer adapter and asserts (a) vtp is present,
(b) the header bytes parse as the current KafkaMessageEnvelope schema JSON,
and (c) producerMetadata.segmentNumber == 0 && messageSequenceNumber == 0
(the invariant that drives needVtpHeader=true inside getHeaders).
- Existing VeniceWriterUnitTest tests still pass.
There was a problem hiding this comment.
Pull request overview
This PR fixes a forward-compatibility issue where VeniceWriter.sendDoLStamp produced a DoL stamp control message with empty headers, bypassing the getHeaders(...) helper that attaches the vtp (VENICE_TRANSPORT_PROTOCOL_HEADER) protocol-schema header needed for consumers to bootstrap unknown KafkaMessageEnvelope protocol versions.
Changes:
- Route
sendDoLStampthroughgetHeaders(...)so segment-start DoL stamps carry thevtpprotocol-schema header. - Add a regression unit test asserting DoL stamps include
vtpand that the DoL stamp’sProducerMetadatatriggers the segment-start header logic.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| internal/venice-common/src/main/java/com/linkedin/venice/writer/VeniceWriter.java | Ensures DoL stamp sends use getHeaders(...) so vtp is attached on segment-start records. |
| internal/venice-common/src/test/java/com/linkedin/venice/writer/VeniceWriterUnitTest.java | Adds regression coverage validating sendDoLStamp carries vtp and uses segment-start producer metadata. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
KaiSernLim
approved these changes
May 20, 2026
Contributor
Author
|
Thanks for the review, @KaiSernLim! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
VeniceWriter.sendDoLStampwas passingEmptyPubSubMessageHeaders.SINGLETONdirectly toproducerAdapter.sendMessage, bypassing thegetHeaders()helper that attaches theVENICE_TRANSPORT_PROTOCOL_HEADER(vtp) on a segment-start record. Every DoL stamp therefore landed on the wire with empty headers — and when aSTANDBY → LEADERtransition writes a DoL stamp at offset 0 of a brand-new version topic, a forward-compat consumer whose jar predates the current KME protocol version has no on-record schema to bootstrap with. It throws:Root cause
VeniceWriter.getHeaders(producerMetadata, addLeaderCompleteHeader, leaderCompleteState, extraHeaders)is the canonical helper that produces per-recordPubSubMessageHeaders. Its contract:producerMetadata.segmentNumber == 0 && producerMetadata.messageSequenceNumber == 0(the first record of a new segment), it attaches thevtpheader carrying the current KME protocol-schema JSON. The receiving consumer uses that bytes-blob to construct an Avro reader for the writer's protocol version, even if the receiver's jar doesn't know about it.extraHeadersunchanged.sendHeartbeatalready routes throughgetHeaders(...)and attachesvtpon the first heartbeat of every segment.sendDoLStampdid not — it constructed its KME viagetDoLStampKME(which setssegmentNumber == 0 && messageSequenceNumber == 0) and then calledproducerAdapter.sendMessagewithEmptyPubSubMessageHeaders.SINGLETONinstead of routing throughgetHeaders. The DoL stamp's "I should be carryingvtp" precondition was always true, butvtpwas never actually attached.The bug was latent until KME
v14was activated (#2780). A consumer running an older jar (only supportsv1..v13) that reads from offset 0 of a fresh version topic now hits av14-encoded DoL stamp with no headers and no way to bootstrap — the deserialize fails permanently for that consumer until it's upgraded.Fix
VeniceWriter.sendDoLStampnow routes itssendMessagecall through:This is the same pattern
sendHeartbeatuses. Because DoL stamps always carrysegmentNumber == 0 && messageSequenceNumber == 0(set insidegetDoLStampKME), theneedVtpHeaderbranch fires andvtpis attached. No behavioral change for non-DoL paths.Scope
This PR is intentionally minimal — only the writer fix and the regression test. A follow-up PR will add defense-in-depth on the consumer side by plumbing a KME
SchemaReaderthrough every callsite that opens its ownPubSubMessageDeserializer, so consumers that land on a header-less record from some other future writer regression can still resolve an unknown protocol version. Splitting these makes the immediate fix easy to review and easy to backport.Related
Testing Done
VeniceWriterUnitTest.testSendDoLStampCarriesVtpHeader: captures the headers passed to the producer adapter on asendDoLStampcall and asserts (a)VENICE_TRANSPORT_PROTOCOL_HEADERis present, (b) the header bytes parse as the currentKafkaMessageEnvelopeprotocol-schema JSON, and (c) the underlyingproducerMetadata.segmentNumber == 0 && messageSequenceNumber == 0(the invariant that drivesneedVtpHeader=trueinsidegetHeaders).VeniceWriterUnitTesttests pass (testSendHeartbeat,testVeniceWriterCloseRetry, etc.).internal:venice-common(main + test sources).