Skip to content

fix(fs): stop depending on the optional FileSystem#getScheme() - #19470

Merged
voonhous merged 3 commits into
apache:masterfrom
rangareddy:fix-15331-getscheme-unsupported
Aug 3, 2026
Merged

fix(fs): stop depending on the optional FileSystem#getScheme()#19470
voonhous merged 3 commits into
apache:masterfrom
rangareddy:fix-15331-getscheme-unsupported

Conversation

@rangareddy

@rangareddy rangareddy commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Describe the issue this Pull Request addresses

Closes #15331 (HUDI-4602).

A MOR _rt query through Presto fails before reading anything:

java.lang.UnsupportedOperationException: Not implemented by the PrestoS3FileSystem FileSystem implementation
	at org.apache.hadoop.fs.FileSystem.getScheme(FileSystem.java:219)
	at org.apache.hadoop.fs.HadoopExtendedFileSystem.getScheme(HadoopExtendedFileSystem.java:71)
	at org.apache.hudi.common.fs.FSUtils.isGCSFileSystem(FSUtils.java:592)
	at org.apache.hudi.common.table.log.HoodieLogFileReader.getFSDataInputStream(HoodieLogFileReader.java:119)
	...
	at org.apache.hudi.hadoop.realtime.HoodieParquetRealtimeInputFormat.getRecordReader

FileSystem#getScheme() is optional in Hadoop — FileSystem's own implementation throws
UnsupportedOperationException — and proxy implementations such as PrestoS3FileSystem do not override
it. Hudi called it unguarded, so a filesystem that declines to implement an optional API took down an
unrelated read. The issue title asks for getScheme to be implemented in PrestoS3FileSystem, which is
not a change Hudi can make; what Hudi can fix is the dependency on it, and that is the actual defect.

Still live on current master, just relocated from FSUtils to
HadoopFSUtils.isGCSFileSystem (line 280) reached from HadoopFSUtils.getFSDataInputStream (line 223).

There is precedent for the conclusion: #793 ("Allow HoodieWrapperFileSystem to wrap other proxy
file-system implementations with no getScheme implementation") already stopped HoodieWrapperFileSystem
calling getScheme() on the filesystem it wraps, deriving the scheme from the URI instead. That fix
covered only the instance methods, though. The static
HoodieWrapperFileSystem#convertToHoodiePath(StoragePath, Configuration) kept calling
getFs(...).getScheme() directly, and it is one of the lines this PR fixes, six years later.

Summary and Changelog

  • Adds HadoopFSUtils#getScheme(FileSystem): returns fs.getScheme(), falling back to
    fs.getUri().getScheme() when it is unimplemented, and failing with a HoodieException that chains the
    original UnsupportedOperationException when neither source yields a scheme. getUri() is abstract, so
    every implementation supplies one to fall back on, but the two are not interchangeable, which is why
    getScheme() is tried first: InLineFileSystem returns "inlinefs" from getScheme() while its
    getUri() is URI.create("inlinefs"), which has no colon and so carries no scheme at all. A URI with no
    scheme is a resolution failure rather than a value to pass on, since returning null would surface much
    later as does not support scheme null with the original failure discarded. Behaviour is unchanged for
    every filesystem that implements getScheme(); only the throwing case is new.

  • Routes the seven unguarded call sites through it:

    call site reached from
    HadoopFSUtils#isGCSFileSystem the reported crashgetFSDataInputStream, i.e. every log-file open
    HadoopFSUtils#isCHDFileSystem same method, same read path
    HadoopFSUtils#registerFileSystem
    HoodieWrapperFileSystem#convertToHoodiePath
    HoodieRetryWrapperFileSystem#getScheme delegates to the wrapped filesystem
    WriteMarkersFactory HDFS gate marker-type selection
    HoodieHadoopStorage#getScheme the HoodieStorage#getScheme entry point — 7 callers, including HoodieLogFileReader's isWriteTransactional check, HoodieLogFormatWriter, RollbackHelperV1 and FileSystemBasedLockProvider
  • isGCSFileSystem's comparison is flipped to put the constant first, matching its neighbour
    isCHDFileSystem, so a filesystem whose URI carries no scheme returns false instead of throwing
    NullPointerException.

