Skip to content

perf(index): reuse driver-resolved base files in the bloom key check - #20263

Draft
yihua wants to merge 1 commit into
apache:masterfrom
yihua:perf-bloom-index-carry-base-file
Draft

yihua wants to merge 1 commit into
apache:masterfrom
yihua:perf-bloom-index-carry-base-file

Conversation

@yihua

@yihua yihua commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Describe the issue this Pull Request addresses

closes #20262
part of #20257

The bloom index key check resolves each candidate base file through the file system view twice per handle, which on executors means two timeline-server getLatestBaseFile requests per (task, candidate base file), although the driver already resolved those base files when it loaded the key ranges. The bloom, key-check and SIMPLE index reads also look up each parquet file's status before opening it, although its size is known.

The two lookups per handle date from #726 (0.5.0), which resolved the base file through the table view in both the HoodieKeyLookupHandle constructor and getLookupResult; they became timeline-server requests when the remote view became the default (#1634, 0.5.3). #6793 (HUDI-4917, 0.13.0) already applied "the driver has the base file" to the range-info stage but not to the key check.

Summary and Changelog

The key-check stage now reads the base files the driver resolved instead of asking the view for them, and parquet footer and key reads skip the status lookup when the file length is known.

  • BloomIndexFileInfo carries the resolved HoodieBaseFile (not part of equality); the loaders set it, and both bloom index helpers pass a file-group-to-base-file lookup (a broadcast on Spark) to HoodieBloomIndexCheckFunction. ListBasedIndexFileFilter keeps only the key ranges, so the base files are not shipped with the record-to-file pairing tasks.
  • HoodieKeyLookupHandle takes an optional base file and falls back to a single view lookup when it is absent.
  • FileFormatUtils gains StoragePathInfo overloads for footer, bloom filter, min/max key, key filter and key iterator reads; ParquetUtils opens the file from its known length (with the read options of withHadoopReadOptions), and the Avro and Spark parquet readers and factories use them. BaseFile#getPathInfoWithKnownSize feeds them from the read handle, HoodieIndexUtils.filterKeysFromFile and HoodieKeyLocationFetchHandle. HoodieFileReaderFactory.getFileReader(config, pathInfo, PARQUET, ...) now builds the known-length reader for its existing callers too (e.g. HoodieAvroReaderContext); only the footer, bloom filter and key reads use the length.
  • Tests: TestHoodieBloomIndex#testKeyCheckReadsResolvedBaseFiles (BLOOM and GLOBAL_BLOOM, 9 helper and metadata configurations) asserts no timeline-server base-file request and no parquet status call during tagging, and that locations match the SIMPLE index; it fails on master. TestHoodieKeyLocationFetchHandle asserts the status calls, and TestParquetUtils covers the new overloads.

Impact

Fewer executor round trips for BLOOM, GLOBAL_BLOOM and SIMPLE index lookups, with no change to results. On a local Spark harness, executor timeline-server requests from the key check go from 64 to 0 (256 to 0 at 4x file groups per partition), and executor base-file status calls go from 152 to 80 (352 to 80 at 4x file groups); the key check makes none during tagging, and the remaining 80 come from other paths (column stats footer reads, the merge handle and write-stat sizes). On S3A, fs.open() still issues a HEAD because parquet's HadoopInputFile.newStream does not pass the status, so S3A HEADs per candidate file drop from 2 to 1. HoodieKeyLookupHandle, HoodieBloomIndexCheckFunction and HoodieSparkBloomIndexCheckFunction constructors change (internal classes). This overlaps #20095 in HoodieKeyLocationFetchHandle.fetchRecordKeysWithPositions, a trivial conflict whichever lands second.

Risk Level

low: the base file comes from the same driver-side view the range stage already uses, and a wrong recorded file size fails the footer read loudly rather than returning wrong data.

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

The bloom index key check resolved each candidate base file through the table view twice per task (two timeline-server getLatestBaseFile requests on executors), and the bloom, SIMPLE and key-check reads stat every base file before opening it although its length is already known. The driver-resolved base files now ride along with BloomIndexFileInfo to the key-check stage, and the footer, bloom filter and key reads open parquet files from their known length. TestHoodieBloomIndex#testKeyCheckReadsResolvedBaseFiles and TestHoodieKeyLocationFetchHandle assert zero timeline-server base-file requests and no extra file-status calls, and TestParquetUtils covers the length-aware overloads.
@codecov-commenter

