Skip to content

feat(flink): add partitioned rli boostrap operator - #19805

Open
HuangZhenQiu wants to merge 1 commit into
apache:masterfrom
HuangZhenQiu:partitioned-rli-bootstrap
Open

feat(flink): add partitioned rli boostrap operator#19805
HuangZhenQiu wants to merge 1 commit into
apache:masterfrom
HuangZhenQiu:partitioned-rli-bootstrap

Conversation

@HuangZhenQiu

Copy link
Copy Markdown
Member

Describe the issue this Pull Request addresses

add partitioned rli boostrap operator to support

Summary and Changelog

  1. Added the new ParititionedRLIBoostrapOperator to load the record index for last x days defined in configuration
  2. Wired the new operator in Pipelines safe guarded by configuration
  3. Added test cases for newly added classes and functionality

Impact

none

Risk Level

none

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
@HuangZhenQiu
HuangZhenQiu requested review from cshuo and danny0405 and removed request for cshuo September 1, 2026 06:32

@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 adds a PartitionedRLIBootstrapOperator that eagerly preloads record-level-index data for date partitions within a configurable recency window, and wires it into the bootstrap pipeline behind the RocksDB partitioned-RLI backend config. The logic is well-tested and the window/parse handling looks solid; one parallelism concern around per-partition file-slice distribution is worth double-checking in 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 minor naming nit below.

closeMetadataTable();
}

private void preLoadPartition(String partitionPath, List<FileSlice> fileSlices, int taskID, int parallelism) {

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.

🤖 The round-robin (i % parallelism == taskID) is applied to the file-slice index within each partition here, whereas RLIBootstrapOperator applies it once over the global file-slice list. For a date-partitioned table where each partition typically has only 1-2 RLI file groups, every single-file-slice partition maps to index 0 and lands entirely on task 0, leaving the other bootstrap tasks idle. Have you considered a global counter across partitions (or hashing on fileId) so the load spreads across all index_bootstrap tasks?

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

* determine whether it falls inside the window. Partitions outside the window, and partitions whose
* path cannot be parsed as a date, are skipped here and are expected to be loaded on demand later.
*
* <p>Setting {@link FlinkOptions#INDEX_RLI_CACHE_ROCKSDB_BOOTSTRAP_DAYS} to {@code 0} disables preloading

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: could you rename loadedCnt to loadedCount? Cnt is an uncommon abbreviation that adds a small mental speed-bump, especially since the field name surfaces directly in log messages.

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

@codecov-commenter

codecov-commenter commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.20388% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.14%. Comparing base (1e59c9e) to head (2f359aa).

Files with missing lines Patch % Lines
...ink/bootstrap/PartitionedRLIBootstrapOperator.java 92.78% 3 Missing and 4 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master   #19805      +/-   ##
============================================
- Coverage     78.30%   74.14%   -4.16%     
+ Complexity    33871    32157    -1714     
============================================
  Files          2541     2542       +1     
  Lines        141708   141810     +102     
  Branches      17177    17191      +14     
============================================
- Hits         110961   105152    -5809     
- Misses        23038    29432    +6394     
+ Partials       7709     7226     -483     
Components Coverage Δ
hudi-common 83.07% <ø> (-0.57%) ⬇️
hudi-client 82.24% <ø> (-0.96%) ⬇️
hudi-flink 85.69% <93.20%> (+0.03%) ⬆️
hudi-spark-datasource 48.25% <ø> (-24.95%) ⬇️
hudi-utilities 74.56% <ø> (+0.03%) ⬆️
hudi-cli 15.06% <ø> (ø)
hudi-hadoop 70.56% <ø> (-0.16%) ⬇️
hudi-sync 75.54% <ø> (ø)
hudi-io 79.88% <ø> (ø)
hudi-timeline-service 83.44% <ø> (ø)
hudi-cloud 65.81% <ø> (ø)
hudi-kafka-connect 53.96% <ø> (ø)
Flag Coverage Δ
common-and-other-modules 51.48% <91.26%> (+0.02%) ⬆️
flink-integration-tests 48.86% <93.20%> (+0.05%) ⬆️
hadoop-mr-java-client 44.08% <ø> (+<0.01%) ⬆️
spark-client-hadoop-common 50.55% <ø> (ø)
spark-java-tests 52.20% <ø> (+0.01%) ⬆️
spark-scala-tests 33.98% <ø> (-12.87%) ⬇️
utilities 36.55% <ø> (+<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 91.25% <100.00%> (+0.04%) ⬆️
...ain/java/org/apache/hudi/sink/utils/Pipelines.java 93.81% <100.00%> (+0.09%) ⬆️
...ink/bootstrap/PartitionedRLIBootstrapOperator.java 92.78% <92.78%> (ø)

... and 238 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 Sep 1, 2026
@hudi-bot

hudi-bot commented Sep 1, 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

@cshuo cshuo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thks for the contribution, left some comments.

* entirely, which is the expected fallback for non-temporal (non date-partitioned) tables.
*/
@Slf4j
public class PartitionedRLIBootstrapOperator

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

PartitionedRLIBootstrapOperator sounds like a general bootstrap operator, while this implementation only preloads RLI data for a configured time window. Could we rename it to make the scope and behavior clearer, like TimeBoundedRLIBootstrapOperator?

* Returns whether the table uses partitioned record level index served by the local RocksDB-based
* partitioned index cache, i.e. {@link FlinkOptions#INDEX_RLI_BACKEND_TYPE} is configured as {@code rocksdb}.
*/
public static boolean isPartitionedRLIWithRocksDBBackend(Configuration conf) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Selecting the RocksDB backend does not necessarily mean this bootstrap is applicable, since it only supports time-partitioned tables with a configured preload window.

Could we change INDEX_RLI_CACHE_ROCKSDB_BOOTSTRAP_DAYS to default to -1, and enable the preload only when the user configures a positive value? This predicate could then be renamed to something like isTimeBoundedRLIBootstrapEnabled and require the partitioned RLI type and BOOTSTRAP_DAYS > 0.

}

private void emitIndexRecord(String partitionPath, String recordKey, HoodieRecordGlobalLocation location) {
output.collect(new StreamRecord<>(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just a reminder that these index records are not currently handled by the downstream DynamicBucketAssignFunction, which treats them as normal data records. If the downstream handling is planned for a follow-up PR, please keep this preload disabled by default until the complete processing path is available.

return partitionsInWindow;
}

private LocalDate parsePartitionDate(String partitionPath, DateTimeFormatter formatter, boolean hiveStylePartitioning) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you check if there is any existing utility we can reuse to parse dates from partition paths?

* Uses round-robin assignment: file group i is assigned to task (i % parallelism).
*/
@VisibleForTesting
boolean shouldLoadBucket(int fileGroupIdx, int parallelism, int taskID) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This may cause a hotspot. Since fileGroupIdx restarts from zero for each partition, for e.g., if each partition has 4 RLI shards, all shards will always be loaded by subtasks 0–3, leaving the remaining subtasks idle.

The distribution should also account for the partition path; you can refer to the shuffle strategy used by the bucket index partitioner via BucketIndexUtil#getPartitionIndexFunc.

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]

5 participants