Three further changes came out of review:

  • HoodieHadoopStorage memoizes the scheme. On a filesystem without getScheme() the fallback costs a
    thrown-and-caught exception, and this is called once per log block via
    StorageSchemes.isWriteTransactional and three times per immutable-file write via needCreateTempFile
    (measured at 321-1923 ns/call depending on stack depth). fs is final, so a Lazy field resolves it once
    without touching any of the five constructors.
  • HoodieWrapperFileSystem#convertToHoodiePath drops a dead try/catch that only caught
    HoodieIOException to rethrow it unchanged, dead since ef70de2bba7b.
  • TestFSUtilsWithRetryWrapperEnable#testGetSchema becomes a real guard. It has been inert since
    HUDI-5286 added it: it asserted on HoodieWrapperFileSystem#getScheme, which is uri.getScheme() and never
    dispatches into the retry wrapper, and FakeRemoteFileSystem overrode getScheme() to delegate to a real
    LocalFileSystem so it could not throw. Dropping that override gives the fake the PrestoS3FileSystem
    shape and the assertion now targets the retry wrapper, so it guards both HUDI-5286 and this change --
    verified by confirming it fails with the pre-PR helper.

Verification

Reproduced first, on the real code path, with no fabrication: FilterFileSystem is a Hadoop-provided
class with exactly the reported shape — it leaves getScheme() to the throwing base implementation while
overriding getUri(). Opening a file through HadoopFSUtils.getFSDataInputStream with one wrapped around
a local filesystem fails on master with the same exception and the same Hudi frames as the report:

java.lang.UnsupportedOperationException: Not implemented by the FilterFileSystem FileSystem implementation
	at org.apache.hudi.hadoop.fs.HadoopFSUtils.isGCSFileSystem(HadoopFSUtils.java:280)
	at org.apache.hudi.hadoop.fs.HadoopFSUtils.getFSDataInputStream(HadoopFSUtils.java:223)

Five tests in TestHadoopFSUtils, each run against a filesystem with the reported shape: getUri()
works, getScheme() throws.

  • testGetFSDataInputStreamWhenGetSchemeIsUnimplemented -- the read completes and returns the file's bytes.
  • testGetSchemeFallsBackToTheUriWhenUnimplemented -- the helper returns file both for an
    implementation that overrides getScheme() and for one that does not.
  • testGetSchemeFailsLoudlyWhenNeitherSourceHasOne -- a getUri() of URI.create("inlinefs") raises a
    HoodieException naming the filesystem, with the original UnsupportedOperationException chained as the
    cause rather than discarded.
  • testCallSitesWorkOnAFileSystemWithoutGetScheme -- one LocalFileSystem subclass whose getScheme()
    throws, registered as fs.file.impl, covering the three rerouted sites no test in the repo reached:
    registerFileSystem, convertToHoodiePath (the write path, via HoodieBaseParquetWriter and friends)
    and HoodieHadoopStorage#getScheme.
  • testSchemeSpecificStreamIsSelectedWithoutGetScheme -- with a URI of gs://bucket and ofs://cluster,
    getFSDataInputStream selects SchemeAwareFSDataInputStream and BoundedFsDataInputStream. This is the
    assertion that pins the helper returning what fs.getScheme() would have, rather than merely not
    throwing. Neither predicate had a test before.

TestFSUtilsWithRetryWrapperEnable#testGetSchema is retargeted at HoodieRetryWrapperFileSystem, and
FakeRemoteFileSystem's getScheme() override is deleted so the throwing base implementation is reached.
It previously asserted on HoodieWrapperFileSystem#getScheme(), which is return uri.getScheme() and
never dispatches into the retry wrapper, so it could not fail for HUDI-5286 either.

All are red with the guard removed and green with it, so none passes vacuously.

Regression runs:

suite result
hudi-hadoop-common full unit suite 1084 tests, 1 failure — TestHoodieActiveTimeline#testParseDateFromInstantTime, which fails identically on unmodified master (a 5.5-hour delta, i.e. the machine's local timezone)
TestWriteMarkersFactory, TestMarkerBasedRollbackUtils 10 pass
TestFlinkWriteClients (covers testMarkerType) 20 pass
TestHoodieLogFormatWriter, TestFSUtilsWithRetryWrapperEnable, TestInLineFileSystem, TestStorageSchemes, TestHadoopFSUtils 78 pass

