Skip to content

refactor(reader): seal the shared merge path in KeyBasedFileGroupRecordBuffer - #19415

Merged
voonhous merged 2 commits into
apache:masterfrom
rangareddy:fix-16920-seal-key-based-record-buffer
Jul 31, 2026
Merged

refactor(reader): seal the shared merge path in KeyBasedFileGroupRecordBuffer#19415
voonhous merged 2 commits into
apache:masterfrom
rangareddy:fix-16920-seal-key-based-record-buffer

Conversation

@rangareddy

Copy link
Copy Markdown
Collaborator

Describe the issue this Pull Request addresses

Closes #16920 (HUDI-9200).

KeyBasedFileGroupRecordBuffer is subclassed by PositionBasedFileGroupRecordBuffer and
SortedKeyBasedFileGroupRecordBuffer. Both legitimately specialize how log blocks are consumed and
how the base-file iterator is advanced, but the step that actually mutates the buffer — merge the
incoming record against what is already buffered, then store the result — must behave identically for
every buffer in the hierarchy. Today nothing expresses that, so a subclass can silently replace it.

Summary and Changelog

Marks the two methods that no subclass overrides as final, so the shared merge path cannot be
replaced by a subclass, and documents why:

  • KeyBasedFileGroupRecordBuffer#processNextDataRecordfinal. Subclasses still choose the
    identifier a record is buffered under (a record key here, a record position in
    PositionBasedFileGroupRecordBuffer), which is the parameter, not the behavior.
  • KeyBasedFileGroupRecordBuffer#isPartialMergingEnabledfinal. The enablePartialMerging flag is
    owned and toggled by the base buffer while processing data blocks.
  • Adds TestKeyBasedFileGroupRecordBuffer#keyMergeBehaviorIsSealedAgainstSubclasses, which asserts
    both modifiers via reflection. final is compiler-enforced, but nothing otherwise stops the keyword
    from being dropped later; the test was confirmed to fail for each method independently before the
    change.

The remaining methods (getBufferType, processDataBlock, processDeleteBlock, containsLogRecord,
hasNextBaseRecord, doHasNext) are deliberately left open — each is overridden by
PositionBasedFileGroupRecordBuffer and/or SortedKeyBasedFileGroupRecordBuffer today.

Note on scope: the ticket is phrased as preventing PositionBasedFileGroupRecordBuffer from overriding
key functions, and PositionBased does not override either method sealed here. If the intent was
instead to refactor PositionBased so it stops overriding the block-processing methods (each of which
currently opens with a if (!getShouldMergeUseRecordPosition()) { super.…; return; } fallback) and then
seal those, that is a larger design change and I'm happy to defer to the ticket's assignee on it. This
PR is the behavior-preserving subset.

Impact

None for users. final is enforced at compile time and these are internal reader classes — nothing in
org.apache.hudi.common.table.read.buffer is annotated @PublicAPIClass. No public API, config, or
on-disk format change. Making a public method final is binary-compatible; it would only affect an
out-of-tree subclass that overrides these methods, and no in-tree subclass, test, or mock does.

No performance claim is made: these call sites are already monomorphic and devirtualized by the JIT.

Risk Level

none

Documentation Update

none — no new config, no default value change, no user-facing behavior change.

Contributor's checklist

  • Read through contributor's guide
  • Enough context is provided in the sections above
  • Adequate tests were added if applicable
  • CI passes on my PR

…rdBuffer

Mark processNextDataRecord and isPartialMergingEnabled as final so that
subclasses cannot replace the merge-then-store behavior shared by every
buffer in this hierarchy. Subclasses still choose the identifier a record
is buffered under, and still specialize block processing and base-record
advancement.

Adds a reflection-based regression test so the modifiers cannot be dropped
silently.

Closes apache#16920
@github-actions github-actions Bot added the size:S PR with lines of changes in (10, 100] label Jul 30, 2026

