Skip to content

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

Merged
danny0405 merged 6 commits into
apache:masterfrom
HuangZhenQiu:partitioned-rli-bootstrap
Sep 3, 2026
Merged

feat(flink): add partitioned rli boostrap operator#19805
danny0405 merged 6 commits into
apache:masterfrom
HuangZhenQiu:partitioned-rli-bootstrap

Conversation

@HuangZhenQiu

@HuangZhenQiu HuangZhenQiu commented Sep 1, 2026

Copy link
Copy Markdown
Member

Describe the issue this Pull Request addresses

add partitioned rli boostrap operator to support

closes #19603

Summary and Changelog

  1. Added AbstractRLIBoostrapOperator for common fields and methods.
  2. Added the new ParititionedRLIBoostrapOperator to load the record index for last x days defined in configuration
  3. Wired the new operator in Pipelines safe guarded by configuration
  4. 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 68.75000% with 35 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.31%. Comparing base (1e59c9e) to head (191382c).
⚠️ Report is 8 commits behind head on master.

Files with missing lines Patch % Lines
...ink/bootstrap/TimeBoundedRLIBootstrapOperator.java 52.17% 32 Missing and 1 partial ⚠️
...i/sink/bootstrap/AbstractRLIBootstrapOperator.java 90.90% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             master   #19805    +/-   ##
==========================================
  Coverage     78.30%   78.31%            
- Complexity    33871    33925    +54     
==========================================
  Files          2541     2545     +4     
  Lines        141708   141869   +161     
  Branches      17177    17269    +92     
==========================================
+ Hits         110961   111098   +137     
- Misses        23038    23065    +27     
+ Partials       7709     7706     -3     
Components Coverage Δ
hudi-common 83.63% <ø> (ø)
hudi-client 83.22% <ø> (+0.02%) ⬆️
hudi-flink 85.56% <68.75%> (-0.10%) ⬇️
hudi-spark-datasource 73.24% <ø> (+0.05%) ⬆️
hudi-utilities 74.56% <ø> (+0.03%) ⬆️
hudi-cli 15.13% <ø> (+0.06%) ⬆️
hudi-hadoop 70.73% <ø> (+0.02%) ⬆️
hudi-sync 75.56% <ø> (+0.02%) ⬆️
hudi-io 80.02% <ø> (+0.14%) ⬆️
hudi-timeline-service 83.44% <ø> (ø)
hudi-cloud 65.81% <ø> (ø)
hudi-kafka-connect 53.20% <ø> (-0.77%) ⬇️
Flag Coverage Δ
common-and-other-modules 51.46% <66.96%> (+<0.01%) ⬆️
flink-integration-tests 48.83% <68.75%> (+0.02%) ⬆️
hadoop-mr-java-client 44.06% <ø> (-0.02%) ⬇️
integration-tests 13.50% <0.00%> (-0.02%) ⬇️
spark-client-hadoop-common 50.55% <ø> (+<0.01%) ⬆️
spark-java-tests 52.24% <ø> (+0.05%) ⬆️
spark-scala-tests 46.97% <ø> (+0.12%) ⬆️
utilities 36.56% <ø> (+0.01%) ⬆️

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

Files with missing lines Coverage Δ
...va/org/apache/hudi/configuration/FlinkOptions.java 99.79% <100.00%> (ø)
...org/apache/hudi/configuration/OptionsResolver.java 91.35% <100.00%> (+0.14%) ⬆️
.../hudi/sink/bootstrap/BootstrapOperatorFactory.java 100.00% <100.00%> (ø)
...ache/hudi/sink/bootstrap/RLIBootstrapOperator.java 100.00% <100.00%> (+3.70%) ⬆️
...ain/java/org/apache/hudi/sink/utils/Pipelines.java 93.72% <100.00%> (ø)
...java/org/apache/hudi/table/HoodieTableFactory.java 85.54% <ø> (-0.06%) ⬇️
...c/main/java/org/apache/hudi/util/StreamerUtil.java 81.40% <100.00%> (+0.43%) ⬆️
...i/sink/bootstrap/AbstractRLIBootstrapOperator.java 90.90% <90.90%> (ø)
...ink/bootstrap/TimeBoundedRLIBootstrapOperator.java 52.17% <52.17%> (ø)

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

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

@HuangZhenQiu

Copy link
Copy Markdown
Member Author

@cshuo
Thanks for these valuable comments. Resolved most of them. The helper function of partition date parse is moved StreamerUtil. Please take a look when you have time.

@HuangZhenQiu
HuangZhenQiu force-pushed the partitioned-rli-bootstrap branch from 683285e to 13a81de Compare September 1, 2026 19:58

