Skip to content

feat(flink): Sort bulk insert records by record key for LSM layout - #19390

Open
cshuo wants to merge 2 commits into
apache:masterfrom
cshuo:issue-19325-flink-lsm-bulk-insert
Open

feat(flink): Sort bulk insert records by record key for LSM layout#19390
cshuo wants to merge 2 commits into
apache:masterfrom
cshuo:issue-19325-flink-lsm-bulk-insert

Conversation

@cshuo

@cshuo cshuo commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Describe the issue this Pull Request addresses

Flink uses the LSM tree storage layout by default, but bulk_insert previously fell back to the default layout because its input was not guaranteed to satisfy the LSM sorted-run invariant.

LSM bulk insert must order records by the encoded Hudi record key rather than the key field’s native Flink type. This PR enables LSM bulk insert and ensures the required physical ordering.

Fixes #19325.

Summary and Changelog

  • Use LSM as the default layout for Flink bulk insert while preserving the explicitly configured default-layout path.
  • Sort LSM bulk-insert records by partition/file group and encoded record key.
  • Add dedicated LSM sort-row and writer helpers while reusing the existing external sorter.
  • Add unit and integration coverage for sorting, duplicate preservation, bucket indexes, COW/MOR tables, and subsequent updates.

Impact

  • Functional impact: Flink bulk insert now supports the default LSM layout and writes records in encoded record-key order.
  • Maintainability: Layout-specific sorting and writing are isolated in dedicated helpers.
  • Compatibility: Explicit hoodie.table.storage.layout=default retains its existing behavior.

Risk Level

Medium. The change affects Flink bulk-insert routing, sorting, and writer selection. Unit and integration tests cover bucket and non-bucket indexes, COW/MOR tables, partitioned and non-partitioned tables, duplicate keys, and subsequent updates.

Documentation Update

none. No new user-facing configuration is introduced.

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 draft July 28, 2026 09:21
@codecov-commenter

codecov-commenter commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.98742% with 35 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.82%. Comparing base (a284d6e) to head (51b5e60).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
...ain/java/org/apache/hudi/sink/utils/Pipelines.java 66.66% 25 Missing and 5 partials ⚠️
.../apache/hudi/sink/bulk/BulkInsertWriterHelper.java 76.92% 1 Missing and 2 partials ⚠️
.../java/org/apache/hudi/sink/bulk/WriterHelpers.java 71.42% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master   #19390      +/-   ##
============================================
- Coverage     74.83%   74.82%   -0.01%     
+ Complexity    32332    32322      -10     
============================================
  Files          2574     2577       +3     
  Lines        142978   143065      +87     
  Branches      17527    17535       +8     
============================================
+ Hits         106992   107055      +63     
- Misses        27921    27950      +29     
+ Partials       8065     8060       -5     
Components Coverage Δ
hudi-common 82.25% <ø> (+<0.01%) ⬆️
hudi-client 82.08% <ø> (+0.28%) ⬆️
hudi-flink 78.59% <77.98%> (-0.05%) ⬇️
hudi-spark-datasource 67.98% <ø> (-0.36%) ⬇️
hudi-utilities 71.20% <ø> (+0.01%) ⬆️
hudi-cli 15.32% <ø> (ø)
hudi-hadoop 63.53% <ø> (+0.03%) ⬆️
hudi-sync 70.70% <ø> (+0.02%) ⬆️
hudi-io 79.66% <ø> (+0.09%) ⬆️
hudi-timeline-service 83.74% <ø> (-0.49%) ⬇️
hudi-cloud 64.00% <ø> (ø)
hudi-kafka-connect 53.20% <ø> (ø)
Flag Coverage Δ
common-and-other-modules 47.75% <33.33%> (-0.03%) ⬇️
flink-integration-tests 47.62% <77.98%> (-0.02%) ⬇️
hadoop-mr-java-client 43.39% <ø> (-0.01%) ⬇️
integration-tests 13.62% <0.00%> (-0.01%) ⬇️
spark-client-hadoop-common 48.71% <ø> (-0.01%) ⬇️
spark-java-tests 51.46% <ø> (+0.07%) ⬆️
spark-scala-tests 45.78% <ø> (-0.31%) ⬇️
utilities 36.64% <ø> (-0.01%) ⬇️

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