checkstyle:check and apache-rat:check clean.

Impact

Restores MOR _rt reads on any filesystem that does not implement getScheme() — Presto's
PrestoS3FileSystem as reported, and any other proxy or vendor implementation with the same gap.
No behaviour change for filesystems that do implement it, since the helper calls it first.

For a scheme outside the StorageSchemes enum the failure moves rather than disappears:
HoodieLogFileReader:258 and RollbackHelperV1:190 throw IllegalArgumentException: Unsupported scheme
instead of the UnsupportedOperationException. That is not a regression, since both cases failed before,
and the reported s3 is in the enum, so the fix works end to end for the reported case.

Not addressed here: whether PrestoS3FileSystem should also implement getScheme(). It should, but that
is a change in Presto, and Hudi should not fall over on an optional API either way.

Risk Level

low -- one new helper plus seven call-site changes, all one-liners except HoodieHadoopStorage, where
the scheme is resolved once into a Lazy field because it is read once per log block. No change in
resolved scheme for any filesystem that implements getScheme(), and the previously-throwing paths are
covered by tests that were shown to fail without the change.

Documentation Update

none — no new config and no user-facing behaviour change beyond the reads that used to fail.

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

FileSystem#getScheme() is optional in Hadoop: the base implementation throws
UnsupportedOperationException, and proxy implementations such as Presto's
PrestoS3FileSystem do not override it. Hudi called it unguarded on filesystems it
did not implement, so opening a log file on such a filesystem failed with
"Not implemented by the PrestoS3FileSystem FileSystem implementation" instead of
reading anything (HUDI-4602).

Adds HadoopFSUtils#getScheme(FileSystem), which returns fs.getScheme() and falls
back to fs.getUri().getScheme() when it is unimplemented. getUri() is abstract, so
every implementation supplies it, and its scheme is what getScheme() returns
wherever both are present. This is the same conclusion as apache#793, which stopped
HoodieWrapperFileSystem calling getScheme() on the filesystem it wraps.

Routes the seven unguarded call sites through it: isGCSFileSystem and
isCHDFileSystem (the reported read path), registerFileSystem,
HoodieWrapperFileSystem#convertToHoodiePath, HoodieRetryWrapperFileSystem#getScheme,
WriteMarkersFactory's HDFS gate, and HoodieHadoopStorage#getScheme, which is what
the seven HoodieStorage#getScheme callers reach.

isGCSFileSystem's comparison is also flipped to put the constant first, matching
isCHDFileSystem, so a filesystem whose URI carries no scheme returns false rather
than throwing NullPointerException.
@github-actions github-actions Bot added the size:S PR with lines of changes in (10, 100] label Aug 3, 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 working on this! This PR stops Hudi from depending on Hadoop's optional FileSystem#getScheme() by adding HadoopFSUtils.getScheme(FileSystem), which falls back to getUri().getScheme() when getScheme() is unimplemented, fixing the _rt MOR read failure on proxy filesystems like PrestoS3FileSystem (HUDI-4602). I traced the helper, its null-return handling, and all callers, and the change looks correct and behavior-preserving on the normal path. No correctness issues found. A few style/readability suggestions in the inline comments. Please take a look, and this should be ready for a Hudi committer or PMC member to take it from here. One minor nit on the test assertion message; the production changes and overall structure look clean.

cc @yihua

Comment thread hudi-hadoop-common/src/test/java/org/apache/hudi/hadoop/fs/TestHadoopFSUtils.java Outdated
@codecov-commenter

codecov-commenter commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.98%. Comparing base (b65bc18) to head (5f96026).
⚠️ Report is 6 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master   #19470      +/-   ##
============================================
- Coverage     77.00%   76.98%   -0.02%     
+ Complexity    33873    33865       -8     
============================================
  Files          2576     2575       -1     
  Lines        143463   143386      -77     
  Branches      17589    17642      +53     