codecov-commenter commented Oct 9, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.94574% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.77%. Comparing base (308f78e) to head (0f57dac).

Files with missing lines Patch % Lines
...ache/hudi/io/storage/HoodieSparkParquetReader.java 36.36% 7 Missing ⚠️
.../hudi/core/io/storage/HoodieFileReaderFactory.java 40.00% 2 Missing and 1 partial ⚠️
...g/apache/hudi/io/HoodieKeyLocationFetchHandle.java 60.00% 1 Missing and 1 partial ⚠️
...main/java/org/apache/hudi/io/HoodieReadHandle.java 66.66% 1 Missing and 1 partial ⚠️
.../hudi/io/storage/HoodieSparkFileReaderFactory.java 50.00% 2 Missing ⚠️
...a/org/apache/hudi/common/util/FileFormatUtils.java 83.33% 2 Missing ⚠️
...java/org/apache/hudi/common/util/ParquetUtils.java 89.47% 2 Missing ⚠️
...in/java/org/apache/hudi/common/model/BaseFile.java 66.66% 0 Missing and 1 partial ⚠️
...udi/io/storage/hadoop/HoodieAvroParquetReader.java 90.90% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master   #20263      +/-   ##
============================================
- Coverage     80.62%   78.77%   -1.86%     
+ Complexity    35055    34297     -758     
============================================
  Files          2552     2552              
  Lines        143400   143476      +76     
  Branches      17454    17458       +4     
============================================
- Hits         115615   113019    -2596     
- Misses        19867    22420    +2553     
- Partials       7918     8037     +119     
Components Coverage Δ
hudi-common 83.16% <70.00%> (-1.01%) ⬇️
hudi-client 80.89% <83.33%> (-2.93%) ⬇️
hudi-flink 85.97% <ø> (+<0.01%) ⬆️
hudi-spark-datasource 68.28% <ø> (-5.78%) ⬇️
hudi-utilities 78.14% <ø> (-0.05%) ⬇️
hudi-cli 70.80% <ø> (ø)
hudi-hadoop 70.01% <90.32%> (-1.10%) ⬇️
hudi-sync 76.25% <ø> (+0.07%) ⬆️
hudi-io 81.38% <ø> (-0.05%) ⬇️
hudi-timeline-service 83.41% <ø> (ø)
hudi-cloud 81.00% <ø> (ø)
hudi-kafka-connect 53.20% <ø> (ø)
Flag Coverage Δ
common-and-other-modules 52.42% <71.31%> (+<0.01%) ⬆️
flink-integration-tests 49.51% <11.42%> (-0.03%) ⬇️
hadoop-mr-java-client 43.97% <66.66%> (-0.01%) ⬇️
integration-tests 13.46% <42.63%> (+<0.01%) ⬆️
spark-client-hadoop-common 38.74% <82.17%> (+0.05%) ⬆️
spark-java-tests 41.87% <60.46%> (-10.81%) ⬇️
spark-scala-tests 47.51% <58.13%> (+<0.01%) ⬆️
utilities 36.88% <24.80%> (-0.01%) ⬇️

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

Files with missing lines Coverage Δ
...n/java/org/apache/hudi/index/HoodieIndexUtils.java 92.11% <100.00%> (-0.64%) ⬇️
...e/hudi/index/bloom/BaseHoodieBloomIndexHelper.java 100.00% <100.00%> (ø)
...rg/apache/hudi/index/bloom/BloomIndexFileInfo.java 94.44% <100.00%> (+2.13%) ⬆️
.../org/apache/hudi/index/bloom/HoodieBloomIndex.java 94.11% <100.00%> (-0.12%) ⬇️
...udi/index/bloom/HoodieBloomIndexCheckFunction.java 89.18% <100.00%> (+0.61%) ⬆️
...i/index/bloom/ListBasedHoodieBloomIndexHelper.java 100.00% <100.00%> (ø)
...che/hudi/index/bloom/ListBasedIndexFileFilter.java 100.00% <100.00%> (ø)
...java/org/apache/hudi/io/HoodieKeyLookupHandle.java 84.84% <100.00%> (ø)
...he/hudi/index/bloom/HoodieFileProbingFunction.java 80.85% <100.00%> (ø)
.../hudi/index/bloom/SparkHoodieBloomIndexHelper.java 90.26% <100.00%> (-0.57%) ⬇️
... and 10 more

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

This branch has not been deployed

No deployments
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.

Bloom index key check asks the timeline server for each candidate base file twice per task

2 participants