Skip to content

fix(cdc): cast to GenericRecord in PostgresDebeziumAvroPayload toasted-value checks - #19564

Draft
yihua wants to merge 1 commit into
apache:masterfrom
yihua:fix-postgres-debezium-toast-cast
Draft

fix(cdc): cast to GenericRecord in PostgresDebeziumAvroPayload toasted-value checks#19564
yihua wants to merge 1 commit into
apache:masterfrom
yihua:fix-postgres-debezium-toast-cast

Conversation

@yihua

@yihua yihua commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Describe the issue this Pull Request addresses

closes #19563

PostgresDebeziumAvroPayload casts to the concrete class GenericData.Record in mergeToastedValuesIfPresent and containsBytesToastedValues. Since #13987 ("perf: Lazy deserialization of Avro indexed record"), BaseAvroPayload.getRecord returns a SerializableIndexedRecord whenever the payload's record does not already carry the exact Schema instance being merged with, which is the ordinary case in production and after any Kryo round trip. SerializableIndexedRecord implements GenericRecord but does not extend GenericData.Record, so any TOAST check on a non-null BYTES column throws ClassCastException. The sibling containsStringToastedValues was already converted to the interface, which is why only BYTES columns are affected.

Summary and Changelog

Debezium Postgres ingestion of tables with a BYTES column no longer fails when the incoming event wins the LSN comparison.

  • PostgresDebeziumAvroPayload: cast to the GenericRecord interface instead of the GenericData.Record implementation, in mergeToastedValuesIfPresent (the record already in storage) and in both reads inside containsBytesToastedValues.
  • TestPostgresDebeziumAvroPayload#testMergeWithToastedValues: turned into a parameterized test over two independent dimensions, whether the incoming record is lazily deserialized (built on an equal but distinct Schema instance, which forces the round trip) and whether the record already in storage is a SerializableIndexedRecord. An added assertion pins that the lazy path is actually taken, so the parameterization cannot quietly degrade into four copies of the original case. Reverting the payload change fails 3 of the 4 cases with the reported ClassCastException; the pre-existing case is the one that passed before.

Impact

None beyond fixing the crash. The interface cast accepts everything the implementation cast accepted.

Risk Level

low

Covered by the parameterized regression test above, verified to fail without the payload change.

Documentation Update

none

Contributor's checklist

  • Read through contributor's guide
  • Enough context is provided in the sections above
  • Adequate tests were added if applicable

…d-value checks

BaseAvroPayload.getRecord returns a SerializableIndexedRecord when the payload
record does not carry the exact Schema instance being merged with, so casting to
GenericData.Record threw ClassCastException on any non-null BYTES column.
@codecov-commenter

codecov-commenter commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 71.97%. Comparing base (65cf7e8) to head (0aab7b4).

Files with missing lines Patch % Lines
...on/model/debezium/PostgresDebeziumAvroPayload.java 66.66% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master   #19564      +/-   ##
============================================
- Coverage     76.84%   71.97%   -4.87%     
+ Complexity    32379    30327    -2052     
============================================
  Files          2522     2522              
  Lines        139106   139106              
  Branches      16713    16713              
============================================
- Hits         106892   100123    -6769     
- Misses        24621    31763    +7142     
+ Partials       7593     7220     -373     
Components Coverage Δ
hudi-common 82.18% <66.66%> (-1.09%) ⬇️
hudi-client 79.32% <ø> (-2.68%) ⬇️
hudi-flink 84.70% <ø> (ø)
hudi-spark-datasource 45.44% <ø> (-25.16%) ⬇️
hudi-utilities 73.63% <ø> (ø)
hudi-cli 15.32% <ø> (ø)
hudi-hadoop 61.56% <ø> (-1.95%) ⬇️
hudi-sync 75.09% <ø> (-0.03%) ⬇️
hudi-io 79.46% <ø> (ø)
hudi-timeline-service 77.57% <ø> (-5.88%) ⬇️
hudi-cloud 64.06% <ø> (ø)
hudi-kafka-connect 53.20% <ø> (ø)
Flag Coverage Δ
common-and-other-modules 50.15% <66.66%> (+<0.01%) ⬆️
flink-integration-tests 48.94% <0.00%> (-0.01%) ⬇️
hadoop-mr-java-client 43.73% <0.00%> (+<0.01%) ⬆️
integration-tests 13.56% <0.00%> (-0.01%) ⬇️
spark-client-hadoop-common 49.63% <0.00%> (ø)
spark-java-tests 48.06% <66.66%> (-3.52%) ⬇️
spark-scala-tests 32.70% <0.00%> (-13.28%) ⬇️
utilities 36.58% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...on/model/debezium/PostgresDebeziumAvroPayload.java 94.28% <66.66%> (ø)

... and 351 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions Bot added the size:S PR with lines of changes in (10, 100] label Aug 8, 2026
@hudi-bot

hudi-bot commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

CI report:

Bot commands @hudi-bot supports the following commands:
  • @hudi-bot run azure re-run the last Azure build

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

Labels

size:S PR with lines of changes in (10, 100]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] PostgresDebeziumAvroPayload throws ClassCastException on BYTES columns after lazy Avro deserialization

3 participants