Skip to content

fix: close the outer file stream of inline reads - #19936

Merged
danny0405 merged 1 commit into
apache:masterfrom
voonhous:fix-inline-stream-close
Sep 14, 2026
Merged

danny0405 merged 1 commit into
apache:masterfrom
voonhous:fix-inline-stream-close

Conversation

@voonhous

Copy link
Copy Markdown
Member

Describe the issue this Pull Request addresses

InLineFsDataInputStream reads an HFile embedded in a log file. It wraps the outer file's FSDataInputStream in an offset-adjusting InputStream and passes that to its super constructor. Neither class overrides close(), so closing an inline stream ends at InputStream.close(), a no-op, and the outer file's stream stays open until garbage collection. Every inline HFile read of a metadata table log block leaks a file handle this way.

Found by TestStreamingSource on #19932, where Spark's DebugFilesystem reported the outer streams as leaked once it was the JVM's cached local filesystem. On master the check never sees Hudi's reads, because the SQL suites cache a plain LocalFileSystem first.

Summary and Changelog

  • InLineFsDataInputStream.close() closes the wrapped stream and then the outer stream.
  • Unit test in TestInLineFileSystem that wraps the outer stream in a counting FSDataInputStream and asserts it is closed exactly once.

Impact

Metadata table reads through the inline filesystem release their file handles when the reader closes instead of at GC.

Risk Level

low. One close override; the outer stream is owned by this object.

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

InLineFsDataInputStream wraps the outer file's FSDataInputStream in
an offset-adjusting InputStream and passes that to its super
constructor. Neither class overrides close(), so closing an inline
stream ends at InputStream.close(), a no-op, and the outer file's
stream stays open until garbage collection. Every inline HFile
read (metadata table log blocks) leaked a file handle this way.
Spark's DebugFilesystem, which tracks the streams it hands out,
reports it in TestStreamingSource as "possibly leaked file
streams" whenever it is the cached local filesystem of the JVM.

close() now closes the wrapped stream and then the outer one. A
unit test checks the outer stream is closed exactly once.
@github-actions github-actions Bot added the size:S PR with lines of changes in (10, 100] label Sep 13, 2026
@codecov-commenter

codecov-commenter commented Sep 13, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.22%. Comparing base (1f5a4fe) to head (7f7c981).

Additional details and impacted files
@@             Coverage Diff              @@
##             master   #19936      +/-   ##
============================================
- Coverage     80.23%   80.22%   -0.01%     
- Complexity    34747    34748       +1     
============================================
  Files          2546     2546              
  Lines        142608   142611       +3     
  Branches      17362    17362              
============================================
- Hits         114419   114411       -8     
- Misses        20277    20289      +12     
+ Partials       7912     7911       -1     
Components Coverage Δ
hudi-common 83.86% <ø> (+0.01%) ⬆️
hudi-client 83.33% <ø> (-0.01%) ⬇️
hudi-flink 85.56% <ø> (-0.01%) ⬇️
hudi-spark-datasource 73.26% <ø> (+<0.01%) ⬆️
hudi-utilities 78.15% <ø> (-0.05%) ⬇️
hudi-cli 69.99% <ø> (ø)
hudi-hadoop 70.89% <100.00%> (-0.01%) ⬇️
hudi-sync 76.02% <ø> (ø)
hudi-io 81.52% <ø> (-0.10%) ⬇️
hudi-timeline-service 83.42% <ø> (+0.08%) ⬆️
hudi-cloud 80.99% <ø> (ø)
hudi-kafka-connect 53.20% <ø> (-0.77%) ⬇️
Flag Coverage Δ
common-and-other-modules 52.04% <100.00%> (-0.01%) ⬇️
flink-integration-tests 49.04% <100.00%> (-0.01%) ⬇️
hadoop-mr-java-client 43.87% <100.00%> (-0.03%) ⬇️
integration-tests 13.45% <0.00%> (-0.01%) ⬇️
spark-client-hadoop-common 38.53% <100.00%> (+<0.01%) ⬆️
spark-java-tests 52.23% <100.00%> (+<0.01%) ⬆️
spark-scala-tests 46.90% <100.00%> (+<0.01%) ⬆️
utilities 36.81% <100.00%> (-0.01%) ⬇️

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

Files with missing lines Coverage Δ
...hudi/hadoop/fs/inline/InLineFsDataInputStream.java 57.14% <100.00%> (+2.42%) ⬆️

... and 22 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 the contribution! This PR overrides close() in InLineFsDataInputStream so that closing an inline stream also closes the wrapped outer FSDataInputStream, fixing a file-handle leak on every inline HFile read from a log block. I verified that the only production caller (InLineFileSystem.open) creates a dedicated outer stream per inline stream, so the ownership assumption behind the override is sound, and the try/finally ensures the outer handle is released even if the wrapper's close throws. No 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

@danny0405
danny0405 merged commit 1d74ee5 into apache:master Sep 14, 2026
23 checks passed
voonhous added a commit to voonhous/hudi that referenced this pull request Sep 14, 2026
InLineFsDataInputStream wraps the outer file's FSDataInputStream in
an offset-adjusting InputStream and passes that to its super
constructor. Neither class overrides close(), so closing an inline
stream ends at InputStream.close(), a no-op, and the outer file's
stream stays open until garbage collection. Every inline HFile
read (metadata table log blocks) leaked a file handle this way.
Spark's DebugFilesystem, which tracks the streams it hands out,
reports it in TestStreamingSource as "possibly leaked file
streams" whenever it is the cached local filesystem of the JVM.

close() now closes the wrapped stream and then the outer one. A
unit test checks the outer stream is closed exactly once.

(cherry picked from commit 1d74ee5)
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.

5 participants