Skip to content

fix(flink): preserve Avro fixed decimal widths in Parquet writes - #19522

Merged
danny0405 merged 2 commits into
apache:masterfrom
cshuo:fix_fixed_len_decimal
Aug 6, 2026
Merged

fix(flink): preserve Avro fixed decimal widths in Parquet writes#19522
danny0405 merged 2 commits into
apache:masterfrom
cshuo:fix_fixed_len_decimal

Conversation

@cshuo

@cshuo cshuo commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Describe the issue this Pull Request addresses

Closes #19521.

The Flink RowData Parquet writer previously derived the physical width of every decimal from precision alone. This produced a schema and encoded values that were narrower than the authoritative Hudi/Avro schema when a decimal used an over-allocated fixed backing type. The compact-decimal encoding path also relied on shifts that are unsafe when the declared fixed width exceeds eight bytes.

Summary and Changelog

  • Honor the declared Avro fixed size when converting fixed-backed decimals to Parquet FIXED_LEN_BYTE_ARRAY, while retaining the precision-based minimum for non-fixed decimal schemas.
  • Use the same resolved width in the RowData value writer and sign-extend compact positive and negative decimal values when the declared fixed width is greater than eight bytes.
  • Add regression coverage for fixed-backed and bytes-backed schemas, including compact negative values and encoded byte widths.

Impact

Flink Parquet writes now preserve the declared physical width of fixed-backed decimal schemas passed to the writer. There are no public API or configuration changes. Decimal schemas without a fixed backing type retain the existing minimum-width behavior.

Risk Level

Low. The change is limited to Flink Parquet decimal schema conversion and value encoding. Targeted tests cover schema widths and encoded values for both fixed-backed and fallback decimal representations.

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

@cshuo
cshuo marked this pull request as ready for review August 5, 2026 08:00

@hudi-agent hudi-agent 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.

⚠️ 🤖 This review was generated by an AI agent and may contain mistakes. Please verify any suggestions before applying.

Thanks for the contribution! This PR makes the Flink RowData Parquet writer honor the declared Avro fixed size for fixed-backed decimals (instead of deriving width from precision alone) and replaces the shift-based compact-decimal encoding with a sign-extending big-endian write that is safe when the fixed width exceeds eight bytes. I traced the encoding paths for both writers (LongUnscaledBytesWriter for precision ≤ 18, UnscaledBytesWriter for 19–38) across boundary widths and positive/negative/nullable/nested cases, and confirmed the Parquet schema length and the emitted binary length stay in sync (both now use decimalFixedLen(fieldSchema, precision) on the non-null schema), so writes remain internally consistent and read-compatible. No correctness issues found. A few style/readability suggestions in the inline comments. Please take a look, and this should be ready for a Hudi committer or PMC member to take it from here. One small naming nit; otherwise the change is clean and readable.

cc @yihua

@cshuo cshuo left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed the naming feedback in c06e676. The targeted Flink Parquet schema and RowData writer tests pass.

@hudi-agent hudi-agent 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.

⚠️ 🤖 This review was generated by an AI agent and may contain mistakes. Please verify any suggestions before applying.

Thanks for working on this! This PR makes the Flink RowData Parquet writer honor the declared Avro fixed size when converting fixed-backed decimals, and adds big-endian sign-extension so compact decimals encode correctly when the declared fixed width exceeds eight bytes. I traced both the schema conversion and value-encoding paths: they now resolve the byte width through the shared resolveDecimalByteLength helper after stripping nullability, so the declared Parquet length and the encoded value width stay consistent; the sign-extension logic matches the test vectors, and Avro validation guarantees the fixed size is never smaller than the precision minimum, so widening only pads and never truncates. No issues flagged from this automated pass, a Hudi committer or PMC member can take it from here for a final review.

cc @yihua

cshuo added 2 commits August 6, 2026 09:48
Honor declared Avro fixed sizes in both the Flink Parquet schema converter and RowData value writer. Safely sign-extend compact decimals when the declared fixed width exceeds eight bytes.
Rename the helper to describe both fixed-schema resolution and precision-based fallback behavior. Addresses review comment 3718983684.
@cshuo
cshuo force-pushed the fix_fixed_len_decimal branch from c06e676 to 1046c0a Compare August 6, 2026 01:49

@hudi-agent hudi-agent 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.

⚠️ 🤖 This review was generated by an AI agent and may contain mistakes. Please verify any suggestions before applying.

Thanks for working on this! The PR makes the Flink RowData Parquet writer honor the declared Avro fixed size for fixed-backed decimals so the emitted FIXED_LEN_BYTE_ARRAY schema width and the encoded value width stay consistent, and adds sign-extension for widths over eight bytes. I traced the compact (LongUnscaledBytesWriter) and non-compact (UnscaledBytesWriter) encoding paths across boundary, negative, and over-allocated-fixed cases, and confirmed the schema converter and value writer share the same width resolver. No correctness issues found. A few style/readability suggestions in the inline comments. Please take a look, and this should be ready for a Hudi committer or PMC member to take it from here. One minor naming nit below, otherwise clean.

cc @yihua

return numBytes;
}

