This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.0 by this push:
new 7b624468652 branch-4.0: [fix](multi-catalog) OSS bucket endpoint path
normalization (#64943)
7b624468652 is described below
commit 7b624468652aef0d74cd42c56239878f65f8e97f
Author: Socrates <[email protected]>
AuthorDate: Tue Jun 30 11:06:13 2026 +0800
branch-4.0: [fix](multi-catalog) OSS bucket endpoint path normalization
(#64943)
## Summary
- Normalize remote paths in SwitchingFileSystem before delegating
operations.
- Select upload filesystem from the remote path instead of the local
path.
- Add an OSS bucket endpoint delegation test.
## Root Cause
OSS bucket-domain-name paths such as `oss://bucket.endpoint/path` were
normalized when selecting the filesystem, but the original unnormalized
path was still passed to object storage operations. The lower
S3-compatible parser could then treat `bucket.endpoint` as the bucket
name, breaking Hive insert overwrite cleanup/list/delete paths.
## Validation
- `DISABLE_BUILD_UI=ON ./build.sh --fe`
- `./run-fe-ut.sh --run
org.apache.doris.fs.remote.SwitchingFileSystemTest`
---
.../doris/fs/remote/SwitchingFileSystem.java | 56 +++++--
.../doris/fs/remote/SwitchingFileSystemTest.java | 170 +++++++++++++++++++++
2 files changed, 210 insertions(+), 16 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/fs/remote/SwitchingFileSystem.java
b/fe/fe-core/src/main/java/org/apache/doris/fs/remote/SwitchingFileSystem.java
index a6545f2db80..6f5b658760d 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/fs/remote/SwitchingFileSystem.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/fs/remote/SwitchingFileSystem.java
@@ -48,77 +48,95 @@ public class SwitchingFileSystem implements FileSystem {
@Override
public Status exists(String remotePath) {
- return fileSystem(remotePath).exists(remotePath);
+ String normalizedPath = normalizeLocation(remotePath);
+ return fileSystem(normalizedPath).exists(normalizedPath);
}
@Override
public Status directoryExists(String dir) {
- return fileSystem(dir).directoryExists(dir);
+ String normalizedPath = normalizeLocation(dir);
+ return fileSystem(normalizedPath).directoryExists(normalizedPath);
}
@Override
public Status downloadWithFileSize(String remoteFilePath, String
localFilePath, long fileSize) {
- return fileSystem(remoteFilePath).downloadWithFileSize(remoteFilePath,
localFilePath, fileSize);
+ String normalizedPath = normalizeLocation(remoteFilePath);
+ return fileSystem(normalizedPath).downloadWithFileSize(normalizedPath,
localFilePath, fileSize);
}
@Override
public Status upload(String localPath, String remotePath) {
- return fileSystem(localPath).upload(localPath, remotePath);
+ String normalizedPath = normalizeLocation(remotePath);
+ return fileSystem(normalizedPath).upload(localPath, normalizedPath);
}
@Override
public Status directUpload(String content, String remoteFile) {
- return fileSystem(remoteFile).directUpload(content, remoteFile);
+ String normalizedPath = normalizeLocation(remoteFile);
+ return fileSystem(normalizedPath).directUpload(content,
normalizedPath);
}
@Override
public Status rename(String origFilePath, String destFilePath) {
- return fileSystem(origFilePath).rename(origFilePath, destFilePath);
+ String normalizedOrigPath = normalizeLocation(origFilePath);
+ String normalizedDestPath = normalizeLocation(destFilePath);
+ return fileSystem(normalizedOrigPath).rename(normalizedOrigPath,
normalizedDestPath);
}
@Override
public Status renameDir(String origFilePath, String destFilePath) {
- return fileSystem(origFilePath).renameDir(origFilePath, destFilePath);
+ String normalizedOrigPath = normalizeLocation(origFilePath);
+ String normalizedDestPath = normalizeLocation(destFilePath);
+ return fileSystem(normalizedOrigPath).renameDir(normalizedOrigPath,
normalizedDestPath);
}
@Override
public Status renameDir(String origFilePath, String destFilePath, Runnable
runWhenPathNotExist) {
- return fileSystem(origFilePath).renameDir(origFilePath, destFilePath,
runWhenPathNotExist);
+ String normalizedOrigPath = normalizeLocation(origFilePath);
+ String normalizedDestPath = normalizeLocation(destFilePath);
+ return fileSystem(normalizedOrigPath).renameDir(normalizedOrigPath,
normalizedDestPath, runWhenPathNotExist);
}
@Override
public Status delete(String remotePath) {
- return fileSystem(remotePath).delete(remotePath);
+ String normalizedPath = normalizeLocation(remotePath);
+ return fileSystem(normalizedPath).delete(normalizedPath);
}
@Override
public Status deleteDirectory(String absolutePath) {
- return fileSystem(absolutePath).deleteDirectory(absolutePath);
+ String normalizedPath = normalizeLocation(absolutePath);
+ return fileSystem(normalizedPath).deleteDirectory(normalizedPath);
}
@Override
public Status makeDir(String remotePath) {
- return fileSystem(remotePath).makeDir(remotePath);
+ String normalizedPath = normalizeLocation(remotePath);
+ return fileSystem(normalizedPath).makeDir(normalizedPath);
}
@Override
public Status listFiles(String remotePath, boolean recursive,
List<RemoteFile> result) {
- return fileSystem(remotePath).listFiles(remotePath, recursive, result);
+ String normalizedPath = normalizeLocation(remotePath);
+ return fileSystem(normalizedPath).listFiles(normalizedPath, recursive,
result);
}
@Override
public Status globList(String remotePath, List<RemoteFile> result) {
- return fileSystem(remotePath).globList(remotePath, result);
+ String normalizedPath = normalizeLocation(remotePath);
+ return fileSystem(normalizedPath).globList(normalizedPath, result);
}
@Override
public Status globList(String remotePath, List<RemoteFile> result, boolean
fileNameOnly) {
- return fileSystem(remotePath).globList(remotePath, result,
fileNameOnly);
+ String normalizedPath = normalizeLocation(remotePath);
+ return fileSystem(normalizedPath).globList(normalizedPath, result,
fileNameOnly);
}
@Override
public Status listDirectories(String remotePath, Set<String> result) {
- return fileSystem(remotePath).listDirectories(remotePath, result);
+ String normalizedPath = normalizeLocation(remotePath);
+ return fileSystem(normalizedPath).listDirectories(normalizedPath,
result);
}
public FileSystem fileSystem(String location) {
@@ -128,5 +146,11 @@ public class SwitchingFileSystem implements FileSystem {
);
return
extMetaCacheMgr.getFsCache().getRemoteFileSystem(fileSystemCacheKey);
}
-}
+ private String normalizeLocation(String location) {
+ if (storagePropertiesMap == null) {
+ return location;
+ }
+ return LocationPath.of(location,
storagePropertiesMap).getNormalizedLocation();
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/fs/remote/SwitchingFileSystemTest.java
b/fe/fe-core/src/test/java/org/apache/doris/fs/remote/SwitchingFileSystemTest.java
new file mode 100644
index 00000000000..3fbcec8715a
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/fs/remote/SwitchingFileSystemTest.java
@@ -0,0 +1,170 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.fs.remote;
+
+import org.apache.doris.backup.Status;
+import org.apache.doris.datasource.property.storage.StorageProperties;
+import org.apache.doris.fs.FileSystem;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class SwitchingFileSystemTest {
+
+ @Test
+ public void testNormalizeOssBucketEndpointPathBeforeDelegating() {
+ RecordingFileSystem delegate = new RecordingFileSystem();
+ SwitchingFileSystem fs = new TestSwitchingFileSystem(delegate,
createOssStorageProperties());
+
+ String sourcePath =
"oss://my-bucket.oss-cn-beijing-internal.aliyuncs.com/path/to/source";
+ String destPath =
"oss://my-bucket.oss-cn-beijing-internal.aliyuncs.com/path/to/dest";
+
+ fs.deleteDirectory(sourcePath);
+ Assertions.assertEquals("s3://my-bucket/path/to/source",
delegate.remotePath);
+ Assertions.assertEquals("s3://my-bucket/path/to/source",
delegate.selectedLocation);
+
+ fs.listFiles(sourcePath, true, Collections.emptyList());
+ Assertions.assertEquals("s3://my-bucket/path/to/source",
delegate.remotePath);
+ Assertions.assertEquals("s3://my-bucket/path/to/source",
delegate.selectedLocation);
+
+ fs.rename(sourcePath, destPath);
+ Assertions.assertEquals("s3://my-bucket/path/to/source",
delegate.remotePath);
+ Assertions.assertEquals("s3://my-bucket/path/to/dest",
delegate.destPath);
+ Assertions.assertEquals("s3://my-bucket/path/to/source",
delegate.selectedLocation);
+
+ fs.upload("/tmp/local-file", destPath);
+ Assertions.assertEquals("/tmp/local-file", delegate.localPath);
+ Assertions.assertEquals("s3://my-bucket/path/to/dest",
delegate.remotePath);
+ Assertions.assertEquals("s3://my-bucket/path/to/dest",
delegate.selectedLocation);
+ }
+
+ private static Map<StorageProperties.Type, StorageProperties>
createOssStorageProperties() {
+ Map<String, String> origProps = new HashMap<>();
+ origProps.put("oss.endpoint", "oss-cn-beijing-internal.aliyuncs.com");
+ origProps.put("oss.access_key", "ak");
+ origProps.put("oss.secret_key", "sk");
+ origProps.put(StorageProperties.FS_OSS_SUPPORT, "true");
+
+ Map<StorageProperties.Type, StorageProperties> storageProperties = new
HashMap<>();
+ storageProperties.put(StorageProperties.Type.OSS,
StorageProperties.createPrimary(origProps));
+ return storageProperties;
+ }
+
+ private static class TestSwitchingFileSystem extends SwitchingFileSystem {
+ private final RecordingFileSystem delegate;
+
+ TestSwitchingFileSystem(RecordingFileSystem delegate,
+ Map<StorageProperties.Type, StorageProperties>
storagePropertiesMap) {
+ super(null, storagePropertiesMap);
+ this.delegate = delegate;
+ }
+
+ @Override
+ public FileSystem fileSystem(String location) {
+ delegate.selectedLocation = location;
+ return delegate;
+ }
+ }
+
+ private static class RecordingFileSystem implements FileSystem {
+ private String selectedLocation;
+ private String localPath;
+ private String remotePath;
+ private String destPath;
+
+ @Override
+ public Map<String, String> getProperties() {
+ return null;
+ }
+
+ @Override
+ public Status exists(String remotePath) {
+ this.remotePath = remotePath;
+ return Status.OK;
+ }
+
+ @Override
+ public Status downloadWithFileSize(String remoteFilePath, String
localFilePath, long fileSize) {
+ this.remotePath = remoteFilePath;
+ this.localPath = localFilePath;
+ return Status.OK;
+ }
+
+ @Override
+ public Status upload(String localPath, String remotePath) {
+ this.localPath = localPath;
+ this.remotePath = remotePath;
+ return Status.OK;
+ }
+
+ @Override
+ public Status directUpload(String content, String remoteFile) {
+ this.remotePath = remoteFile;
+ return Status.OK;
+ }
+
+ @Override
+ public Status rename(String origFilePath, String destFilePath) {
+ this.remotePath = origFilePath;
+ this.destPath = destFilePath;
+ return Status.OK;
+ }
+
+ @Override
+ public Status delete(String remotePath) {
+ this.remotePath = remotePath;
+ return Status.OK;
+ }
+
+ @Override
+ public Status deleteDirectory(String dir) {
+ this.remotePath = dir;
+ return Status.OK;
+ }
+
+ @Override
+ public Status makeDir(String remotePath) {
+ this.remotePath = remotePath;
+ return Status.OK;
+ }
+
+ @Override
+ public Status listFiles(String remotePath, boolean recursive,
List<RemoteFile> result) {
+ this.remotePath = remotePath;
+ return Status.OK;
+ }
+
+ @Override
+ public Status globList(String remotePath, List<RemoteFile> result,
boolean fileNameOnly) {
+ this.remotePath = remotePath;
+ return Status.OK;
+ }
+
+ @Override
+ public Status listDirectories(String remotePath, Set<String> result) {
+ this.remotePath = remotePath;
+ return Status.OK;
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]