@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 marks processNextDataRecord and isPartialMergingEnabled in KeyBasedFileGroupRecordBuffer as final to seal the shared merge path against subclass overrides, and adds a reflection-based test to enforce it. No issues flagged from this automated pass — a Hudi committer or PMC member can take it from here for a final review.

cc @yihua

@codecov-commenter

codecov-commenter commented Jul 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.10%. Comparing base (45dcf1e) to head (4c46e5e).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master   #19415      +/-   ##
============================================
- Coverage     75.36%   75.10%   -0.26%     
+ Complexity    32552    32439     -113     
============================================
  Files          2574     2574              
  Lines        142994   142995       +1     
  Branches      17529    17535       +6     
============================================
- Hits         107762   107400     -362     
- Misses        27182    27605     +423     
+ Partials       8050     7990      -60     
Components Coverage Δ
hudi-common 82.25% <ø> (-0.03%) ⬇️
hudi-client 81.72% <ø> (-0.09%) ⬇️
hudi-flink 83.98% <ø> (+1.38%) ⬆️
hudi-spark-datasource 66.07% <ø> (-2.28%) ⬇️
hudi-utilities 71.17% <ø> (-0.04%) ⬇️
hudi-cli 15.32% <ø> (ø)
hudi-hadoop 63.46% <ø> (-0.03%) ⬇️
hudi-sync 70.72% <ø> (+0.05%) ⬆️
hudi-io 79.57% <ø> (ø)
hudi-timeline-service 83.44% <ø> (-0.10%) ⬇️
hudi-cloud 64.00% <ø> (ø)
hudi-kafka-connect 53.20% <ø> (ø)
Flag Coverage Δ
common-and-other-modules 49.22% <ø> (+0.32%) ⬆️
flink-integration-tests 48.82% <ø> (+0.30%) ⬆️
hadoop-mr-java-client 43.38% <ø> (-0.02%) ⬇️
integration-tests 13.63% <ø> (+<0.01%) ⬆️
spark-client-hadoop-common 48.71% <ø> (+<0.01%) ⬆️
spark-java-tests 50.53% <ø> (-0.86%) ⬇️
spark-scala-tests 45.76% <ø> (-0.33%) ⬇️
utilities 36.65% <ø> (-0.01%) ⬇️

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

Files with missing lines Coverage Δ
...ble/read/buffer/KeyBasedFileGroupRecordBuffer.java 94.59% <ø> (ø)

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

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

…arantees

Review feedback: the test name, javadoc and failure message claimed every
buffer funnels records through a single merge-then-put path. That is not true
and the test did not check it. PositionBasedFileGroupRecordBuffer writes to
the protected records map directly when it re-keys entries in
fallbackToKeyBasedBuffer and when it overwrites delete markers under
COMMIT_TIME_ORDERING, and subclasses can still override block processing.

Narrow the claim instead of inventing the invariant: those two paths must not
merge, so routing them through processNextDataRecord would change behaviour.
Renamed the test to sealedMethodsCannotBeOverridden, reworded its javadoc and
message to the exact guarantee (the two methods keep their final modifier),
and corrected the same overstatement in the production javadoc, including that
a subclass may still set enablePartialMerging while processing a data block.

No behaviour change.

@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 seals processNextDataRecord and isPartialMergingEnabled in KeyBasedFileGroupRecordBuffer with final and adds a reflection test to guard the modifier. I verified the hierarchy — only PositionBasedFileGroupRecordBuffer and SortedKeyBasedFileGroupRecordBuffer extend the class, neither overrides either method, and the external isPartialMergingEnabled caller in CDCFileGroupIterator is unaffected, so this is behavior-preserving. No new issues flagged from this automated pass — a Hudi committer or PMC member can take it from here for a final review.

cc @yihua

@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

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

+1

@voonhous
voonhous merged commit 29b42cb into apache:master Jul 31, 2026
143 of 144 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S PR with lines of changes in (10, 100]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make some functions final in KeyBasedFileGroupRecordBuffer

7 participants