Files with missing lines Coverage Δ
...org/apache/hudi/configuration/OptionsResolver.java 84.61% <100.00%> (ø)
...hudi/sink/bucket/BucketBulkInsertWriterHelper.java 79.48% <100.00%> (+1.10%) ⬆️
...i/sink/bucket/LsmBucketBulkInsertWriterHelper.java 100.00% <100.00%> (ø)
...ache/hudi/sink/bulk/LsmBulkInsertWriterHelper.java 100.00% <100.00%> (ø)
...he/hudi/sink/bulk/sort/LsmBulkInsertSortUtils.java 100.00% <100.00%> (ø)
...java/org/apache/hudi/table/HoodieTableFactory.java 85.60% <100.00%> (+0.23%) ⬆️
.../java/org/apache/hudi/sink/bulk/WriterHelpers.java 62.50% <71.42%> (-12.50%) ⬇️
.../apache/hudi/sink/bulk/BulkInsertWriterHelper.java 74.03% <76.92%> (+0.56%) ⬆️
...ain/java/org/apache/hudi/sink/utils/Pipelines.java 86.03% <66.66%> (-8.19%) ⬇️

... and 23 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:L PR with lines of changes in (300, 1000] label Jul 28, 2026
@cshuo
cshuo force-pushed the issue-19325-flink-lsm-bulk-insert branch from 09cdda4 to 1b8b304 Compare July 29, 2026 01:53
@cshuo
cshuo marked this pull request as ready for review July 29, 2026 01:53

@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 enables the LSM storage layout as the default for Flink bulk insert and physically orders records by encoded Hudi record key (partition/file-group, then key) via dedicated LSM sort-row and writer helpers, while preserving existing default-layout behavior. I traced the pipeline routing, writer handle management, sort-row decoding, and backward compatibility for existing default-layout tables, and did not find correctness issues in this automated pass. 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 indentation inconsistency in WriterHelpers — the rest of the changes are clean and well-structured.

cc @yihua

} else {
return OptionsResolver.isBucketIndexType(conf)
? new BucketBulkInsertWriterHelper(
conf, hoodieTable, writeConfig, instantTime, taskPartitionId, taskId, taskEpochId, rowType)

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 conf argument here is indented to the ? level rather than the constructor argument level — could you align it with the conf on line 39/47 ( conf, ...) to match the other three constructor calls in this method?

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

@cshuo
cshuo force-pushed the issue-19325-flink-lsm-bulk-insert branch from 1b8b304 to 6c24577 Compare July 29, 2026 03:29
@cshuo
cshuo force-pushed the issue-19325-flink-lsm-bulk-insert branch from 6c24577 to 51b5e60 Compare July 29, 2026 06:35
@hudi-bot

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

@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 enables the LSM storage layout for Flink bulk insert — sorting records by encoded record key and adding dedicated LSM sort/writer helpers. The routing, sorting, and writer refactor look consistent, and backward compatibility for existing tables is preserved (the persisted hoodie.table.storage.layout is inherited on reopen). The one item worth a closer look is how duplicate record keys behave on read now that bulk insert defaults to LSM — see the inline comment. Please take a look at any inline comments, and this should be ready for a Hudi committer or PMC member to take it from here. One indentation inconsistency worth a quick fix, otherwise the code is clean and well-structured.

return HoodieTableConfig.TableStorageLayout.fromConfigValue(conf.getString(
HoodieTableConfig.TABLE_STORAGE_LAYOUT.key(),
(isInsertOperation(conf) || isBulkInsertOperation(conf))
isInsertOperation(conf)

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.

🤖 Bulk insert now defaults to LSM, but the LSM reader collapses all versions of a key within a sorted run (LsmFileGroupRecordIterator#nextMergedRecord pops consecutive same-key records and merges them), so duplicate keys that bulk insert writes into a base file get merged into one on snapshot read. checkStorageLayout still blocks insert "because they allow duplicate record keys" — bulk insert has the same property. @danny0405 is silently collapsing bulk-insert duplicates on read the intended behavior? The new test only checks the sort row, not an end-to-end read.

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

} else {
return OptionsResolver.isBucketIndexType(conf)
? new BucketBulkInsertWriterHelper(
conf, hoodieTable, writeConfig, instantTime, taskPartitionId, taskId, taskEpochId, rowType)

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 constructor arguments for BucketBulkInsertWriterHelper are indented at the ? level rather than four spaces past it, unlike every other constructor in this block — could you align it with the LsmBucketBulkInsertWriterHelper args above (i.e. add four more spaces)?

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

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

Labels

size:L PR with lines of changes in (300, 1000]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] Sort Flink bulk insert records by record key for LSM layout

4 participants