rangareddy commented on code in PR #19470:
URL: https://github.com/apache/hudi/pull/19470#discussion_r3704437325


##########
hudi-hadoop-common/src/main/java/org/apache/hudi/hadoop/fs/HoodieRetryWrapperFileSystem.java:
##########
@@ -277,7 +277,7 @@ public Configuration getConf() {
 
   @Override
   public String getScheme() {
-    return fileSystem.getScheme();
+    return HadoopFSUtils.getScheme(fileSystem);

Review Comment:
   Applied exactly as suggested, and you are right that it had been a no-op 
since it merged. Deleted `FakeRemoteFileSystem#getScheme()` and retargeted the 
assertion at the wrapper:
   
   ```java
   assertThrows(UnsupportedOperationException.class, fakeFs::getScheme);
   assertEquals("file", ((HoodieRetryWrapperFileSystem) 
fileSystem).getScheme(), ...);
   ```
   
   `FakeRemoteFileSystem extends FileSystem`, so with the override gone the 
base implementation throws and `getUri()` still delegates to the real local 
filesystem — the `PrestoS3FileSystem` shape, as you said.
   
   Confirmed it is now a real guard: with the pre-PR helper restored, 
`TestFSUtilsWithRetryWrapperEnable` reports `Tests run: 55, Errors: 1` and the 
failing one is `testGetSchema`. It could not fail before.
   



##########
hudi-hadoop-common/src/main/java/org/apache/hudi/hadoop/fs/HadoopFSUtils.java:
##########
@@ -294,7 +315,7 @@ private static StorageConfiguration<Configuration> 
getStorageConf(Configuration
 
   public static Configuration registerFileSystem(StoragePath file, 
Configuration conf) {
     Configuration returnConf = new Configuration(conf);
-    String scheme = HadoopFSUtils.getFs(file.toString(), conf).getScheme();
+    String scheme = getScheme(HadoopFSUtils.getFs(file.toString(), conf));

Review Comment:
   Added, with the fixture registered as `fs.file.impl` so all three go through 
the normal `FileSystem.get` path:
   
   ```java
   Configuration conf = new Configuration();
   conf.setClass("fs.file.impl", NoSchemeLocalFileSystem.class, 
FileSystem.class);
   StoragePath path = new StoragePath(tempDir.toUri());
   
   assertDoesNotThrow(() -> HadoopFSUtils.registerFileSystem(path, conf), ...);
   assertDoesNotThrow(() -> HoodieWrapperFileSystem.convertToHoodiePath(path, 
conf), ...);
   assertEquals("file", new HoodieHadoopStorage(path, 
HadoopFSUtils.getStorageConf(conf)).getScheme(), ...);
   ```
   
   `testCallSitesWorkOnAFileSystemWithoutGetScheme`. With the pre-PR helper 
restored it errors on all three, so it reproduces your A/B.
   
   Your point about `convertToHoodiePath` being the write path is what made me 
stop treating this as a read-path fix — thanks for spelling out the HUDI-4495 
lineage.
   



##########
hudi-hadoop-common/src/main/java/org/apache/hudi/hadoop/fs/HadoopFSUtils.java:
##########
@@ -277,15 +277,36 @@ private static FSDataInputStream 
getFSDataInputStreamForGCS(FSDataInputStream fs
    * @return true if the inputstream or the wrapped one is of type 
GoogleHadoopFSInputStream
    */
   public static boolean isGCSFileSystem(FileSystem fs) {
-    return fs.getScheme().equals(StorageSchemes.GCS.getScheme());
+    return StorageSchemes.GCS.getScheme().equals(getScheme(fs));

Review Comment:
   Added `testSchemeSpecificStreamIsSelectedWithoutGetScheme`, parameterised 
over both predicates, using a `FilterFileSystem` subclass that reports a URI of 
my choosing while leaving `getScheme()` to the throwing base implementation:
   
   ```java
   @CsvSource({
       "gs://bucket, org.apache.hudi.hadoop.fs.SchemeAwareFSDataInputStream",
       "ofs://cluster, org.apache.hudi.hadoop.fs.BoundedFsDataInputStream"
   })
   ```
   
   It passes, which independently confirms your A/B: the fallback-resolved 
scheme does select `SchemeAwareFSDataInputStream` and 
`BoundedFsDataInputStream`. With the pre-PR helper both cases error instead.
   
   Agreed this is the assertion with the most future value — it is the one that 
pins `HadoopFSUtils.getScheme` returning what `fs.getScheme()` would have, 
rather than merely not throwing.
   



##########
hudi-hadoop-common/src/main/java/org/apache/hudi/hadoop/fs/HadoopFSUtils.java:
##########
@@ -277,15 +277,36 @@ private static FSDataInputStream 
getFSDataInputStreamForGCS(FSDataInputStream fs
    * @return true if the inputstream or the wrapped one is of type 
GoogleHadoopFSInputStream
    */
   public static boolean isGCSFileSystem(FileSystem fs) {
-    return fs.getScheme().equals(StorageSchemes.GCS.getScheme());
+    return StorageSchemes.GCS.getScheme().equals(getScheme(fs));
   }
 
   /**
    * Chdfs will throw {@code IOException} instead of {@code EOFException}. It 
will cause error in isBlockCorrupted().
    * Wrapped by {@code BoundedFsDataInputStream}, to check whether the desired 
offset is out of the file size in advance.
    */
   public static boolean isCHDFileSystem(FileSystem fs) {
-    return StorageSchemes.CHDFS.getScheme().equals(fs.getScheme());
+    return StorageSchemes.CHDFS.getScheme().equals(getScheme(fs));
+  }
+
+  /**
+   * Resolves the scheme of {@code fs} without depending on {@link 
FileSystem#getScheme()}.
+   *
+   * <p>{@code getScheme()} is optional in Hadoop: {@link FileSystem}'s own 
implementation throws
+   * {@link UnsupportedOperationException}, and proxy implementations such as 
Presto's
+   * {@code PrestoS3FileSystem} do not override it, so calling it unguarded 
turns an unrelated read into
+   * "Not implemented by the PrestoS3FileSystem FileSystem implementation" 
(HUDI-4602).
+   * {@link FileSystem#getUri()} is abstract, so every implementation supplies 
it, and its scheme is what
+   * {@code getScheme()} returns wherever both are present.
+   *
+   * @param fs instance of {@link FileSystem} in use.
+   * @return the scheme of {@code fs}, or null if its URI carries none.
+   */
+  public static String getScheme(FileSystem fs) {
+    try {
+      return fs.getScheme();
+    } catch (UnsupportedOperationException e) {
+      return fs.getUri().getScheme();
+    }

Review Comment:
   You are right, and this is the most important comment on the PR — the 
javadoc sentence you quote was simply false, and `InLineFileSystem` is the 
counter-example sitting in the same module. Verified: 
`URI.create("inlinefs").getScheme()` is `null`.
   
   Applied, with one deviation. Your snippet does not compile as written: 
`HoodieIOException`'s only cause-taking constructor is 
`HoodieIOException(String, IOException)`, so an `UnsupportedOperationException` 
cannot be chained through it — and discarding that cause is the thing being 
fixed. I used `HoodieException`, which takes a `Throwable`:
   
   ```java
   } catch (UnsupportedOperationException e) {
     String scheme = fs.getUri().getScheme();
     if (scheme == null) {
       throw new HoodieException("Cannot resolve the scheme of " + 
fs.getClass().getName()
           + ": getScheme() is unimplemented and its URI " + fs.getUri() + " 
carries no scheme", e);
     }
     return scheme;
   }
   ```
   
   Say the word if you would rather keep `HoodieIOException` and wrap the cause 
in an `IOException` to fit its signature; I preferred not to invent a layer.
   
   Both claims are gone from the javadoc. It now states the opposite — that the 
two sources are *not* interchangeable, names `InLineFileSystem` as the reason, 
and says why the ordering is load-bearing rather than cosmetic. `@return` is 
`never null`, with `@throws`.
   
   `testGetSchemeFailsLoudlyWhenNeitherSourceHasOne` pins it and asserts the 
cause is chained. With the null-returning fallback restored it fails with 
`Expected org.apache.hudi.exception.HoodieException to be thrown, but nothing 
was thrown.`
   



##########
hudi-hadoop-common/src/main/java/org/apache/hudi/storage/hadoop/HoodieHadoopStorage.java:
##########
@@ -111,7 +111,7 @@ public HoodieStorage newInstance(StoragePath path, 
StorageConfiguration<?> stora
 
   @Override
   public String getScheme() {
-    return fs.getScheme();
+    return HadoopFSUtils.getScheme(fs);

Review Comment:
   Memoized. Thank you for measuring it — the per-log-block call through 
`isWriteTransactional` is the one that convinced me, since that is squarely 
this PR's own target path.
   
   ```java
   private final Lazy<String> scheme = Lazy.lazily(this::resolveScheme);
   
   @Override
   public String getScheme() {
     return scheme.get();
   }
   
   private String resolveScheme() {
     return HadoopFSUtils.getScheme(fs);
   }
   ```
   
   One wrinkle: `Lazy.lazily(() -> HadoopFSUtils.getScheme(fs))` as a field 
initializer does not compile — `fs` is a blank final assigned in the 
constructors, so javac reports "variable fs might not have been initialized". A 
method reference defers the read and still avoids touching all five 
constructors, which was your point.
   



##########
hudi-hadoop-common/src/main/java/org/apache/hudi/hadoop/fs/HoodieWrapperFileSystem.java:
##########
@@ -160,7 +160,7 @@ public HoodieWrapperFileSystem(FileSystem fileSystem, 
ConsistencyGuard consisten
 
   public static Path convertToHoodiePath(StoragePath file, Configuration conf) 
{
     try {
-      String scheme = HadoopFSUtils.getFs(file.toString(), conf).getScheme();
+      String scheme = 
HadoopFSUtils.getScheme(HadoopFSUtils.getFs(file.toString(), conf));

Review Comment:
   Dropped — the `catch (HoodieIOException e) { throw e; }` was indeed a no-op:
   
   ```java
   public static Path convertToHoodiePath(StoragePath file, Configuration conf) 
{
     String scheme = 
HadoopFSUtils.getScheme(HadoopFSUtils.getFs(file.toString(), conf));
     return convertPathWithScheme(convertToHadoopPath(file), 
getHoodieScheme(scheme));
   }
   ```
   
   Worth noting this method is now covered by 
`testCallSitesWorkOnAFileSystemWithoutGetScheme` from your other comment, so 
the removal is not unobserved.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to