Skip to content

refactor(reader): Use reader context API for legacy scanner delete re… - #19804

Open
fhan688 wants to merge 1 commit into
apache:masterfrom
fhan688:use-reader-context-API-for-legacy-scanner-delete-records
Open

refactor(reader): Use reader context API for legacy scanner delete re…#19804
fhan688 wants to merge 1 commit into
apache:masterfrom
fhan688:use-reader-context-API-for-legacy-scanner-delete-records

Conversation

@fhan688

@fhan688 fhan688 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Describe the issue this Pull Request addresses

Closes #19196.

The legacy log record scanner reads delete blocks through the no-argument HoodieDeleteBlock#getRecordsToDelete() API. This bypasses HoodieReaderContext and prevents the scanner from correctly reading native delete log blocks, whose records must be materialized through the reader-context API.

Summary and Changelog

This PR refactors legacy log scanners to read delete records through HoodieReaderContext.

  • Lazily initializes an Avro HoodieReaderContext when the first delete block is processed.
  • Configures the reader context with table merge properties, instant range, schema handler, table path, and latest commit time.
  • Reads delete records through HoodieDeleteBlock#getRecordsToDelete(HoodieReaderContext).
  • Passes the physical partition path separately when processing BufferedRecord delete records.
  • Updates merged log scanning while preserving delete ordering semantics:
    • A delete with a higher ordering value wins.
    • A delete with a lower ordering value does not overwrite a newer data record.
  • Updates unmerged log scanning so deletion callbacks retain the correct physical partition path.
  • Deprecates the no-argument getRecordsToDelete() APIs while retaining them for compatibility and block serialization.
  • Adds coverage for:
    • Merged native data and native delete log scanning.
    • Delete ordering semantics.
    • Data-record metric preservation.
    • Unmerged legacy delete callbacks.
    • Mixed legacy data and native delete log scanning.

Impact

There are no storage-format, configuration, or user-facing behavior changes.

The existing no-argument delete-record APIs remain available but are now deprecated. Reader-context initialization is lazy and only occurs when a delete block is processed.

The protected delete-processing hook used by the legacy scanner implementations now operates on BufferedRecord and receives the physical partition path explicitly.

Risk Level

Low.

The change is scoped to delete-block processing in legacy merged and unmerged log scanners. Existing compatibility APIs and serialization paths are preserved.

The merged, unmerged, and mixed-format delete paths are covered by regression tests. Module compilation and Checkstyle validation also pass.

Documentation Update

None. This is an internal refactoring without new configuration or user-facing behavior.

Contributor's checklist

  • Read through contributor's guide
  • Enough context is provided in the sections above
  • Adequate tests were added if applicable
@github-actions github-actions Bot added the size:M PR with lines of changes in (100, 300] label Sep 1, 2026
@codecov-commenter

codecov-commenter commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 20.58824% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.27%. Comparing base (886ebc7) to head (fe52f87).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...mmon/table/log/AbstractHoodieLogRecordScanner.java 22.58% 24 Missing ⚠️
...common/table/log/HoodieMergedLogRecordScanner.java 0.00% 2 Missing ⚠️
...mmon/table/log/HoodieUnMergedLogRecordScanner.java 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master   #19804      +/-   ##
============================================
- Coverage     78.29%   78.27%   -0.02%     
- Complexity    33850    33855       +5     
============================================
  Files          2541     2541              
  Lines        141674   141704      +30     
  Branches      17172    17175       +3     
============================================
+ Hits         110917   110923       +6     
- Misses        23048    23069      +21     
- Partials       7709     7712       +3     
Components Coverage Δ
hudi-common 83.58% <20.58%> (-0.05%) ⬇️
hudi-client 83.16% <ø> (-0.01%) ⬇️
hudi-flink 85.65% <ø> (-0.01%) ⬇️
hudi-spark-datasource 73.18% <ø> (-0.01%) ⬇️
hudi-utilities 74.52% <ø> (+<0.01%) ⬆️
hudi-cli 15.06% <ø> (ø)
hudi-hadoop 70.71% <ø> (ø)
hudi-sync 75.56% <ø> (ø)
hudi-io 79.98% <ø> (ø)
hudi-timeline-service 83.44% <ø> (ø)
hudi-cloud 65.81% <ø> (ø)
hudi-kafka-connect 53.20% <ø> (ø)
Flag Coverage Δ
common-and-other-modules 51.45% <0.00%> (-0.02%) ⬇️
flink-integration-tests 48.80% <0.00%> (-0.03%) ⬇️
hadoop-mr-java-client 44.09% <20.58%> (+<0.01%) ⬆️
integration-tests 13.51% <0.00%> (-0.01%) ⬇️
spark-client-hadoop-common 50.49% <0.00%> (-0.02%) ⬇️
spark-java-tests 52.20% <5.88%> (+<0.01%) ⬆️
spark-scala-tests 46.84% <5.88%> (-0.02%) ⬇️
utilities 36.55% <0.00%> (+<0.01%) ⬆️

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

Files with missing lines Coverage Δ
...hudi/common/table/log/block/HoodieDeleteBlock.java 83.78% <ø> (ø)
...on/table/log/block/HoodieNativeLogDeleteBlock.java 85.41% <ø> (ø)
...mmon/table/log/HoodieUnMergedLogRecordScanner.java 65.38% <0.00%> (ø)
...common/table/log/HoodieMergedLogRecordScanner.java 53.50% <0.00%> (ø)
...mmon/table/log/AbstractHoodieLogRecordScanner.java 55.51% <22.58%> (-3.85%) ⬇️

... and 11 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-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 routes legacy log-scanner delete-block reading through HoodieReaderContext so native delete log blocks materialize correctly, lazily building an Avro reader context on the first delete block and threading the physical partition path through the delete callbacks. The tracing checks out (delete ordering semantics, the unchanged data-record metric, and the null-safe metaClient path all look correct); one semantic change around how the delete partition path is derived is worth a second look 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 naming nit on the implicit assumption baked into resolveDeletePartitionPath.

if (logFilePaths.isEmpty()) {
return Option.empty();
}
return Option.of(getRelativePartitionPath(

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 changes where the delete's partition path comes from: previously each DeleteRecord.getPartitionPath() was used per-record, now it's derived once from partitionNameOverride or the first log file's parent dir and applied to every delete in the block. For these single-file-group scanners that should always match, but I want to double-check — is there any path (e.g. a legacy delete block whose DeleteRecords carry a partition different from the physical log location) where the stored partition could now differ? @yihua does relying on the log file's physical partition for deletes hold across all read paths here?

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

return Option.empty();
}
return Option.of(getRelativePartitionPath(
new StoragePath(basePath), new StoragePath(logFilePaths.get(0)).getParent()));

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 add a brief inline comment here explaining why get(0) is safe — something like // all log files in a file group share the same parent partition directory? Without it a future reader might wonder whether this silently breaks for multi-directory log paths.

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

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

fhan688 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

please help review, thanks! @cshuo

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

Labels

size:M PR with lines of changes in (100, 300]

4 participants