============================================
- Hits         110476   110390      -86     
- Misses        24716    24732      +16     
+ Partials       8271     8264       -7     
Components Coverage Δ
hudi-common 82.26% <ø> (-0.01%) ⬇️
hudi-client 81.83% <100.00%> (-0.28%) ⬇️
hudi-flink 84.03% <ø> (+0.09%) ⬆️
hudi-spark-datasource 75.09% <ø> (+0.11%) ⬆️
hudi-utilities 73.65% <ø> (-0.01%) ⬇️
hudi-cli 15.32% <ø> (ø)
hudi-hadoop 63.72% <100.00%> (+0.18%) ⬆️
hudi-sync 70.92% <ø> (+0.02%) ⬆️
hudi-io 79.60% <ø> (-0.10%) ⬇️
hudi-timeline-service 83.44% <ø> (-0.40%) ⬇️
hudi-cloud 64.00% <ø> (ø)
hudi-kafka-connect 53.20% <ø> (ø)
Flag Coverage Δ
common-and-other-modules 49.55% <58.82%> (+0.04%) ⬆️
flink-integration-tests 48.83% <58.82%> (+0.03%) ⬆️
hadoop-mr-java-client 43.76% <58.82%> (+0.34%) ⬆️
integration-tests 13.58% <47.05%> (+<0.01%) ⬆️
spark-client-hadoop-common 48.70% <100.00%> (+0.02%) ⬆️
spark-java-tests 51.32% <58.82%> (-0.09%) ⬇️
spark-scala-tests 47.40% <58.82%> (+<0.01%) ⬆️
utilities 36.58% <58.82%> (+<0.01%) ⬆️

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

Files with missing lines Coverage Δ
.../apache/hudi/table/marker/WriteMarkersFactory.java 86.95% <100.00%> (ø)
.../java/org/apache/hudi/hadoop/fs/HadoopFSUtils.java 64.13% <100.00%> (+4.80%) ⬆️
...e/hudi/hadoop/fs/HoodieRetryWrapperFileSystem.java 34.78% <100.00%> (+1.44%) ⬆️
...apache/hudi/hadoop/fs/HoodieWrapperFileSystem.java 38.10% <100.00%> (+0.23%) ⬆️
...pache/hudi/storage/hadoop/HoodieHadoopStorage.java 84.26% <100.00%> (+0.36%) ⬆️

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

Review nit: the assertion messages did not make clear what had gone wrong. Each
now names the filesystem and the branch of the helper it pins - LocalFileSystem
overriding getScheme() so the helper returns what it reports, FilterFileSystem not
overriding it so the helper falls back to getUri().getScheme().

@voonhous voonhous left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the change against the history of these files rather than just the diff. The production change is sound -- the try branch is byte-identical to the old code at all seven call sites, so the only behavioural deltas are the UOE fallback and the null-safe equals flip.

Things I checked and am explicitly not raising, so nobody re-derives them:

  • No accidental revert. #793 (83dab21ae131) is intact -- HoodieWrapperFileSystem#getScheme() still returns uri.getScheme() and the instance convertToHoodiePath/convertToDefaultPath still route through it. HUDI-5286's override is modified, not removed. Worth adding to the PR description: #793 fixed only the instance methods and left the static convertToHoodiePath(StoragePath, Configuration) calling getFs(...).getScheme() -- that is the line this PR finally fixes, six years later.
  • No prior attempt was reverted. git log -i --grep=getScheme returns only #793 and this PR; --grep=PrestoS3 and --grep="Not implemented by the" return nothing.
  • The fix is complete at the FileSystem level. The only raw fs.getScheme() left in main code is inside the new helper and FileSystem.getLocal(getConf()).getScheme() (HoodieWrapperFileSystem.java:991, and LocalFileSystem implements it). Every HoodieStorage#getScheme() consumer routes through the now-guarded HoodieHadoopStorage:113.
  • It reaches the reporter. packaging/hudi-presto-bundle/pom.xml:69 and packaging/hudi-hadoop-mr-bundle/pom.xml:69 both shade hudi-hadoop-common.
  • Case sensitivity is a non-issue. URI#getScheme() preserves case, but FileSystem.getFileSystemClass does a case-sensitive lookup, so S3A://bucket/x dies in Hadoop with UnsupportedFileSystemException before Hudi ever sees it.
  • Returning null from the Hadoop @Overrides is safe. The only Hadoop internal consuming an FS's own getScheme() is loadFileSystems(), and there is no META-INF/services/org.apache.hadoop.fs.FileSystem anywhere in the repo.
  • The equals flip is an improvement, not a revert -- e93c6a569310 wrote it the throwing way round; isCHDFileSystem has had the constant first since eaa2f8ed3bb3.

