This is an automated email from the ASF dual-hosted git repository.
yiguolei 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 698b28ac319 branch-4.0: [Fix](Iam-role)Ensure StorageProperties list
remains ordered when auto-loading default HDFS #58968 (#59072)
698b28ac319 is described below
commit 698b28ac31904d47a11127958af1f0fa6bac8c20
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Dec 17 16:06:20 2025 +0800
branch-4.0: [Fix](Iam-role)Ensure StorageProperties list remains ordered
when auto-loading default HDFS #58968 (#59072)
Cherry-picked from #58968
Co-authored-by: Calvin Kirs <[email protected]>
---
.../apache/doris/datasource/CatalogProperty.java | 30 +++++++++++++++++++---
.../datasource/iceberg/IcebergExternalCatalog.java | 5 ++--
.../datasource/paimon/PaimonExternalCatalog.java | 2 +-
.../property/storage/StorageProperties.java | 23 +++++++++++------
.../property/storage/AzurePropertiesTest.java | 4 +--
.../property/storage/COSPropertiesTest.java | 7 ++---
.../property/storage/OBSPropertyTest.java | 8 +++---
.../property/storage/OSSPropertiesTest.java | 7 ++---
.../property/storage/S3PropertiesTest.java | 3 ++-
9 files changed, 62 insertions(+), 27 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogProperty.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogProperty.java
index 6e5d5281796..0ad7b66c39b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogProperty.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogProperty.java
@@ -53,6 +53,21 @@ public class CatalogProperty {
@SerializedName(value = "properties")
private Map<String, String> properties;
+ /**
+ * An ordered list of all initialized {@link StorageProperties} instances.
+ * <p>
+ * The order of this list is significant:
+ * <ul>
+ * <li>The default HDFSProperties (if auto-created) is always inserted
at index 0.</li>
+ * <li>Explicitly configured storage providers follow in the order they
are detected.</li>
+ * <li>Callers rely on this deterministic ordering for selecting or
iterating through
+ * storage backends.</li>
+ * </ul>
+ * <p>
+ * Declared as {@code volatile} to ensure visibility across threads once
initialized.
+ */
+ private volatile List<StorageProperties> orderedStoragePropertiesList;
+
// Lazy-loaded storage properties map, using volatile to ensure visibility
private volatile Map<StorageProperties.Type, StorageProperties>
storagePropertiesMap;
@@ -137,13 +152,13 @@ public class CatalogProperty {
/**
* Get storage properties map with lazy loading, using double-check
locking to ensure thread safety
*/
- public Map<StorageProperties.Type, StorageProperties>
getStoragePropertiesMap() {
+ private void initStorageProperties() {
if (storagePropertiesMap == null) {
synchronized (this) {
if (storagePropertiesMap == null) {
try {
- List<StorageProperties> storageProperties =
StorageProperties.createAll(getProperties());
- this.storagePropertiesMap = storageProperties.stream()
+ this.orderedStoragePropertiesList =
StorageProperties.createAll(getProperties());
+ this.storagePropertiesMap =
orderedStoragePropertiesList.stream()
.collect(Collectors.toMap(StorageProperties::getType, Function.identity()));
} catch (UserException e) {
LOG.warn("Failed to initialize catalog storage
properties", e);
@@ -153,9 +168,18 @@ public class CatalogProperty {
}
}
}
+ }
+
+ public Map<StorageProperties.Type, StorageProperties>
getStoragePropertiesMap() {
+ initStorageProperties();
return storagePropertiesMap;
}
+ public List<StorageProperties> getOrderedStoragePropertiesList() {
+ initStorageProperties();
+ return orderedStoragePropertiesList;
+ }
+
public void checkMetaStoreAndStorageProperties(Class msClass) {
MetastoreProperties msProperties;
List<StorageProperties> storageProperties;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergExternalCatalog.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergExternalCatalog.java
index 02d24cc0146..90d7ab41ebe 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergExternalCatalog.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergExternalCatalog.java
@@ -34,7 +34,6 @@ import org.apache.doris.transaction.TransactionManagerFactory;
import org.apache.iceberg.catalog.Catalog;
-import java.util.ArrayList;
import java.util.List;
public abstract class IcebergExternalCatalog extends ExternalCatalog {
@@ -60,8 +59,8 @@ public abstract class IcebergExternalCatalog extends
ExternalCatalog {
protected void initCatalog() {
try {
msProperties = (AbstractIcebergProperties)
catalogProperty.getMetastoreProperties();
- this.catalog = msProperties.initializeCatalog(getName(), new
ArrayList<>(catalogProperty
- .getStoragePropertiesMap().values()));
+ this.catalog = msProperties.initializeCatalog(getName(),
catalogProperty
+ .getOrderedStoragePropertiesList());
this.icebergCatalogType = msProperties.getIcebergCatalogType();
} catch (ClassCastException e) {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonExternalCatalog.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonExternalCatalog.java
index 76e093656bb..09ec08e904d 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonExternalCatalog.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonExternalCatalog.java
@@ -173,7 +173,7 @@ public class PaimonExternalCatalog extends ExternalCatalog {
protected Catalog createCatalog() {
try {
return paimonProperties.initializeCatalog(getName(), new
ArrayList<>(catalogProperty
- .getStoragePropertiesMap().values()));
+ .getOrderedStoragePropertiesList()));
} catch (Exception e) {
throw new RuntimeException("Failed to create catalog, catalog
name: " + getName() + ", exception: "
+ ExceptionUtils.getRootCauseMessage(e), e);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/StorageProperties.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/StorageProperties.java
index 3e8a703204b..5de749f45d6 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/StorageProperties.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/StorageProperties.java
@@ -56,7 +56,7 @@ public abstract class StorageProperties extends
ConnectionProperties {
public static final String FS_PROVIDER_KEY = "provider";
- protected final String userFsPropsPrefix = "fs.";
+ protected final String userFsPropsPrefix = "fs.";
public enum Type {
HDFS,
@@ -118,12 +118,19 @@ public abstract class StorageProperties extends
ConnectionProperties {
/**
* Creates a list of StorageProperties instances based on the provided
properties.
* <p>
- * This method iterates through the list of supported storage types and
creates an instance
- * for each supported type. If no supported type is found, an
HDFSProperties instance is added
- * by default.
+ * This method iterates through all registered storage providers and
constructs one
+ * {@link StorageProperties} instance for each provider that recognizes
the given properties.
+ * <p>
+ * If no HDFSProperties is explicitly configured, a default HDFSProperties
will be added
+ * automatically. The default HDFSProperties is inserted at index 0 to
ensure that:
+ * <ul>
+ * <li>The list preserves a deterministic order (it is an ordered
List).</li>
+ * <li>The default HDFS configuration does not override or shadow
explicitly configured
+ * object storage providers, which are appended after detection.</li>
+ * </ul>
*
- * @param origProps the original properties map to create the
StorageProperties instances
- * @return a list of StorageProperties instances for all supported storage
types
+ * @param origProps the raw property map used to initialize each
StorageProperties instance
+ * @return an ordered list of StorageProperties instances
*/
public static List<StorageProperties> createAll(Map<String, String>
origProps) throws UserException {
List<StorageProperties> result = new ArrayList<>();
@@ -135,7 +142,7 @@ public abstract class StorageProperties extends
ConnectionProperties {
}
// Add default HDFS storage if not explicitly configured
if (result.stream().noneMatch(HdfsProperties.class::isInstance)) {
- result.add(new HdfsProperties(origProps, false));
+ result.add(0, new HdfsProperties(origProps, false));
}
for (StorageProperties storageProperties : result) {
@@ -203,7 +210,7 @@ public abstract class StorageProperties extends
ConnectionProperties {
|| LocalProperties.guessIsMe(props)) ? new
LocalProperties(props) : null,
props -> (isFsSupport(props, FS_HTTP_SUPPORT)
|| HttpProperties.guessIsMe(props)) ? new
HttpProperties(props) : null
- );
+ );
protected StorageProperties(Type type, Map<String, String> origProps) {
super(origProps);
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/AzurePropertiesTest.java
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/AzurePropertiesTest.java
index 927a9f1c148..8171f33db40 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/AzurePropertiesTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/AzurePropertiesTest.java
@@ -80,8 +80,8 @@ public class AzurePropertiesTest {
origProps.put("s3.secret_key", "myAzureSecretKey");
List<StorageProperties> storagePropertiesList =
StorageProperties.createAll(origProps);
Assertions.assertEquals(2, storagePropertiesList.size());
- Assertions.assertEquals(HdfsProperties.class,
storagePropertiesList.get(1).getClass());
- Assertions.assertEquals(AzureProperties.class,
storagePropertiesList.get(0).getClass());
+ Assertions.assertEquals(HdfsProperties.class,
storagePropertiesList.get(0).getClass());
+ Assertions.assertEquals(AzureProperties.class,
storagePropertiesList.get(1).getClass());
origProps.put("s3.endpoint", "https://mystorageaccount.net");
// Expect an exception due to missing provider
origProps.put("provider", "azure");
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 df20ea5d333..d4b0277c946 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
@@ -56,7 +56,7 @@ public class COSPropertiesTest {
origProps.put("test_non_storage_param", "6000");
Assertions.assertDoesNotThrow(() ->
StorageProperties.createAll(origProps));
origProps.put("cos.endpoint", "cos.ap-beijing-1.myqcloud.com");
- COSProperties cosProperties = (COSProperties)
StorageProperties.createAll(origProps).get(0);
+ COSProperties cosProperties = (COSProperties)
StorageProperties.createAll(origProps).get(1);
Map<String, String> cosConfig = cosProperties.getMatchedProperties();
Assertions.assertTrue(!cosConfig.containsKey("test_non_storage_param"));
@@ -90,7 +90,7 @@ public class COSPropertiesTest {
origProps.put(StorageProperties.FS_COS_SUPPORT, "true");
//origProps.put("cos.region", "ap-beijing");
- COSProperties cosProperties = (COSProperties)
StorageProperties.createAll(origProps).get(0);
+ COSProperties cosProperties = (COSProperties)
StorageProperties.createAll(origProps).get(1);
Map<String, String> s3Props =
cosProperties.generateBackendS3Configuration();
Map<String, String> cosConfig = cosProperties.getMatchedProperties();
Assertions.assertTrue(!cosConfig.containsKey("test_non_storage_param"));
@@ -110,7 +110,8 @@ public class COSPropertiesTest {
Assertions.assertEquals("1000",
s3Props.get("AWS_CONNECTION_TIMEOUT_MS"));
Assertions.assertEquals("false", s3Props.get("use_path_style"));
origProps.put("cos.use_path_style", "true");
- cosProperties = (COSProperties)
StorageProperties.createAll(origProps).get(0);
+ cosProperties = (COSProperties)
StorageProperties.createAll(origProps).get(1);
+ Assertions.assertEquals(HdfsProperties.class,
StorageProperties.createAll(origProps).get(0).getClass());
s3Props = cosProperties.generateBackendS3Configuration();
Assertions.assertEquals("true", s3Props.get("use_path_style"));
// Add any additional assertions for other properties if needed
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 4329368254a..06c2b36cf7d 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
@@ -70,7 +70,8 @@ public class OBSPropertyTest {
origProps.put("obs.use_path_style", "true");
origProps.put("test_non_storage_param", "test_non_storage_value");
origProps.put(StorageProperties.FS_OBS_SUPPORT, "true");
- OBSProperties obsProperties = (OBSProperties)
StorageProperties.createAll(origProps).get(0);
+ OBSProperties obsProperties = (OBSProperties)
StorageProperties.createAll(origProps).get(1);
+ Assertions.assertEquals(HdfsProperties.class,
StorageProperties.createAll(origProps).get(0).getClass());
Map<String, String> s3Props = new HashMap<>();
Map<String, String> obsConfig = obsProperties.getMatchedProperties();
Assertions.assertTrue(!obsConfig.containsKey("test_non_storage_param"));
@@ -91,7 +92,7 @@ public class OBSPropertyTest {
Assertions.assertEquals("1000",
s3Props.get("AWS_CONNECTION_TIMEOUT_MS"));
Assertions.assertEquals("true", s3Props.get("use_path_style"));
origProps.remove("obs.use_path_style");
- obsProperties = (OBSProperties)
StorageProperties.createAll(origProps).get(0);
+ obsProperties = (OBSProperties)
StorageProperties.createAll(origProps).get(1);
s3Props = obsProperties.getBackendConfigProperties();
Assertions.assertEquals("false", s3Props.get("use_path_style"));
}
@@ -102,7 +103,8 @@ public class OBSPropertyTest {
origProps.put("obs.endpoint", "obs.cn-north-4.myhuaweicloud.com");
origProps.put("obs.access_key", "myOBSAccessKey");
origProps.put("obs.secret_key", "myOBSSecretKey");
- OBSProperties obsProperties = (OBSProperties)
StorageProperties.createAll(origProps).get(0);
+ OBSProperties obsProperties = (OBSProperties)
StorageProperties.createAll(origProps).get(1);
+ Assertions.assertEquals(HdfsProperties.class,
StorageProperties.createAll(origProps).get(0).getClass());
Assertions.assertEquals("cn-north-4", obsProperties.getRegion());
Assertions.assertEquals("myOBSAccessKey",
obsProperties.getAccessKey());
Assertions.assertEquals("myOBSSecretKey",
obsProperties.getSecretKey());
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 0bc5e823f0e..dbf34751742 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
@@ -70,7 +70,8 @@ public class OSSPropertiesTest {
origProps.put("oss.connection.timeout", "1000");
origProps.put("oss.use_path_style", "true");
origProps.put("test_non_storage_param", "6000");
- OSSProperties ossProperties = (OSSProperties)
StorageProperties.createAll(origProps).get(0);
+ OSSProperties ossProperties = (OSSProperties)
StorageProperties.createAll(origProps).get(1);
+ Assertions.assertEquals(HdfsProperties.class,
StorageProperties.createAll(origProps).get(0).getClass());
Map<String, String> s3Props;
Map<String, String> ossConfig = ossProperties.getMatchedProperties();
@@ -93,7 +94,7 @@ public class OSSPropertiesTest {
Assertions.assertEquals("1000",
s3Props.get("AWS_CONNECTION_TIMEOUT_MS"));
Assertions.assertEquals("true", s3Props.get("use_path_style"));
origProps.remove("oss.use_path_style");
- ossProperties = (OSSProperties)
StorageProperties.createAll(origProps).get(0);
+ ossProperties = (OSSProperties)
StorageProperties.createAll(origProps).get(1);
s3Props = ossProperties.generateBackendS3Configuration();
Assertions.assertEquals("false", s3Props.get("use_path_style"));
}
@@ -120,7 +121,7 @@ public class OSSPropertiesTest {
origProps.put("oss.endpoint",
"http://s3.oss-cn-hongkong.aliyuncs.com");
Assertions.assertEquals("cn-hongkong", ((OSSProperties)
StorageProperties.createPrimary(origProps)).getRegion());
origProps.put("oss.endpoint", "https://dlf.cn-beijing.aliyuncs.com");
- Assertions.assertEquals("cn-beijing", ((OSSProperties)
StorageProperties.createAll(origProps).get(0)).getRegion());
+ Assertions.assertEquals("cn-beijing", ((OSSProperties)
StorageProperties.createAll(origProps).get(1)).getRegion());
origProps.put("oss.endpoint", "datalake-vpc.cn-shenzhen.aliyuncs.com");
Assertions.assertEquals("cn-shenzhen", ((OSSProperties)
StorageProperties.createPrimary(origProps)).getRegion());
origProps.put("oss.endpoint",
"https://datalake-vpc.cn-shenzhen.aliyuncs.com");
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 f4ea2fcb381..0c279df3368 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
@@ -130,7 +130,8 @@ public class S3PropertiesTest {
origProps.put("s3.connection.timeout", "6000");
origProps.put("test_non_storage_param", "6000");
origProps.put("s3.endpoint", "s3.us-west-1.amazonaws.com");
- S3Properties s3Properties = (S3Properties)
StorageProperties.createAll(origProps).get(0);
+ S3Properties s3Properties = (S3Properties)
StorageProperties.createAll(origProps).get(1);
+ Assertions.assertEquals(HdfsProperties.class,
StorageProperties.createAll(origProps).get(0).getClass());
Map<String, String> s3Props =
s3Properties.getBackendConfigProperties();
Map<String, String> s3Config = s3Properties.getMatchedProperties();
Assertions.assertTrue(!s3Config.containsKey("test_non_storage_param"));
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]