This is an automated email from the ASF dual-hosted git repository.

hello-stephen 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 6e79ef0c714 branch-4.0: [fix](docker)(case) Fix the remaining 4 
external regression failures: kerberos HDFS SASL, Paimon fixture, file cache 
flakiness (#66417)
6e79ef0c714 is described below

commit 6e79ef0c714871bb8bcf991d9423533801258b6a
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Tue Aug 4 23:04:21 2026 +0800

    branch-4.0: [fix](docker)(case) Fix the remaining 4 external regression 
failures: kerberos HDFS SASL, Paimon fixture, file cache flakiness (#66417)
    
    Related PR: #65939, #66254
    
    Problem Summary:
    
    Follow-up to #66254. The daily `Regression External` pipeline for
    `branch-4.0`
    (build 482/483) still fails 4 cases. None of them is a runtime problem:
    the
    cluster is healthy, 522 cases pass, and there is no crash, OOM or core
    dump.
    Two root causes remain, both of them the tail of #65939.
    
    **1. Kerberos HDFS writes fail (3 p2 cases, red on every run since
    #65939)**
    
    `hive_on_hms_and_dlf` fails with
    
        could only be written to 0 of the 1 minReplication nodes.
    There are 1 datanode(s) running and 1 node(s) are excluded in this
    operation.
    
    and `iceberg_on_hms_and_filesystem_and_dlf` fails with
    `IllegalArgumentException: Self-suppression not permitted`, which is
    what
    `TableMetadataParser.internalWrite` turns the same failure into.
    
    The DataNode is alive — the client excluded it. `jni.log` shows why:
    `java.net.SocketException: Connection reset` in
    `DataStreamer.createBlockOutputStream`. Connection **reset**, not
    refused: the
    socket was established and the DataNode closed it. #65939 created
    `kerberos/conf/hdfs-site.xml.tpl` — the previous environment carried its
    server
    configuration inside the image, so this file is new — and it sets
    `dfs.data.transfer.protection=authentication`. A client that does not
    set the
    same property sends a plain `writeBlock` op, `SaslDataTransferServer`
    does not
    find the SASL magic number and closes the socket, the client excludes
    the only
    DataNode, and the next `addBlock` has no target left. Metadata
    operations only
    reach the NameNode and the HMS, so `CREATE DATABASE` / `CREATE TABLE`
    still
    succeed and only the write fails.
    
    The same commit added the client property to the four
    `external_table_p0/kerberos`
    cases but not to the p2 cases that talk to the same kerberized HDFS.
    Both suites
    were green through build 468 and red from build 469, the first run after
    #65939.
    They were held at `GeneralSecurityException: Checksum failed` until
    #66254 fixed
    the keytab, which is why this second gap only surfaced now.
    
    Fix: set `dfs.data.transfer.protection` in every property block that
    points at
    the kerberized HDFS. A scan of `:8520` / `:8620` across the whole suite
    tree found
    seven such blocks still missing it: two each in `hive_on_hms_and_dlf`,
    `iceberg_on_hms_and_filesystem_and_dlf` and `test_paimon_hms_catalog`,
    plus one
    in `hdfs_all_test`, which writes to HDFS but currently returns early
    because
    `refactor_params_hdfs_kerberos_test` is unset — it would fail the moment
    it is
    enabled. The kerberos block of `test_information_schema_timezone` has
    the same
    gap but is disabled by an unrelated TODO, so it is left alone.
    
    **2. `test_paimon_hms_catalog`: `Unknown database 'hdfs_db'`**
    
    The lightweight Kerberos environment from #65939 starts an empty
    metastore —
    `schematool -dbType derby -initSchema` and nothing else. The environment
    it
    replaced provisioned the Paimon fixture on every start (`hadoop fs -put
    /tmp/paimon_data/*`, `create_paimon_hive_table.hql`, and the
    Paimon/jindo jars in
    the image's hive auxlib). None of that survived;
    `paimon_data/hdfs_db.db/` is
    still in the tree but the compose file never mounted it.
    
    This restores the fixture on `kerberos1` only — it owns metastore 9583,
    the only
    one the suite talks to, so `kerberos2` stays as light as it is today.
    `hdfs_db` is
    backed by `paimon_data`; `ali_db` carries no data and points at the same
    OSS
    warehouse the Hive3 stack registers under that name, so the two
    metastores share
    one copy. Registering it still needs the `oss://` scheme to resolve,
    because
    `HiveMetaStore.create_table_core` puts any explicit LOCATION through
    `Warehouse.getDnsPath` → `Path.getFileSystem`; hence the jindo
    filesystem settings
    in `hive-site.xml` and the aliyun jars next to the Paimon one. The
    fixture runs
    before `DORIS_KERBEROS_READY`, so nothing races the suites, and a
    failure aborts
    the container instead of handing out an environment silently missing the
    two
    databases.
    
    The golden output already contains the `hdfs_kerberos`,
    `hdfs_new_kerberos` and
    `oss_hms_kerberos` blocks, so no baseline has to be regenerated.
    
    **3. `test_file_cache_statistics` (flaky, ~25% pass rate)**
    
    #66254 relaxed the upper bound after build 474 reported
    `normal_queue_curr_size`
    2.44e8 above a `max_size` of 1.68e8. Build 482 fails the other way
    round, with
    `normal_queue_curr_size` exactly 0 while `hits_ratio` is 3.76 — the
    queue is empty
    rather than over its share.
    
    `test_file_cache_features` runs immediately before it and drives every
    backend into
    `disk_resource_limit_mode` and `need_evict_cache_in_advance` on purpose,
    by lowering
    the enter thresholds to 2%. Both modes drain the shared cache, and while
    `disk_resource_limit_mode` is on `BlockFileCache::try_reserve` admits
    nothing: it
    multiplies the requested size by 5 and only succeeds if it evicted that
    much, which
    an already drained cache never can. `setBeConfigTemporary` restores the
    configs, but
    the backend only recomputes the two flags in `run_background_monitor`,
    once per
    `file_cache_background_monitor_interval_ms` (5s in this pipeline). The
    next suite
    starts 37ms later, so its warm-up queries can land inside that window
    and cache
    nothing — and since the case then only polls a passive metric, it can
    never recover.
    Both polls time out 30s later, which matches the log exactly.
    
    Fix, in two parts:
    - `test_file_cache_features` waits until both metrics are back to 0
    before it returns,
    so it leaves the cluster in the state it found. The wait uses `max()`
    rather than
    `limit 1`, because one row is one arbitrary (backend, cache_path) pair
    and any path
      still in the mode reports a 1.
    - `test_file_cache_statistics` re-runs the read on every poll of the
    normal queue
    metrics, the same self-healing shape its hit/read-count section already
    uses.
    
    The upper bound from #66254 is kept: build 474 shows that mode is real
    too.
    
    **4. `IcebergMetadataOps`: report the root cause of failed metadata
    commits**
    
    While diagnosing (1), the failure surfaced to the user as
    `Failed to create table: ..., error message is:Self-suppression not
    permitted`,
    which says nothing about what went wrong.
    `TableMetadataParser.internalWrite`
    writes the metadata JSON inside try-with-resources, and
    `DFSOutputStream` throws
    the same exception instance from `write` and from `close` once its
    pipeline has
    failed, so the generated `addSuppressed` call becomes
    `addSuppressed(this)` and
    `Throwable` replaces the failure with `IllegalArgumentException`. Every
    path that
    commits Iceberg metadata goes through the same parser and can be masked
    the same
    way. `addPartitionField`, `dropPartitionField` and
    `replacePartitionField` already
    report `ExceptionUtils.getRootCauseMessage(e)`; this applies it to the
    remaining
    eleven commit paths so all fourteen agree. The surrounding message is a
    literal
    prefix that already names the operation and the object, so replacing
    only the
    trailing part loses no context, and no regression case asserts on this
    text.
    
    
    Signed-off-by: morningman <[email protected]>
---
 .../docker-compose/kerberos/conf/hive-site.xml.tpl | 27 +++++++++
 .../kerberos/entrypoint-hive-master.sh             | 31 +++++++++++
 .../docker-compose/kerberos/hadoop-hive.env.tpl    |  8 +++
 .../docker-compose/kerberos/kerberos.yaml.tpl      |  6 ++
 .../kerberos/sql/create_paimon_hive_table.hql      | 32 +++++++++++
 docker/thirdparties/run-thirdparties-docker.sh     | 65 +++++++++++++++++++++-
 .../datasource/iceberg/IcebergMetadataOps.java     | 26 ++++-----
 .../cache/test_file_cache_features.groovy          | 34 +++++++++++
 .../cache/test_file_cache_statistics.groovy        | 26 ++++++++-
 .../refactor_storage_param/hdfs_all_test.groovy    |  1 +
 .../paimon/test_paimon_hms_catalog.groovy          |  2 +
 .../hive_on_hms_and_dlf.groovy                     |  2 +
 .../iceberg_on_hms_and_filesystem_and_dlf.groovy   |  2 +
 13 files changed, 245 insertions(+), 17 deletions(-)

diff --git a/docker/thirdparties/docker-compose/kerberos/conf/hive-site.xml.tpl 
b/docker/thirdparties/docker-compose/kerberos/conf/hive-site.xml.tpl
index 2b4b41e20a7..9c46c326f24 100644
--- a/docker/thirdparties/docker-compose/kerberos/conf/hive-site.xml.tpl
+++ b/docker/thirdparties/docker-compose/kerberos/conf/hive-site.xml.tpl
@@ -62,4 +62,31 @@ under the License.
         <name>metastore.storage.schema.reader.impl</name>
         
<value>org.apache.hadoop.hive.metastore.SerDeStorageSchemaReader</value>
     </property>
+    <!--
+      Needed by the ali_db Paimon fixture: HiveMetaStore.create_table_core 
resolves
+      any explicit LOCATION through Warehouse.getDnsPath -> 
Path.getFileSystem, so
+      registering a table at oss:// requires the scheme to be resolvable at 
DDL time.
+      Matches the Hive3 stack (HIVE_SITE_CONF_fs_oss_impl in 
hadoop-hive-3x.env.tpl);
+      the jindo jars come from the auxlib mount. Inert when nothing touches 
oss://.
+    -->
+    <property>
+        <name>fs.oss.impl</name>
+        <value>com.aliyun.jindodata.oss.JindoOssFileSystem</value>
+    </property>
+    <property>
+        <name>fs.AbstractFileSystem.oss.impl</name>
+        <value>com.aliyun.jindodata.oss.JindoOSS</value>
+    </property>
+    <property>
+        <name>fs.oss.accessKeyId</name>
+        <value>${OSSAk}</value>
+    </property>
+    <property>
+        <name>fs.oss.accessKeySecret</name>
+        <value>${OSSSk}</value>
+    </property>
+    <property>
+        <name>fs.oss.endpoint</name>
+        <value>${OSSEndpoint}</value>
+    </property>
 </configuration>
diff --git 
a/docker/thirdparties/docker-compose/kerberos/entrypoint-hive-master.sh 
b/docker/thirdparties/docker-compose/kerberos/entrypoint-hive-master.sh
index 6735ae7ee40..db7f642c0cd 100644
--- a/docker/thirdparties/docker-compose/kerberos/entrypoint-hive-master.sh
+++ b/docker/thirdparties/docker-compose/kerberos/entrypoint-hive-master.sh
@@ -136,9 +136,40 @@ kdestroy
 
 report_stage "initialize-hive-metastore"
 schematool -dbType derby -initSchema
+# The Paimon tables declare no columns, so the metastore itself resolves their
+# schema through the storage handler, and registering ali_db at an oss:// 
location
+# makes it resolve that scheme too. Both classes must therefore be on the
+# metastore service's own classpath, not merely on the DDL client's.
+if [[ -d /opt/doris/auxlib ]]; then
+    export HIVE_AUX_JARS_PATH=/opt/doris/auxlib
+fi
 start_service hive --service metastore -p "${HMS_PORT}"
 wait_for_port "${HOST}" "${HMS_PORT}" "Hive Metastore"
 
+# Register the Paimon fixture consumed by test_paimon_hms_catalog. This runs
+# before the readiness marker below on purpose: run-thirdparties-docker.sh
+# releases the pipeline on DORIS_KERBEROS_READY, so anything published later
+# would race the suites. A failure here aborts the container (set -e) instead 
of
+# handing out an environment that is silently missing hdfs_db / ali_db.
+#
+# Both kerberos containers run this entrypoint with the same rendered env
+# switch, but the fixture and its mounts (sql/, paimon_data/, auxlib/) belong 
to
+# kerberos1 only - its metastore (9583) is the one the suite talks to. Gate on
+# the container role, not on the mounts, so a broken mount on kerberos1 still
+# fails loudly while kerberos2 skips deterministically.
+if [[ "${enablePaimonHms:-false}" == "true" && "${HOST:-}" == "hadoop-master" 
]]; then
+    report_stage "load-paimon-hms"
+    export KRB5CCNAME=FILE:/tmp/hive-admin.ccache
+    kinit -kt /data/keytabs/hive.keytab "${HIVE_PRINCIPAL}"
+    hdfs dfs -mkdir -p /user/hive/warehouse
+    hdfs dfs -put -f /opt/doris/paimon_data/* /user/hive/warehouse/
+    paimon_hql="$(mktemp /tmp/create_paimon_hive_table.XXXXXX.hql)"
+    sed "s|__OSS_BUCKET__|${OSSBucket}|g" 
/opt/doris/sql/create_paimon_hive_table.hql >"${paimon_hql}"
+    hive -f "${paimon_hql}"
+    rm -f "${paimon_hql}"
+    kdestroy
+fi
+
 touch /tmp/SUCCESS
 echo "Minimal Kerberos HDFS and Hive Metastore environment is ready"
 echo "DORIS_KERBEROS_READY"
diff --git a/docker/thirdparties/docker-compose/kerberos/hadoop-hive.env.tpl 
b/docker/thirdparties/docker-compose/kerberos/hadoop-hive.env.tpl
index 792d0c0491d..e1f5e7863f9 100644
--- a/docker/thirdparties/docker-compose/kerberos/hadoop-hive.env.tpl
+++ b/docker/thirdparties/docker-compose/kerberos/hadoop-hive.env.tpl
@@ -29,3 +29,11 @@ PRESTO_CLIENT_KEYTAB=${PRESTO_CLIENT_KEYTAB}
 HADOOP_CONF_DIR=/opt/doris/conf
 HIVE_CONF_DIR=/opt/doris/conf
 KRB5_CONFIG=/etc/krb5.conf
+# Paimon-on-kerberized-HMS fixture. These four are read from 
hive-3x_settings.env
+# by start_kerberos() rather than duplicated into kerberos*_settings.env, so 
that
+# the pipeline keeps patching exactly one file and the two stacks cannot drift.
+enablePaimonHms=${enablePaimonHms}
+OSSBucket=${OSSBucket}
+OSSAk=${OSSAk}
+OSSSk=${OSSSk}
+OSSEndpoint=${OSSEndpoint}
diff --git a/docker/thirdparties/docker-compose/kerberos/kerberos.yaml.tpl 
b/docker/thirdparties/docker-compose/kerberos/kerberos.yaml.tpl
index d21496516af..8e903ed6d7f 100644
--- a/docker/thirdparties/docker-compose/kerberos/kerberos.yaml.tpl
+++ b/docker/thirdparties/docker-compose/kerberos/kerberos.yaml.tpl
@@ -33,11 +33,17 @@ services:
     <<: *kerberos-service
     container_name: doris-${CONTAINER_UID}-kerberos1
     hostname: hadoop-master
+    # auxlib/sql/paimon_data are mounted on kerberos1 only: 
test_paimon_hms_catalog
+    # is the sole consumer and it talks to this metastore (9583). Leaving 
kerberos2
+    # untouched keeps the second container as light as it was.
     volumes:
       - ./conf/kerberos1:/opt/doris/conf:ro
       - ./conf/kerberos1/krb5.conf:/etc/krb5.conf:ro
       - ./data/kerberos1:/data
       - ./two-kerberos-hives:/keytabs
+      - ./auxlib:/opt/doris/auxlib:ro
+      - ./sql:/opt/doris/sql:ro
+      - ./paimon_data:/opt/doris/paimon_data:ro
     env_file:
       - ./hadoop-hive-1.env
 
diff --git 
a/docker/thirdparties/docker-compose/kerberos/sql/create_paimon_hive_table.hql 
b/docker/thirdparties/docker-compose/kerberos/sql/create_paimon_hive_table.hql
new file mode 100644
index 00000000000..6bdbfc9f882
--- /dev/null
+++ 
b/docker/thirdparties/docker-compose/kerberos/sql/create_paimon_hive_table.hql
@@ -0,0 +1,32 @@
+-- Paimon tables registered in the kerberized Hive Metastore, consumed by
+-- external_table_p2/paimon/test_paimon_hms_catalog.groovy.
+--
+-- hdfs_db is backed by ../paimon_data, which the entrypoint uploads into HDFS.
+-- ali_db carries no data of its own: it points at the same OSS warehouse the
+-- Hive3 stack registers as ali_db, so this is a second metastore entry over 
one
+-- copy of the data. __OSS_BUCKET__ is substituted with ${OSSBucket}.
+--
+-- Neither table declares columns, so the metastore derives them through the
+-- Paimon storage handler (metastore.storage.schema.reader.impl is
+-- SerDeStorageSchemaReader) -- the Paimon jar has to be on the metastore's own
+-- classpath, not just on the classpath of whoever runs this script.
+
+CREATE DATABASE IF NOT EXISTS hdfs_db;
+
+USE hdfs_db;
+
+DROP TABLE IF EXISTS external_test_table;
+
+CREATE EXTERNAL TABLE external_test_table
+    STORED BY 'org.apache.paimon.hive.PaimonStorageHandler'
+LOCATION 'hdfs:///user/hive/warehouse/hdfs_db.db/external_test_table';
+
+CREATE DATABASE IF NOT EXISTS ali_db;
+
+USE ali_db;
+
+DROP TABLE IF EXISTS external_test_table;
+
+CREATE EXTERNAL TABLE external_test_table
+    STORED BY 'org.apache.paimon.hive.PaimonStorageHandler'
+LOCATION 
'oss://__OSS_BUCKET__/regression/paimon_warehouse/ali_db.db/hive_test_table';
diff --git a/docker/thirdparties/run-thirdparties-docker.sh 
b/docker/thirdparties/run-thirdparties-docker.sh
index 9ad5239e4ab..1c803e502d7 100755
--- a/docker/thirdparties/run-thirdparties-docker.sh
+++ b/docker/thirdparties/run-thirdparties-docker.sh
@@ -1575,6 +1575,64 @@ validate_kerberos_container() {
     fi
 }
 
+# Aux jars for the Paimon fixture in the kerberized metastore: the Paimon 
storage
+# handler, plus the jindo OSS filesystem that ali_db's oss:// location has to
+# resolve at DDL time. Same source and same skip-if-present caching the Hive3
+# stack uses in prepare-hive-data.sh.
+download_kerberos_paimon_jars() {
+    local auxlib_dir="$1"
+    local 
url_prefix="https://${s3BucketName}.${s3Endpoint}/regression/docker/hive3";
+    local jars=(
+        paimon-hive-connector-3.1-1.3-SNAPSHOT.jar
+        jdom-1.1.jar
+        aliyun-java-sdk-core-3.4.0.jar
+        aliyun-java-sdk-ecs-4.2.0.jar
+        aliyun-java-sdk-ram-3.0.0.jar
+        aliyun-java-sdk-sts-3.0.0.jar
+        jindo-core-6.3.4.jar
+        jindo-core-linux-el7-aarch64-6.3.4.jar
+        jindo-sdk-6.3.4.jar
+    )
+    local jar
+
+    for jar in "${jars[@]}"; do
+        if [[ -f "${auxlib_dir}/${jar}" ]]; then
+            echo "Reuse cached kerberos aux jar ${jar}"
+            continue
+        fi
+        echo "Download kerberos aux jar ${jar}"
+        curl -sSfL -o "${auxlib_dir}/${jar}" "${url_prefix}/${jar}"
+    done
+}
+
+# enablePaimonHms and the OSS credentials live in hive-3x_settings.env, which 
the
+# pipeline already patches with the real values. Read them from there instead 
of
+# duplicating the switch into kerberos*_settings.env, where the two copies 
would
+# drift and one stack would silently lose the fixture. Sourced in a subshell so
+# that hive3's FS_PORT / HMS_PORT cannot leak into the kerberos settings 
sourced
+# a few lines below.
+resolve_kerberos_paimon_env() {
+    local hive3_settings="${ROOT}/docker-compose/hive/hive-3x_settings.env"
+
+    enablePaimonHms="false"
+    OSSAk=""
+    OSSSk=""
+    OSSEndpoint=""
+    if [[ -f "${hive3_settings}" ]]; then
+        {
+            read -r enablePaimonHms
+            read -r OSSAk
+            read -r OSSSk
+            read -r OSSEndpoint
+        } < <(
+            . "${hive3_settings}" >/dev/null 2>&1
+            printf '%s\n%s\n%s\n%s\n' \
+                "${enablePaimonHms:-false}" "${OSSAk:-}" "${OSSSk:-}" 
"${OSSEndpoint:-}"
+        )
+    fi
+    export enablePaimonHms OSSAk OSSSk OSSEndpoint
+}
+
 start_kerberos() {
     echo "RUN_KERBEROS"
     local KERBEROS_DIR="${ROOT}/docker-compose/kerberos"
@@ -1589,8 +1647,13 @@ start_kerberos() {
 
     export CONTAINER_UID=${CONTAINER_UID}
     envsubst <"${KERBEROS_DIR}/kerberos.yaml.tpl" 
>"${KERBEROS_DIR}/kerberos.yaml"
+    # auxlib must exist even when the fixture is off: kerberos1 mounts it 
read-only.
     mkdir -p "${KERBEROS_DIR}/conf/kerberos1" "${KERBEROS_DIR}/conf/kerberos2" 
\
-        "${KERBEROS_DIR}/two-kerberos-hives"
+        "${KERBEROS_DIR}/two-kerberos-hives" "${KERBEROS_DIR}/auxlib"
+    resolve_kerberos_paimon_env
+    if [[ "${enablePaimonHms}" == "true" && "${STOP}" -ne 1 ]]; then
+        download_kerberos_paimon_jars "${KERBEROS_DIR}/auxlib"
+    fi
     for i in {1..2}; do
         . "${KERBEROS_DIR}/kerberos${i}_settings.env"
         envsubst <"${KERBEROS_DIR}/hadoop-hive.env.tpl" 
>"${KERBEROS_DIR}/hadoop-hive-${i}.env"
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergMetadataOps.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergMetadataOps.java
index 3aa26f3572c..e2e659695b6 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergMetadataOps.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergMetadataOps.java
@@ -310,9 +310,8 @@ public class IcebergMetadataOps implements 
ExternalMetadataOps {
         try {
             return executionAuthenticator.execute(() -> 
performCreateTable(createTableInfo));
         } catch (Exception e) {
-            throw new DdlException(
-                "Failed to create table: " + createTableInfo.getTableName() + 
", error message is:" + e.getMessage(),
-                    e);
+            throw new DdlException("Failed to create table: " + 
createTableInfo.getTableName()
+                    + ", error message is:" + 
ExceptionUtils.getRootCauseMessage(e), e);
         }
     }
 
@@ -514,7 +513,7 @@ public class IcebergMetadataOps implements 
ExternalMetadataOps {
         } catch (Exception e) {
             throw new RuntimeException(
                     "Failed to create or replace branch: " + branchName + " in 
table: " + icebergTable.name()
-                            + ", error message is: " + e.getMessage(), e);
+                            + ", error message is: " + 
ExceptionUtils.getRootCauseMessage(e), e);
         }
     }
 
@@ -578,7 +577,7 @@ public class IcebergMetadataOps implements 
ExternalMetadataOps {
         } catch (Exception e) {
             throw new RuntimeException(
                     "Failed to create or replace tag: " + tagName + " in 
table: " + icebergTable.name()
-                            + ", error message is: " + e.getMessage(), e);
+                            + ", error message is: " + 
ExceptionUtils.getRootCauseMessage(e), e);
         }
     }
 
@@ -598,7 +597,7 @@ public class IcebergMetadataOps implements 
ExternalMetadataOps {
             } catch (Exception e) {
                 throw new RuntimeException(
                         "Failed to drop tag: " + tagName + " in table: " + 
icebergTable.name()
-                                + ", error message is: " + e.getMessage(), e);
+                                + ", error message is: " + 
ExceptionUtils.getRootCauseMessage(e), e);
             }
         }
     }
@@ -619,7 +618,7 @@ public class IcebergMetadataOps implements 
ExternalMetadataOps {
             } catch (Exception e) {
                 throw new RuntimeException(
                         "Failed to drop branch: " + branchName + " in table: " 
+ icebergTable.name()
-                                + ", error message is: " + e.getMessage(), e);
+                                + ", error message is: " + 
ExceptionUtils.getRootCauseMessage(e), e);
             }
         }
     }
@@ -666,7 +665,7 @@ public class IcebergMetadataOps implements 
ExternalMetadataOps {
             executionAuthenticator.execute(() -> updateSchema.commit());
         } catch (Exception e) {
             throw new UserException("Failed to add column: " + 
column.getName() + " to table: "
-                    + icebergTable.name() + ", error message is: " + 
e.getMessage(), e);
+                    + icebergTable.name() + ", error message is: " + 
ExceptionUtils.getRootCauseMessage(e), e);
         }
         refreshTable(dorisTable, updateTime);
     }
@@ -683,7 +682,7 @@ public class IcebergMetadataOps implements 
ExternalMetadataOps {
             executionAuthenticator.execute(() -> updateSchema.commit());
         } catch (Exception e) {
             throw new UserException("Failed to add columns to table: " + 
icebergTable.name()
-                    + ", error message is: " + e.getMessage(), e);
+                    + ", error message is: " + 
ExceptionUtils.getRootCauseMessage(e), e);
         }
         refreshTable(dorisTable, updateTime);
     }
@@ -697,7 +696,7 @@ public class IcebergMetadataOps implements 
ExternalMetadataOps {
             executionAuthenticator.execute(() -> updateSchema.commit());
         } catch (Exception e) {
             throw new UserException("Failed to drop column: " + columnName + " 
from table: "
-                    + icebergTable.name() + ", error message is: " + 
e.getMessage(), e);
+                    + icebergTable.name() + ", error message is: " + 
ExceptionUtils.getRootCauseMessage(e), e);
         }
         refreshTable(dorisTable, updateTime);
     }
@@ -712,7 +711,8 @@ public class IcebergMetadataOps implements 
ExternalMetadataOps {
             executionAuthenticator.execute(() -> updateSchema.commit());
         } catch (Exception e) {
             throw new UserException("Failed to rename column: " + oldName + " 
to " + newName
-                    + " in table: " + icebergTable.name() + ", error message 
is: " + e.getMessage(), e);
+                    + " in table: " + icebergTable.name()
+                    + ", error message is: " + 
ExceptionUtils.getRootCauseMessage(e), e);
         }
         refreshTable(dorisTable, updateTime);
     }
@@ -758,7 +758,7 @@ public class IcebergMetadataOps implements 
ExternalMetadataOps {
             executionAuthenticator.execute(() -> updateSchema.commit());
         } catch (Exception e) {
             throw new UserException("Failed to modify column: " + 
column.getName() + " in table: "
-                    + icebergTable.name() + ", error message is: " + 
e.getMessage(), e);
+                    + icebergTable.name() + ", error message is: " + 
ExceptionUtils.getRootCauseMessage(e), e);
         }
         refreshTable(dorisTable, updateTime);
     }
@@ -967,7 +967,7 @@ public class IcebergMetadataOps implements 
ExternalMetadataOps {
             executionAuthenticator.execute(() -> updateSchema.commit());
         } catch (Exception e) {
             throw new UserException("Failed to reorder columns in table: " + 
icebergTable.name()
-                    + ", error message is: " + e.getMessage(), e);
+                    + ", error message is: " + 
ExceptionUtils.getRootCauseMessage(e), e);
         }
         refreshTable(dorisTable, updateTime);
     }
diff --git 
a/regression-test/suites/external_table_p0/cache/test_file_cache_features.groovy
 
b/regression-test/suites/external_table_p0/cache/test_file_cache_features.groovy
index a3485f5ff6b..4b20a927c47 100644
--- 
a/regression-test/suites/external_table_p0/cache/test_file_cache_features.groovy
+++ 
b/regression-test/suites/external_table_p0/cache/test_file_cache_features.groovy
@@ -236,6 +236,40 @@ suite("test_file_cache_features", 
"external_docker,hive,external_docker_hive,p0,
     }
     // ===== End File Cache Features Metrics Check =====
 
+    // Both setBeConfigTemporary blocks above deliberately push EVERY backend 
into
+    // disk_resource_limit_mode and need_evict_cache_in_advance. Those two 
modes drain the shared
+    // file cache, and while disk_resource_limit_mode is on 
BlockFileCache::try_reserve refuses
+    // every new block: it multiplies the requested size by 5 and admits only 
if it managed to evict
+    // that much, which an already-drained cache never can. 
setBeConfigTemporary restores the
+    // configs, but the BE only recomputes the two flags in 
run_background_monitor, i.e. once per
+    // file_cache_background_monitor_interval_ms. Returning right after the 
restore therefore hands
+    // a drained, admit-nothing cache to whatever suite runs next -- that is 
what made
+    // test_file_cache_statistics see normal_queue_curr_size == 0 and fail. 
Wait for the cluster to
+    // come back to normal before finishing so this suite leaves the state it 
found.
+    //
+    // max() (not "limit 1") because a single row is one arbitrary (backend, 
cache_path) pair: the
+    // wait has to see every path leave the mode, and any path still in it is 
a 1.
+    def cacheMetricMax = { String metricName ->
+        def r = sql """select max(cast(METRIC_VALUE as double)) from 
information_schema.file_cache_statistics
+                where METRIC_NAME = '${metricName}';"""
+        if (r.size() == 0 || r[0][0] == null) {
+            return null
+        }
+        return Double.valueOf(r[0][0].toString())
+    }
+
+    Awaitility.await()
+            .atMost((totalWaitTime * 4 + 10) as long, TimeUnit.SECONDS)
+            .pollInterval(1, TimeUnit.SECONDS)
+            .until {
+                def limitMode = cacheMetricMax('disk_resource_limit_mode')
+                def evictInAdvance = 
cacheMetricMax('need_evict_cache_in_advance')
+                logger.info("waiting for file cache modes to reset - 
disk_resource_limit_mode: " +
+                    "${limitMode}, need_evict_cache_in_advance: 
${evictInAdvance}")
+                return limitMode != null && evictInAdvance != null &&
+                    limitMode == 0.0 && evictInAdvance == 0.0
+            }
+
     sql """set global enable_file_cache=false"""
     return true
 }
\ No newline at end of file
diff --git 
a/regression-test/suites/external_table_p0/cache/test_file_cache_statistics.groovy
 
b/regression-test/suites/external_table_p0/cache/test_file_cache_statistics.groovy
index 34f4948c510..cb3f11e16c5 100644
--- 
a/regression-test/suites/external_table_p0/cache/test_file_cache_statistics.groovy
+++ 
b/regression-test/suites/external_table_p0/cache/test_file_cache_statistics.groovy
@@ -97,12 +97,17 @@ suite("test_file_cache_statistics", 
"external_docker,hive,external_docker_hive,p
     // interval after the query races the refresh. Awaitility polling waits 
only as long as needed
     // and avoids reading too soon. On timeout we swallow the exception so the 
caller's own
     // metric-specific assert below can surface the precise failure message.
-    def pollMetric = { String metricName, Closure predicate, long 
timeoutSeconds ->
+    // `warmUp`, when given, re-runs the caching query on every poll: a metric 
that only a fresh
+    // read can move must not be polled passively (see the normal-queue 
section below).
+    def pollMetric = { String metricName, Closure predicate, long 
timeoutSeconds, Closure warmUp = null ->
         try {
             Awaitility.await()
                     .atMost(timeoutSeconds, TimeUnit.SECONDS)
                     .pollInterval(1, TimeUnit.SECONDS)
                     .until {
+                        if (warmUp != null) {
+                            warmUp()
+                        }
                         def v = cacheMetricSum(metricName)
                         return v != null && predicate(v)
                     }
@@ -188,8 +193,23 @@ suite("test_file_cache_statistics", 
"external_docker,hive,external_docker_hive,p
     // The hard bound the BE actually enforces is the per-cache _capacity, and 
by construction in
     // get_file_cache_settings() capacity == normal + index + ttl + disposable 
max sizes (the
     // normal/query queue is defined as the remainder). Sum across paths and 
assert against that.
-    pollMetric('normal_queue_curr_size', { it > 0 }, metricPollTimeoutSeconds)
-    pollMetric('normal_queue_curr_elements', { it > 0 }, 
metricPollTimeoutSeconds)
+    //
+    // The warm-up queries above are NOT enough on their own to guarantee the 
queue is populated.
+    // This suite shares one file cache with every other case on the cluster, 
and the case that runs
+    // right before it (test_file_cache_features) deliberately forces the 
backends into
+    // disk_resource_limit_mode / need_evict_cache_in_advance. Those modes 
drain the cache, and while
+    // disk_resource_limit_mode is on try_reserve admits nothing (it must 
evict 5x the block size
+    // first and a drained cache has nothing to evict). The flags only clear 
on the next background
+    // monitor tick, so a warm-up that lands inside that window caches zero 
blocks -- and polling a
+    // passive metric afterwards can never recover, which is exactly how this 
case failed with
+    // normal_queue_curr_size == 0. Re-run the read on every poll so the first 
tick after the modes
+    // clear repopulates the queue, the same self-healing shape the 
hit/read-count section uses.
+    def warmUpFileCache = {
+        sql """select * from 
${catalog_name}.${ex_db_name}.parquet_partition_table
+            where l_orderkey=1 and l_partkey=1534 limit 1;"""
+    }
+    pollMetric('normal_queue_curr_size', { it > 0 }, metricPollTimeoutSeconds, 
warmUpFileCache)
+    pollMetric('normal_queue_curr_elements', { it > 0 }, 
metricPollTimeoutSeconds, warmUpFileCache)
 
     def normalQueueCurrSizeSum = cacheMetricSum('normal_queue_curr_size')
     logger.info("normal_queue_curr_size sum: " + normalQueueCurrSizeSum)
diff --git 
a/regression-test/suites/external_table_p0/refactor_storage_param/hdfs_all_test.groovy
 
b/regression-test/suites/external_table_p0/refactor_storage_param/hdfs_all_test.groovy
index 25ca5ebf6da..dc5eb7ea662 100644
--- 
a/regression-test/suites/external_table_p0/refactor_storage_param/hdfs_all_test.groovy
+++ 
b/regression-test/suites/external_table_p0/refactor_storage_param/hdfs_all_test.groovy
@@ -77,6 +77,7 @@ suite("refactor_params_hdfs_all_test", 
"p0,external,kerberos,external_docker,ext
     def hdfsNonXmlParams = "\"fs.defaultFS\" = 
\"hdfs://${externalEnvIp}:8520\",\n" +
             "\"dfs.namenode.kerberos.principal\" = 
\"hdfs/[email protected]\",\n" +
             "\"dfs.client.use.datanode.hostname\" = \"true\",\n" +
+            "\"dfs.data.transfer.protection\" = \"authentication\",\n" +
             "\"hadoop.security.token.service.use_ip\" = \"false\",\n" +
             "\"hadoop.kerberos.min.seconds.before.relogin\" = \"5\",\n" +
             "\"hadoop.security.authentication\" = \"kerberos\",\n" +
diff --git 
a/regression-test/suites/external_table_p2/paimon/test_paimon_hms_catalog.groovy
 
b/regression-test/suites/external_table_p2/paimon/test_paimon_hms_catalog.groovy
index 01b98dc02b2..b1b8c2d155e 100644
--- 
a/regression-test/suites/external_table_p2/paimon/test_paimon_hms_catalog.groovy
+++ 
b/regression-test/suites/external_table_p2/paimon/test_paimon_hms_catalog.groovy
@@ -124,6 +124,7 @@ suite("test_paimon_hms_catalog", 
"p2,external,paimon,new_catalog_property") {
                 "fs.defaultFS" = "hdfs://${extHiveHmsHost}:8520",
                 "dfs.namenode.kerberos.principal" = 
"hdfs/[email protected]",
                 "dfs.client.use.datanode.hostname" = "true",
+                "dfs.data.transfer.protection" = "authentication",
                 "hadoop.security.token.service.use_ip" = "false",
                 "hadoop.security.authentication" = "kerberos",           
                 
"hadoop.kerberos.principal"="hive/[email protected]",
@@ -134,6 +135,7 @@ suite("test_paimon_hms_catalog", 
"p2,external,paimon,new_catalog_property") {
                 "fs.defaultFS" = "hdfs://${extHiveHmsHost}:8520",
                 "dfs.namenode.kerberos.principal" = 
"hdfs/[email protected]",
                 "dfs.client.use.datanode.hostname" = "true",
+                "dfs.data.transfer.protection" = "authentication",
                 "hadoop.security.token.service.use_ip" = "false",
                 "hdfs.authentication.type" = "kerberos",
                 
"hdfs.authentication.kerberos.principal"="hive/[email protected]",
diff --git 
a/regression-test/suites/external_table_p2/refactor_catalog_param/hive_on_hms_and_dlf.groovy
 
b/regression-test/suites/external_table_p2/refactor_catalog_param/hive_on_hms_and_dlf.groovy
index 74844ee1145..e074e27c352 100644
--- 
a/regression-test/suites/external_table_p2/refactor_catalog_param/hive_on_hms_and_dlf.groovy
+++ 
b/regression-test/suites/external_table_p2/refactor_catalog_param/hive_on_hms_and_dlf.groovy
@@ -420,6 +420,7 @@ suite("hive_on_hms_and_dlf", 
"p2,external,new_catalog_property") {
                 "fs.defaultFS" = "hdfs://${externalEnvIp}:8520",
                 "dfs.namenode.kerberos.principal" = 
"hdfs/[email protected]",
                 "dfs.client.use.datanode.hostname" = "true",
+                "dfs.data.transfer.protection" = "authentication",
                 "hadoop.security.token.service.use_ip" = "false",
                 "hadoop.security.authentication" = "kerberos",
              
@@ -430,6 +431,7 @@ suite("hive_on_hms_and_dlf", 
"p2,external,new_catalog_property") {
                 "fs.defaultFS" = "hdfs://${externalEnvIp}:8520",
                 "dfs.namenode.kerberos.principal" = 
"hdfs/[email protected]",
                 "dfs.client.use.datanode.hostname" = "true",
+                "dfs.data.transfer.protection" = "authentication",
                 "hadoop.security.token.service.use_ip" = "false",
                 "hdfs.authentication.type" = "kerberos",
                 
"hdfs.authentication.kerberos.principal"="hive/[email protected]",
diff --git 
a/regression-test/suites/external_table_p2/refactor_catalog_param/iceberg_on_hms_and_filesystem_and_dlf.groovy
 
b/regression-test/suites/external_table_p2/refactor_catalog_param/iceberg_on_hms_and_filesystem_and_dlf.groovy
index 32ea498a9db..65dd50e580c 100644
--- 
a/regression-test/suites/external_table_p2/refactor_catalog_param/iceberg_on_hms_and_filesystem_and_dlf.groovy
+++ 
b/regression-test/suites/external_table_p2/refactor_catalog_param/iceberg_on_hms_and_filesystem_and_dlf.groovy
@@ -485,6 +485,7 @@ suite("iceberg_on_hms_and_filesystem_and_dlf", 
"p2,external,new_catalog_property
                 "fs.defaultFS" = "hdfs://${externalEnvIp}:8520",
                 "dfs.namenode.kerberos.principal" = 
"hdfs/[email protected]",
                 "dfs.client.use.datanode.hostname" = "true",
+                "dfs.data.transfer.protection" = "authentication",
                 "hadoop.security.token.service.use_ip" = "false",
                 "hadoop.security.authentication" = "kerberos", 
                 "io-impl" = 
"org.apache.doris.datasource.iceberg.fileio.DelegateFileIO",          
@@ -496,6 +497,7 @@ suite("iceberg_on_hms_and_filesystem_and_dlf", 
"p2,external,new_catalog_property
                 "fs.defaultFS" = "hdfs://${externalEnvIp}:8520",
                 "dfs.namenode.kerberos.principal" = 
"hdfs/[email protected]",
                 "dfs.client.use.datanode.hostname" = "true",
+                "dfs.data.transfer.protection" = "authentication",
                 "hadoop.security.token.service.use_ip" = "false",
                 "io-impl" = 
"org.apache.doris.datasource.iceberg.fileio.DelegateFileIO",          
                 "hdfs.authentication.type" = "kerberos",


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to