One thing worth a sentence in the PR body: for a scheme outside the StorageSchemes enum the failure moves rather than disappears -- HoodieLogFileReader.java:258 and RollbackHelperV1.java:190 will throw IllegalArgumentException: Unsupported scheme instead of the UOE. Not a regression (both cases failed before), and the reported s3 is in the enum, so the fix works end to end.

The substance of my review is inline: two coverage gaps I'd like closed before merge, one error-reporting fix, one perf nit, and four optional cleanliness nits.

Comment thread hudi-hadoop-common/src/test/java/org/apache/hudi/hadoop/fs/TestHadoopFSUtils.java Outdated
Comment thread hudi-hadoop-common/src/test/java/org/apache/hudi/hadoop/fs/TestHadoopFSUtils.java Outdated
Comment thread hudi-hadoop-common/src/test/java/org/apache/hudi/hadoop/fs/TestHadoopFSUtils.java Outdated

@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. The PR stops Hudi from calling the optional FileSystem getScheme unguarded by adding a helper that falls back to the URI scheme, fixing the Presto MOR read crash in HUDI-4602. I traced the helper and the reported read path and found no new correctness issue this round. The substantive open items were already raised in prior rounds. A Hudi committer or PMC member can take it from here for a final review.

…his reroutes

Review feedback, all of it well founded.

The fallback no longer returns null. InLineFileSystem is the counter-example in this
module: getScheme() is "inlinefs" while getUri() is URI.create("inlinefs"), which has no
colon and so no scheme, so the two are not interchangeable and the javadoc claim that
they agree was simply wrong. A null surfaced far from the cause as "does not support
scheme null" or "Unsupported scheme :null" with the UnsupportedOperationException
discarded; it now throws with that exception chained. HoodieException rather than
HoodieIOException, since the latter only accepts an IOException cause.

HoodieHadoopStorage memoizes the scheme. On a filesystem without getScheme() the
fallback costs a thrown-and-caught exception, and this is called once per log block via
StorageSchemes.isWriteTransactional and three times per immutable-file write via
needCreateTempFile. A lazy field keeps all five constructors untouched.

Test coverage for what this actually reroutes, none of which any test reached:

- registerFileSystem, HoodieWrapperFileSystem#convertToHoodiePath (the write path) and
  HoodieHadoopStorage#getScheme, via a LocalFileSystem subclass whose getScheme() throws,
  registered as fs.file.impl so it is reached through FileSystem.get.
- isGCSFileSystem and isCHDFileSystem, which become reachable for proxy filesystems for
  the first time here and select different stream wrappers: a scheme-less filesystem
  reporting gs:// now yields SchemeAwareFSDataInputStream and ofs:// yields
  BoundedFsDataInputStream.
- the new unresolvable-scheme failure.

TestFSUtilsWithRetryWrapperEnable#testGetSchema has been inert since HUDI-5286 added it:
it asserted on HoodieWrapperFileSystem#getScheme, which is uri.getScheme() and never
dispatches into the retry wrapper, and FakeRemoteFileSystem overrode getScheme() to
delegate to a real LocalFileSystem so it could not throw. Dropping that override gives
the fake the PrestoS3FileSystem shape and the assertion now targets the retry wrapper,
so it guards both HUDI-5286 and this change. Verified: it fails with the pre-PR helper.

Also drops the try/catch in convertToHoodiePath that only rethrew HoodieIOException
unchanged, dead since ef70de2, and the duplicated fixture and redundant nested
close in TestHadoopFSUtils.
@hudi-bot

hudi-bot commented Aug 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

