This is an automated email from the ASF dual-hosted git repository.
morrysnow pushed a commit to branch branch-3.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-3.1 by this push:
new 0a2fc5c4fea branch-3.1: [fix](catalog)Disable Hadoop FileSystem cache
for multi-configurationobject storage catalogs #57063 (#57153)
0a2fc5c4fea is described below
commit 0a2fc5c4fea9547aa372265c13eb11eb7e98fe7f
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Oct 21 15:59:15 2025 +0800
branch-3.1: [fix](catalog)Disable Hadoop FileSystem cache for
multi-configurationobject storage catalogs #57063 (#57153)
Cherry-picked from #57063
Co-authored-by: Calvin Kirs <[email protected]>
---
.../storage/AbstractS3CompatibleProperties.java | 45 ++++++++++++++++++++--
.../datasource/property/storage/COSProperties.java | 5 +++
.../datasource/property/storage/GCSProperties.java | 5 +++
.../property/storage/MinioProperties.java | 5 +++
.../datasource/property/storage/OBSProperties.java | 5 +++
.../datasource/property/storage/OSSProperties.java | 5 +++
.../datasource/property/storage/S3Properties.java | 10 ++---
.../property/storage/COSPropertiesTest.java | 16 ++++++++
.../property/storage/GCSPropertiesTest.java | 18 +++++++++
.../property/storage/OBSPropertyTest.java | 18 +++++++++
.../property/storage/OSSPropertiesTest.java | 18 +++++++++
.../property/storage/S3PropertiesTest.java | 17 ++++++++
12 files changed, 158 insertions(+), 9 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/AbstractS3CompatibleProperties.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/AbstractS3CompatibleProperties.java
index 2a7e58d787f..c4cc493abe9 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/AbstractS3CompatibleProperties.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/AbstractS3CompatibleProperties.java
@@ -21,6 +21,7 @@ import org.apache.doris.common.UserException;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
+import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.logging.log4j.LogManager;
@@ -149,10 +150,6 @@ public abstract class AbstractS3CompatibleProperties
extends StorageProperties i
}
}
- boolean isEndpointCheckRequired() {
- return true;
- }
-
/**
* Checks and validates the configured endpoint.
* <p>
@@ -229,6 +226,8 @@ public abstract class AbstractS3CompatibleProperties
extends StorageProperties i
protected abstract Set<Pattern> endpointPatterns();
+ protected abstract Set<String> schemas();
+
// This method should be overridden by subclasses to provide a default
endpoint based on the region.
// Because for aws s3, only region is needed, the endpoint can be
constructed from the region.
// But for other s3 compatible storage, the endpoint may need to be
specified explicitly.
@@ -250,10 +249,16 @@ public abstract class AbstractS3CompatibleProperties
extends StorageProperties i
@Override
public void initializeHadoopStorageConfig() {
hadoopStorageConfig = new Configuration();
+ origProps.forEach((key, value) -> {
+ if (key.startsWith("fs.")) {
+ hadoopStorageConfig.set(key, value);
+ }
+ });
// Compatibility note: Due to historical reasons, even when the
underlying
// storage is OSS, OBS, etc., users may still configure the schema as
"s3://".
// To ensure backward compatibility, we append S3-related properties
by default.
appendS3HdfsProperties(hadoopStorageConfig);
+ ensureDisableCache(hadoopStorageConfig, origProps);
}
private void appendS3HdfsProperties(Configuration hadoopStorageConfig) {
@@ -279,6 +284,38 @@ public abstract class AbstractS3CompatibleProperties
extends StorageProperties i
hadoopStorageConfig.set("fs.s3a.path.style.access", getUsePathStyle());
}
+ /**
+ * By default, Hadoop caches FileSystem instances per scheme and authority
(e.g. s3a://bucket/), meaning that all
+ * subsequent calls using the same URI will reuse the same FileSystem
object.
+ * In multi-tenant or dynamic credential environments — where different
users may access the same bucket using
+ * different access keys or tokens — this cache reuse can lead to
cross-credential contamination.
+ * <p>
+ * Specifically, if the cache is not disabled, a FileSystem instance
initialized with one set of credentials may
+ * be reused by another session targeting the same bucket but with a
different AK/SK. This results in:
+ * <p>
+ * Incorrect authentication (using stale credentials)
+ * <p>
+ * Unexpected permission errors or access denial
+ * <p>
+ * Potential data leakage between users
+ * <p>
+ * To avoid such risks, the configuration property
+ * fs.<schema>.impl.disable.cache
+ * must be set to true for all object storage backends (e.g., S3A, OSS,
COS, OBS), ensuring that each new access
+ * creates an isolated FileSystem instance with its own credentials and
configuration context.
+ */
+ private void ensureDisableCache(Configuration conf, Map<String, String>
origProps) {
+ for (String schema : schemas()) {
+ String key = "fs." + schema + ".impl.disable.cache";
+ String userValue = origProps.get(key);
+ if (StringUtils.isNotBlank(userValue)) {
+ conf.setBoolean(key, BooleanUtils.toBoolean(userValue));
+ } else {
+ conf.setBoolean(key, true);
+ }
+ }
+ }
+
@Override
public String getStorageName() {
return "S3";
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/COSProperties.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/COSProperties.java
index af4c3324424..8f1d4b88f3a 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/COSProperties.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/COSProperties.java
@@ -167,4 +167,9 @@ public class COSProperties extends
AbstractS3CompatibleProperties {
hadoopStorageConfig.set("fs.cosn.userinfo.secretId", accessKey);
hadoopStorageConfig.set("fs.cosn.userinfo.secretKey", secretKey);
}
+
+ @Override
+ protected Set<String> schemas() {
+ return ImmutableSet.of("cos", "cosn");
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/GCSProperties.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/GCSProperties.java
index dffd18d0d58..20de10f97cd 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/GCSProperties.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/GCSProperties.java
@@ -183,6 +183,11 @@ public class GCSProperties extends
AbstractS3CompatibleProperties {
return backendProperties;
}
+ @Override
+ protected Set<String> schemas() {
+ return ImmutableSet.of("gs");
+ }
+
@Override
public AwsCredentialsProvider getAwsCredentialsProvider() {
AwsCredentialsProvider credentialsProvider =
super.getAwsCredentialsProvider();
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/MinioProperties.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/MinioProperties.java
index 1f52a84d392..34450383d83 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/MinioProperties.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/MinioProperties.java
@@ -136,4 +136,9 @@ public class MinioProperties extends
AbstractS3CompatibleProperties {
throw new IllegalArgumentException("Property minio.endpoint is
required.");
}
}
+
+ @Override
+ protected Set<String> schemas() {
+ return ImmutableSet.of("s3");
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/OBSProperties.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/OBSProperties.java
index e2adb19895c..92abf2997b4 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/OBSProperties.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/OBSProperties.java
@@ -180,4 +180,9 @@ public class OBSProperties extends
AbstractS3CompatibleProperties {
throw new IllegalArgumentException("Property obs.endpoint is
required.");
}
}
+
+ @Override
+ protected Set<String> schemas() {
+ return ImmutableSet.of("obs");
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/OSSProperties.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/OSSProperties.java
index dee34a76d16..47558497a68 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/OSSProperties.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/OSSProperties.java
@@ -282,6 +282,11 @@ public class OSSProperties extends
AbstractS3CompatibleProperties {
return null;
}
+ @Override
+ protected Set<String> schemas() {
+ return ImmutableSet.of("oss");
+ }
+
@Override
public void initializeHadoopStorageConfig() {
super.initializeHadoopStorageConfig();
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
index 1b162e87bf0..47d0301ad9b 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
@@ -215,11 +215,6 @@ public class S3Properties extends
AbstractS3CompatibleProperties {
convertGlueToS3EndpointIfNeeded();
}
- @Override
- boolean isEndpointCheckRequired() {
- return false;
- }
-
/**
* Guess if the storage properties is for this storage type.
* Subclass should override this method to provide the correct
implementation.
@@ -268,6 +263,11 @@ public class S3Properties extends
AbstractS3CompatibleProperties {
return ENDPOINT_PATTERN;
}
+ @Override
+ protected Set<String> schemas() {
+ return ImmutableSet.of("s3", "s3a", "s3n");
+ }
+
@Override
public Map<String, String> getBackendConfigProperties() {
Map<String, String> backendProperties =
generateBackendS3Configuration();
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/COSPropertiesTest.java
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/COSPropertiesTest.java
index aadecf63c3f..df20ea5d333 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/COSPropertiesTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/COSPropertiesTest.java
@@ -20,6 +20,7 @@ package org.apache.doris.datasource.property.storage;
import org.apache.doris.common.UserException;
import
org.apache.doris.datasource.property.storage.exception.StoragePropertiesException;
+import com.google.common.collect.Maps;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -176,4 +177,19 @@ public class COSPropertiesTest {
obsStorageProperties = (COSProperties)
StorageProperties.createPrimary(props);
Assertions.assertEquals(StaticCredentialsProvider.class,
obsStorageProperties.getAwsCredentialsProvider().getClass());
}
+
+ @Test
+ public void testS3DisableHadoopCache() throws UserException {
+ Map<String, String> props = Maps.newHashMap();
+ props.put("cos.endpoint", "cos.ap-beijing.myqcloud.com");
+ COSProperties s3Properties = (COSProperties)
StorageProperties.createPrimary(props);
+ Assertions.assertEquals("true",
s3Properties.hadoopStorageConfig.get("fs.cos.impl.disable.cache"));
+ Assertions.assertEquals("true",
s3Properties.hadoopStorageConfig.get("fs.s3.impl.disable.cache"));
+ Assertions.assertEquals("true",
s3Properties.hadoopStorageConfig.get("fs.cosn.impl.disable.cache"));
+ props.put("fs.cos.impl.disable.cache", "true");
+ props.put("fs.cosn.impl.disable.cache", "false");
+ s3Properties = (COSProperties) StorageProperties.createPrimary(props);
+ Assertions.assertEquals("true",
s3Properties.hadoopStorageConfig.get("fs.cos.impl.disable.cache"));
+ Assertions.assertEquals("false",
s3Properties.hadoopStorageConfig.get("fs.cosn.impl.disable.cache"));
+ }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/GCSPropertiesTest.java
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/GCSPropertiesTest.java
index c7167a8eba7..179655582b7 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/GCSPropertiesTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/GCSPropertiesTest.java
@@ -17,6 +17,7 @@
package org.apache.doris.datasource.property.storage;
+import com.google.common.collect.Maps;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -110,4 +111,21 @@ public class GCSPropertiesTest {
gcsStorageProperties = (GCSProperties)
StorageProperties.createPrimary(gcsProps);
Assertions.assertEquals(StaticCredentialsProvider.class,
gcsStorageProperties.getAwsCredentialsProvider().getClass());
}
+
+ @Test
+ public void testS3DisableHadoopCache() {
+ Map<String, String> props = Maps.newHashMap();
+ props.put("fs.gcs.support", "true");
+ GCSProperties s3Properties = (GCSProperties)
StorageProperties.createPrimary(props);
+
Assertions.assertTrue(s3Properties.hadoopStorageConfig.getBoolean("fs.gs.impl.disable.cache",
false));
+ props.put("fs.gs.impl.disable.cache", "true");
+ s3Properties = (GCSProperties) StorageProperties.createPrimary(props);
+
Assertions.assertTrue(s3Properties.hadoopStorageConfig.getBoolean("fs.gs.impl.disable.cache",
false));
+ props.put("fs.gs.impl.disable.cache", "false");
+ s3Properties = (GCSProperties) StorageProperties.createPrimary(props);
+
Assertions.assertFalse(s3Properties.hadoopStorageConfig.getBoolean("fs.gs.impl.disable.cache",
false));
+ props.put("fs.gs.impl.disable.cache", "null");
+ s3Properties = (GCSProperties) StorageProperties.createPrimary(props);
+
Assertions.assertFalse(s3Properties.hadoopStorageConfig.getBoolean("fs.gs.impl.disable.cache",
false));
+ }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/OBSPropertyTest.java
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/OBSPropertyTest.java
index 1d9fee06e19..4329368254a 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/OBSPropertyTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/OBSPropertyTest.java
@@ -20,6 +20,7 @@ package org.apache.doris.datasource.property.storage;
import org.apache.doris.common.ExceptionChecker;
import org.apache.doris.common.UserException;
+import com.google.common.collect.Maps;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider;
@@ -152,6 +153,23 @@ public class OBSPropertyTest {
Assertions.assertEquals(StaticCredentialsProvider.class,
obsStorageProperties.getAwsCredentialsProvider().getClass());
}
+ @Test
+ public void testS3DisableHadoopCache() {
+ Map<String, String> props = Maps.newHashMap();
+ props.put("obs.endpoint", "obs.cn-north-4.myhuaweicloud.com");
+ OBSProperties s3Properties = (OBSProperties)
StorageProperties.createPrimary(props);
+
Assertions.assertTrue(s3Properties.hadoopStorageConfig.getBoolean("fs.obs.impl.disable.cache",
false));
+ props.put("fs.obs.impl.disable.cache", "true");
+ s3Properties = (OBSProperties) StorageProperties.createPrimary(props);
+
Assertions.assertTrue(s3Properties.hadoopStorageConfig.getBoolean("fs.obs.impl.disable.cache",
false));
+ props.put("fs.obs.impl.disable.cache", "false");
+ s3Properties = (OBSProperties) StorageProperties.createPrimary(props);
+
Assertions.assertFalse(s3Properties.hadoopStorageConfig.getBoolean("fs.obs.impl.disable.cache",
false));
+ props.put("fs.obs.impl.disable.cache", "null");
+ s3Properties = (OBSProperties) StorageProperties.createPrimary(props);
+
Assertions.assertFalse(s3Properties.hadoopStorageConfig.getBoolean("fs.obs.impl.disable.cache",
false));
+ }
+
@Test
public void testMissingSecretKey() {
origProps.put("obs.endpoint", "obs.cn-north-4.myhuaweicloud.com");
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/OSSPropertiesTest.java
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/OSSPropertiesTest.java
index 7a0460f3153..874aea6e510 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/OSSPropertiesTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/OSSPropertiesTest.java
@@ -20,6 +20,7 @@ package org.apache.doris.datasource.property.storage;
import org.apache.doris.common.ExceptionChecker;
import org.apache.doris.common.UserException;
+import com.google.common.collect.Maps;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider;
@@ -250,4 +251,21 @@ public class OSSPropertiesTest {
Assertions.assertEquals(StaticCredentialsProvider.class,
ossStorageProperties.getAwsCredentialsProvider().getClass());
}
+ @Test
+ public void testS3DisableHadoopCache() throws UserException {
+ Map<String, String> props = Maps.newHashMap();
+ props.put("oss.endpoint", "oss-cn-hangzhou.aliyuncs.com");
+ OSSProperties s3Properties = (OSSProperties)
StorageProperties.createPrimary(props);
+
Assertions.assertTrue(s3Properties.hadoopStorageConfig.getBoolean("fs.oss.impl.disable.cache",
false));
+ props.put("fs.oss.impl.disable.cache", "true");
+ s3Properties = (OSSProperties) StorageProperties.createPrimary(props);
+
Assertions.assertTrue(s3Properties.hadoopStorageConfig.getBoolean("fs.oss.impl.disable.cache",
false));
+ props.put("fs.oss.impl.disable.cache", "false");
+ s3Properties = (OSSProperties) StorageProperties.createPrimary(props);
+
Assertions.assertFalse(s3Properties.hadoopStorageConfig.getBoolean("fs.oss.impl.disable.cache",
false));
+ props.put("fs.oss.impl.disable.cache", "null");
+ s3Properties = (OSSProperties) StorageProperties.createPrimary(props);
+
Assertions.assertFalse(s3Properties.hadoopStorageConfig.getBoolean("fs.oss.impl.disable.cache",
false));
+ }
+
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
index a0e94042c59..f4ea2fcb381 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
@@ -423,4 +423,21 @@ public class S3PropertiesTest {
Assertions.assertEquals(AwsCredentialsProviderChain.class,
provider.getClass());
Config.aws_credentials_provider_version = "v2";
}
+
+ @Test
+ public void testS3DisableHadoopCache() throws UserException {
+ Map<String, String> props = Maps.newHashMap();
+ props.put("s3.endpoint", "s3.us-west-2.amazonaws.com");
+ S3Properties s3Properties = (S3Properties)
StorageProperties.createPrimary(props);
+ Assertions.assertEquals("true",
s3Properties.hadoopStorageConfig.get("fs.s3a.impl.disable.cache"));
+ Assertions.assertEquals("true",
s3Properties.hadoopStorageConfig.get("fs.s3.impl.disable.cache"));
+ Assertions.assertEquals("true",
s3Properties.hadoopStorageConfig.get("fs.s3n.impl.disable.cache"));
+ props.put("fs.s3a.impl.disable.cache", "true");
+ props.put("fs.s3.impl.disable.cache", "false");
+ props.put("fs.s3n.impl.disable.cache", "null");
+ s3Properties = (S3Properties) StorageProperties.createPrimary(props);
+ Assertions.assertEquals("true",
s3Properties.hadoopStorageConfig.get("fs.s3a.impl.disable.cache"));
+ Assertions.assertEquals("false",
s3Properties.hadoopStorageConfig.get("fs.s3.impl.disable.cache"));
+ Assertions.assertEquals("false",
s3Properties.hadoopStorageConfig.get("fs.s3n.impl.disable.cache"));
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]