static int resolveDecimalByteLength(HoodieSchema fieldSchema, int precision) {

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.

🤖 nit: the sibling method uses compute as its verb (computeMinBytesForDecimalPrecision); could you align the name here — e.g. computeDecimalByteLength — so the two helpers read as a family?

⚠️ AI-generated; verify before applying. React 👍/👎 to flag quality.

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

codecov-commenter commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 69.23077% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.76%. Comparing base (4a9cef8) to head (1046c0a).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
...io/storage/row/parquet/ParquetSchemaConverter.java 50.00% 1 Missing and 2 partials ⚠️
...i/io/storage/row/parquet/ParquetRowDataWriter.java 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master   #19522      +/-   ##
============================================
- Coverage     76.40%   73.76%   -2.64%     
+ Complexity    32398    31014    -1384     
============================================
  Files          2520     2520              
  Lines        138985   138987       +2     
  Branches      16695    16697       +2     
============================================
- Hits         106189   102523    -3666     
- Misses        25166    28680    +3514     
- Partials       7630     7784     +154     
Components Coverage Δ
hudi-common 80.80% <ø> (-1.55%) ⬇️
hudi-client 77.46% <ø> (-4.53%) ⬇️
hudi-flink 83.91% <69.23%> (-0.06%) ⬇️
hudi-spark-datasource 64.07% <ø> (-6.55%) ⬇️
hudi-utilities 73.63% <ø> (-0.05%) ⬇️
hudi-cli 15.32% <ø> (ø)
hudi-hadoop 60.43% <ø> (-3.08%) ⬇️
hudi-sync 71.00% <ø> (ø)
hudi-io 79.31% <ø> (-0.15%) ⬇️
hudi-timeline-service 77.57% <ø> (-5.88%) ⬇️
hudi-cloud 64.06% <ø> (ø)
hudi-kafka-connect 53.20% <ø> (ø)
Flag Coverage Δ
flink-integration-tests 48.79% <69.23%> (-0.01%) ⬇️
hadoop-mr-java-client 43.75% <ø> (-0.02%) ⬇️
integration-tests 13.58% <0.00%> (-0.01%) ⬇️
spark-client-hadoop-common 49.62% <ø> (-0.01%) ⬇️
spark-java-tests 32.64% <ø> (-18.61%) ⬇️
utilities 36.59% <ø> (-0.01%) ⬇️

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

Files with missing lines Coverage Δ
...i/io/storage/row/parquet/ParquetRowDataWriter.java 81.18% <85.71%> (-0.58%) ⬇️
...io/storage/row/parquet/ParquetSchemaConverter.java 82.38% <50.00%> (-1.04%) ⬇️

... and 342 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.

@hudi-bot

hudi-bot commented Aug 6, 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

@danny0405
danny0405 merged commit 0425c5c into apache:master Aug 6, 2026
23 checks passed
voonhous pushed a commit that referenced this pull request Aug 7, 2026
)

* fix(flink): preserve Avro fixed decimal widths in Parquet writes

Honor declared Avro fixed sizes in both the Flink Parquet schema converter and RowData value writer. Safely sign-extend compact decimals when the declared fixed width exceeds eight bytes.

* style(flink): clarify decimal byte length resolver name

Rename the helper to describe both fixed-schema resolution and precision-based fallback behavior. Addresses review comment 3718983684.

(cherry picked from commit 0425c5c)

Adaptations for release-1.2.1:
The fix needs each field's HoodieSchema to read the Avro fixed size, and master
had already plumbed that through. Here the schema reached
RowDataParquetWriteSupport and was converted straight to a RowType and dropped,
so the threading is backported alongside the fix, without master's VECTOR work:
- HoodieSchemaUtils.getFieldSchema, matching master.
- A convertToParquetMessageType(String, HoodieSchema) overload, and a fieldSchema
  argument threaded through convertToParquetType into ROW, ARRAY and MAP.
- ParquetRowDataWriter takes master's (RecordConsumer, boolean, HoodieSchema)
  constructor. The RowType is derivable from the schema and the GroupType was
  unused. This also lets the upstream test file apply unchanged.

Unlike master, convertToParquetMessageType(String, RowType) does not delegate to
the HoodieSchema overload. Converting a RowType to a HoodieSchema is lossy for
two types Parquet supports and Avro does not, and both are covered by existing
tests here: a map with a non-string key, and a timestamp of precision 9. The
field schema is therefore @nullable, and resolveDecimalByteLength already falls
back to computeMinBytesForDecimalPrecision when it is absent.
voonhous pushed a commit that referenced this pull request Aug 7, 2026
)

* fix(flink): preserve Avro fixed decimal widths in Parquet writes

Honor declared Avro fixed sizes in both the Flink Parquet schema converter and RowData value writer. Safely sign-extend compact decimals when the declared fixed width exceeds eight bytes.

* style(flink): clarify decimal byte length resolver name

Rename the helper to describe both fixed-schema resolution and precision-based fallback behavior. Addresses review comment 3718983684.

(cherry picked from commit 0425c5c)

Adaptations for release-1.2.1:
The fix needs each field's HoodieSchema to read the Avro fixed size, and master
had already plumbed that through. Here the schema reached
RowDataParquetWriteSupport and was converted straight to a RowType and dropped,
so the threading is backported alongside the fix, without master's VECTOR work:
- HoodieSchemaUtils.getFieldSchema, matching master.
- A convertToParquetMessageType(String, HoodieSchema) overload, and a fieldSchema
  argument threaded through convertToParquetType into ROW, ARRAY and MAP.
- ParquetRowDataWriter takes master's (RecordConsumer, boolean, HoodieSchema)
  constructor. The RowType is derivable from the schema and the GroupType was
  unused. This also lets the upstream test file apply unchanged.

Unlike master, convertToParquetMessageType(String, RowType) does not delegate to
the HoodieSchema overload. Converting a RowType to a HoodieSchema is lossy for
two types Parquet supports and Avro does not, and both are covered by existing
tests here: a map with a non-string key, and a timestamp of precision 9. The
field schema is therefore @nullable, and resolveDecimalByteLength already falls
back to computeMinBytesForDecimalPrecision when it is absent.
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] Flink Parquet writer ignores declared Avro fixed size for decimal

5 participants