@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! The PR stops Hudi from calling the optional FileSystem#getScheme() unguarded by adding HadoopFSUtils.getScheme(fs), which falls back to the URI scheme when getScheme() is unimplemented (the PrestoS3FileSystem/HUDI-4602 case), and reroutes the affected call sites. I traced the helper, the Lazy scheme memoization in HoodieHadoopStorage (thread-safe, fs is final), and the wrapper call sites for recursion and behavior regressions, and found no new correctness issues beyond what prior rounds already covered and the author addressed. No issues flagged from this automated pass — a Hudi committer or PMC member can take it from here for a final review.

cc @yihua

@voonhous

voonhous commented Aug 3, 2026

Copy link
Copy Markdown
Member

Went through the head commit and every inline thread is addressed, so I have resolved them. Thanks for the A/B on each one, that made them quick to check.

Three things left, all in the PR description rather than the code:

  1. This bullet in Summary and Changelog is now the false claim I asked you to delete from the javadoc:

    getUri() is abstract, so every implementation supplies it, and its scheme is what getScheme() returns wherever both are present.

    InLineFileSystem is the counter-example, and the helper now throws rather than returning null, so the bullet should say what the code says: getScheme() first, URI as a fallback, and a hard failure when neither yields a scheme.

  2. The two sentences from my earlier review are still missing:

    • Allow HoodieWrapperFileSystem to wrap other proxy file-system implementations with no getScheme implementation #793 fixed only the instance methods on HoodieWrapperFileSystem. The static convertToHoodiePath(StoragePath, Configuration) kept calling getFs(...).getScheme(), and that is the line this PR finally fixes. Worth saying, because it explains why the precedent you cite did not already cover this.
    • For a scheme outside the StorageSchemes enum the failure moves rather than disappears: HoodieLogFileReader:258 and RollbackHelperV1:190 throw IllegalArgumentException: Unsupported scheme instead of the UOE. Not a regression, and s3 is in the enum so the reported case works end to end, but a reader should not have to derive that.
  3. Verification still lists the two tests from the first round. There are five in TestHadoopFSUtils now, plus the retargeted TestFSUtilsWithRetryWrapperEnable#testGetSchema, and the last three are the ones carrying the weight.

No code changes needed from my side after that.

@voonhous

voonhous commented Aug 3, 2026

Copy link
Copy Markdown
Member

Applied these to the description myself, no action needed on your end -- the helper bullet now matches what the code does, the #793 and StorageSchemes notes are in, and Verification lists all five tests plus the retargeted testGetSchema. Also softened the Risk Level line, since HoodieHadoopStorage is no longer a one-liner after the memoization.

@github-actions github-actions Bot added size:M PR with lines of changes in (100, 300] and removed size:S PR with lines of changes in (10, 100] labels Aug 3, 2026
@voonhous
voonhous enabled auto-merge (squash) August 3, 2026 17:50
@voonhous
voonhous merged commit 70a5a4d into apache:master Aug 3, 2026
76 of 77 checks passed
voonhous pushed a commit that referenced this pull request Aug 6, 2026
* fix(fs): stop depending on the optional FileSystem#getScheme()

FileSystem#getScheme() is optional in Hadoop: the base implementation throws
UnsupportedOperationException, and proxy implementations such as Presto's
PrestoS3FileSystem do not override it. Hudi called it unguarded on filesystems it
did not implement, so opening a log file on such a filesystem failed with
"Not implemented by the PrestoS3FileSystem FileSystem implementation" instead of
reading anything (HUDI-4602).

Adds HadoopFSUtils#getScheme(FileSystem), which returns fs.getScheme() and falls
back to fs.getUri().getScheme() when it is unimplemented. getUri() is abstract, so
every implementation supplies it, and its scheme is what getScheme() returns
wherever both are present. This is the same conclusion as #793, which stopped
HoodieWrapperFileSystem calling getScheme() on the filesystem it wraps.

Routes the seven unguarded call sites through it: isGCSFileSystem and
isCHDFileSystem (the reported read path), registerFileSystem,
HoodieWrapperFileSystem#convertToHoodiePath, HoodieRetryWrapperFileSystem#getScheme,
WriteMarkersFactory's HDFS gate, and HoodieHadoopStorage#getScheme, which is what
the seven HoodieStorage#getScheme callers reach.