@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 TimeBoundedRLIBootstrapOperator that preloads the partitioned record-level index for a configurable recent-day window, wires it into the Flink bootstrap pipeline behind the rocksdb RLI backend, and adds a parsePartitionDate helper. The operator's window math, per-task file-group assignment, and partition-path handling all check out against the existing RLI paths. One thing to double-check in the inline comments: the new -1 default for index.rli.cache.rocksdb.bootstrap.days appears to make two of the newly added tests fail, since they assert the feature is enabled without setting a positive day window. 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. A couple of small doc/readability suggestions below, otherwise the code is clean and well-tested.

assertFalse(OptionsResolver.isTimeBoundedRLIBootstrapEnabled(conf));

conf.set(FlinkOptions.INDEX_RLI_BACKEND_TYPE, "rocksdb");
assertTrue(OptionsResolver.isTimeBoundedRLIBootstrapEnabled(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.

🤖 With index.rli.cache.rocksdb.bootstrap.days now defaulting to -1, isTimeBoundedRLIBootstrapEnabled requires days > 0, but this test never sets it — so conf.get(...) returns -1 and this assertTrue would actually fail. Could you conf.set(FlinkOptions.INDEX_RLI_CACHE_ROCKSDB_BOOTSTRAP_DAYS, <positive>) before asserting enabled?

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

Pipelines.bootstrap(conf, TestConfigurations.ROW_TYPE, input, false, false);

assertEquals("index_bootstrap", streaming.getTransformation().getName());
assertInstanceOf(TimeBoundedRLIBootstrapOperator.class, bootstrapOperator(streaming));

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.

🤖 Same root cause as the OptionsResolver test: defaultConf() doesn't set index.rli.cache.rocksdb.bootstrap.days, so it defaults to -1, isTimeBoundedRLIBootstrapEnabled returns false, and streamBootstrap selects BootstrapOperator instead — so this assertInstanceOf(TimeBoundedRLIBootstrapOperator.class, ...) would fail. Setting a positive bootstrap-days on conf should fix it.

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

private int parallelism;
private int taskID;
/**
* Functions for calculating the task partition to dispatch.

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 javadoc says setting this to 0 disables preloading, but the default is now -1 and the check is <= 0. Could you reword to say any non-positive value disables it to match the code?

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

preLoadPartition(partitionPath, partitionedFileGroups.get(partitionPath), taskID, parallelism);
}
long costMs = System.currentTimeMillis() - startTime;
log.info("Finish preloading partitioned RLI records, total records: {}, cost: {} ms, taskId = {}", loadedCnt, costMs, taskID);

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: preLoadPartition takes taskID and parallelism as parameters, but both are already instance fields set in initializeState. Could you drop the params and read the fields directly to avoid the confusion of two sources of truth?

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

@HuangZhenQiu
HuangZhenQiu force-pushed the partitioned-rli-bootstrap branch from 13a81de to 5928cf0 Compare September 2, 2026 00:52

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

lgtm, only one minor comment.

@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 TimeBoundedRLIBootstrapOperator that pre-warms the partitioned record-level index for partitions within a configurable day window, wires it into the Flink write pipeline behind the rocksdb RLI backend config, and flips the bootstrap-days default to -1 (off). I traced the record-emission coverage/disjointness (each RLI file slice is owned by exactly one subtask via getPartitionIndexFunc, so no duplicate/dropped index records), the waitForBootstrapReady barrier paths, the config default change, and the date-window filtering — no new correctness issues surfaced in this round. The most significant open concern (the emitted index records flowing into DynamicBucketAssignFunction, which doesn't check isIndexRecord() and would treat them as null-rowData data records) has already been raised by @cshuo and @danny0405 and remains the key thing to resolve. 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. A few naming and simplification suggestions below.

private transient HoodieBackedTableMetadata tableMetadata;
private transient long loadedCnt;
private int parallelism;
private int taskID;

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 spell this out as loadedCount? loadedCnt is a small abbreviation but it reads slightly awkwardly next to the surrounding unabbreviated field names.

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


private void preLoadPartition(String partitionPath, List<FileSlice> fileSlices, int taskID, int parallelism) {
List<FileSlice> filteredFileSlices = new ArrayList<>();
for (int i = 0; i < fileSlices.size(); i++) {

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: parallelism is passed in here but never used inside the method body — might be worth dropping it from the signature and letting shouldLoadBucket and the log statement read off the captured taskID only.

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

OptionsResolver.getConflictResolutionStrategy(conf));
}

@Test

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: testPartitionedRLIWithRocksDBBackend only exercises cases where the method returns false — it might be worth adding one assertTrue with INDEX_TYPE=RECORD_LEVEL_INDEX, INDEX_RLI_BACKEND_TYPE=rocksdb, and INDEX_RLI_CACHE_ROCKSDB_BOOTSTRAP_DAYS > 0 to confirm the happy path.

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

@HuangZhenQiu

Copy link
Copy Markdown
Member Author

@cshuo @danny0405
Thanks for valuable feedback.

@hudi-bot

hudi-bot commented Sep 3, 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 d9e08f0 into apache:master Sep 3, 2026
25 checks passed
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.

Partitioned RLI time-bounded bootstrap

6 participants