isGCSFileSystem's comparison is also flipped to put the constant first, matching
isCHDFileSystem, so a filesystem whose URI carries no scheme returns false rather
than throwing NullPointerException.

* test(fs): say which branch of the helper each assertion covers

Review nit: the assertion messages did not make clear what had gone wrong. Each
now names the filesystem and the branch of the helper it pins - LocalFileSystem
overriding getScheme() so the helper returns what it reports, FilterFileSystem not
overriding it so the helper falls back to getUri().getScheme().

* fix(fs): fail loudly on an unresolvable scheme, and cover the sites this reroutes

Review feedback, all of it well founded.

The fallback no longer returns null. InLineFileSystem is the counter-example in this
module: getScheme() is "inlinefs" while getUri() is URI.create("inlinefs"), which has no
colon and so no scheme, so the two are not interchangeable and the javadoc claim that
they agree was simply wrong. A null surfaced far from the cause as "does not support
scheme null" or "Unsupported scheme :null" with the UnsupportedOperationException
discarded; it now throws with that exception chained. HoodieException rather than
HoodieIOException, since the latter only accepts an IOException cause.

HoodieHadoopStorage memoizes the scheme. On a filesystem without getScheme() the
fallback costs a thrown-and-caught exception, and this is called once per log block via
StorageSchemes.isWriteTransactional and three times per immutable-file write via
needCreateTempFile. A lazy field keeps all five constructors untouched.

Test coverage for what this actually reroutes, none of which any test reached:

- registerFileSystem, HoodieWrapperFileSystem#convertToHoodiePath (the write path) and
  HoodieHadoopStorage#getScheme, via a LocalFileSystem subclass whose getScheme() throws,
  registered as fs.file.impl so it is reached through FileSystem.get.
- isGCSFileSystem and isCHDFileSystem, which become reachable for proxy filesystems for
  the first time here and select different stream wrappers: a scheme-less filesystem
  reporting gs:// now yields SchemeAwareFSDataInputStream and ofs:// yields
  BoundedFsDataInputStream.
- the new unresolvable-scheme failure.

TestFSUtilsWithRetryWrapperEnable#testGetSchema has been inert since HUDI-5286 added it:
it asserted on HoodieWrapperFileSystem#getScheme, which is uri.getScheme() and never
dispatches into the retry wrapper, and FakeRemoteFileSystem overrode getScheme() to
delegate to a real LocalFileSystem so it could not throw. Dropping that override gives
the fake the PrestoS3FileSystem shape and the assertion now targets the retry wrapper,
so it guards both HUDI-5286 and this change. Verified: it fails with the pre-PR helper.

Also drops the try/catch in convertToHoodiePath that only rethrew HoodieIOException
unchanged, dead since ef70de2, and the duplicated fixture and redundant nested
close in TestHadoopFSUtils.

(cherry picked from commit 70a5a4d)
voonhous added a commit that referenced this pull request Aug 6, 2026
…opStorage

The cherry-pick of #19470 (b8a09bf) brought master's
org.apache.hudi.common.util.Lazy import, but the hudi-common core package
reorganization (#19195) that moved Lazy there is not on this branch. Lazy is
still org.apache.hudi.util.Lazy here, so hudi-hadoop-common failed to compile
with "cannot find symbol: class Lazy", breaking every downstream module.

Note: 14 files under hudi-trino carry the same master-only Lazy import. That
module is profile-gated off by default (-Phudi-trino) so it does not break the
default build, and is left alone here.
voonhous pushed a commit that referenced this pull request Aug 7, 2026
* fix(fs): stop depending on the optional FileSystem#getScheme()

FileSystem#getScheme() is optional in Hadoop: the base implementation throws
UnsupportedOperationException, and proxy implementations such as Presto's
PrestoS3FileSystem do not override it. Hudi called it unguarded on filesystems it
did not implement, so opening a log file on such a filesystem failed with
"Not implemented by the PrestoS3FileSystem FileSystem implementation" instead of
reading anything (HUDI-4602).

Adds HadoopFSUtils#getScheme(FileSystem), which returns fs.getScheme() and falls
back to fs.getUri().getScheme() when it is unimplemented. getUri() is abstract, so
every implementation supplies it, and its scheme is what getScheme() returns
wherever both are present. This is the same conclusion as #793, which stopped
HoodieWrapperFileSystem calling getScheme() on the filesystem it wraps.

Routes the seven unguarded call sites through it: isGCSFileSystem and
isCHDFileSystem (the reported read path), registerFileSystem,
HoodieWrapperFileSystem#convertToHoodiePath, HoodieRetryWrapperFileSystem#getScheme,
WriteMarkersFactory's HDFS gate, and HoodieHadoopStorage#getScheme, which is what
the seven HoodieStorage#getScheme callers reach.

isGCSFileSystem's comparison is also flipped to put the constant first, matching
isCHDFileSystem, so a filesystem whose URI carries no scheme returns false rather
than throwing NullPointerException.

* test(fs): say which branch of the helper each assertion covers

Review nit: the assertion messages did not make clear what had gone wrong. Each
now names the filesystem and the branch of the helper it pins - LocalFileSystem
overriding getScheme() so the helper returns what it reports, FilterFileSystem not
overriding it so the helper falls back to getUri().getScheme().

* fix(fs): fail loudly on an unresolvable scheme, and cover the sites this reroutes

Review feedback, all of it well founded.

The fallback no longer returns null. InLineFileSystem is the counter-example in this
module: getScheme() is "inlinefs" while getUri() is URI.create("inlinefs"), which has no
colon and so no scheme, so the two are not interchangeable and the javadoc claim that
they agree was simply wrong. A null surfaced far from the cause as "does not support
scheme null" or "Unsupported scheme :null" with the UnsupportedOperationException
discarded; it now throws with that exception chained. HoodieException rather than
HoodieIOException, since the latter only accepts an IOException cause.

HoodieHadoopStorage memoizes the scheme. On a filesystem without getScheme() the
fallback costs a thrown-and-caught exception, and this is called once per log block via
StorageSchemes.isWriteTransactional and three times per immutable-file write via
needCreateTempFile. A lazy field keeps all five constructors untouched.

Test coverage for what this actually reroutes, none of which any test reached:

- registerFileSystem, HoodieWrapperFileSystem#convertToHoodiePath (the write path) and
  HoodieHadoopStorage#getScheme, via a LocalFileSystem subclass whose getScheme() throws,
  registered as fs.file.impl so it is reached through FileSystem.get.
- isGCSFileSystem and isCHDFileSystem, which become reachable for proxy filesystems for
  the first time here and select different stream wrappers: a scheme-less filesystem
  reporting gs:// now yields SchemeAwareFSDataInputStream and ofs:// yields
  BoundedFsDataInputStream.
- the new unresolvable-scheme failure.

TestFSUtilsWithRetryWrapperEnable#testGetSchema has been inert since HUDI-5286 added it:
it asserted on HoodieWrapperFileSystem#getScheme, which is uri.getScheme() and never
dispatches into the retry wrapper, and FakeRemoteFileSystem overrode getScheme() to
delegate to a real LocalFileSystem so it could not throw. Dropping that override gives
the fake the PrestoS3FileSystem shape and the assertion now targets the retry wrapper,
so it guards both HUDI-5286 and this change. Verified: it fails with the pre-PR helper.

Also drops the try/catch in convertToHoodiePath that only rethrew HoodieIOException
unchanged, dead since ef70de2, and the duplicated fixture and redundant nested
close in TestHadoopFSUtils.

(cherry picked from commit 70a5a4d)
voonhous added a commit that referenced this pull request Aug 7, 2026
…opStorage

The cherry-pick of #19470 (b8a09bf) brought master's
org.apache.hudi.common.util.Lazy import, but the hudi-common core package
reorganization (#19195) that moved Lazy there is not on this branch. Lazy is
still org.apache.hudi.util.Lazy here, so hudi-hadoop-common failed to compile
with "cannot find symbol: class Lazy", breaking every downstream module.

Note: 14 files under hudi-trino carry the same master-only Lazy import. That
module is profile-gated off by default (-Phudi-trino) so it does not break the
default build, and is left alone here.
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]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement getScheme for PrestoS3FileSystem

5 participants