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

hello-stephen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 6ee10af8bbd [fix](docker)(regression) Stabilize external test 
environment (#66313)
6ee10af8bbd is described below

commit 6ee10af8bbd0071cce9603f0c6db2f01ebf4c176
Author: Socrates <[email protected]>
AuthorDate: Mon Aug 3 14:53:16 2026 +0800

    [fix](docker)(regression) Stabilize external test environment (#66313)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Related PR: #66254
    
    Problem Summary:
    
    The branch-4.0 fix in #66254 also applies to master in four places:
    
    - The lightweight Kerberos environment creates principals with random
    keys on every container start, so keytabs provisioned to Doris hosts no
    longer match after the KDC is rebuilt.
    - A successful Hive baseline restore skips `init-hive-baseline.sh`,
    while the incremental Hive module list does not include the Paimon HMS
    table definitions. The restored environment therefore misses the Paimon
    databases.
    - `test_file_cache_query_limit` sends BE brpc and HTTP requests to
    `externalEnvIp`, which identifies the third-party Docker host rather
    than the backend host in a multi-host deployment.
    - `test_file_cache_statistics` treats each queue's `max_size` as a hard
    limit even though queues may borrow unused capacity from one another.
    The actual hard bound is the total file-cache capacity.
    
    This PR uses deterministic Kerberos keys, registers Paimon HMS as an
    incremental Hive module, resolves the single live backend before
    accessing BE endpoints, and validates normal-queue usage against the sum
    of all queue capacities.
    
    The ES database-name fix from #66254 is intentionally not included
    because master has already migrated this suite to external ES catalogs
    and no longer switches to the old internal P0 database.
---
 docker/thirdparties/docker-compose/hive/README.md  |  1 +
 .../thirdparties/docker-compose/hive/README_ZH.md  |  1 +
 .../docker-compose/hive/scripts/hive-module-lib.sh | 26 +++++++++++++++++++---
 .../hive/scripts/init-hive-baseline.sh             |  8 +++++--
 .../kerberos/entrypoint-hive-master.sh             | 11 +++++++--
 .../cache/test_file_cache_query_limit.groovy       | 20 ++++++++++++++---
 .../cache/test_file_cache_statistics.groovy        | 18 ++++++++++-----
 7 files changed, 70 insertions(+), 15 deletions(-)

diff --git a/docker/thirdparties/docker-compose/hive/README.md 
b/docker/thirdparties/docker-compose/hive/README.md
index 443b1d6ada7..57c6d414b1d 100644
--- a/docker/thirdparties/docker-compose/hive/README.md
+++ b/docker/thirdparties/docker-compose/hive/README.md
@@ -60,6 +60,7 @@ Modules are refreshed incrementally: only modules whose 
content SHA changed are
 | `test` | `scripts/data/test/` | Lightweight smoke-test datasets |
 | `preinstalled_hql` | `scripts/create_preinstalled_scripts/*.hql` | ~77 HQL 
files, executed in parallel via `xargs -P` |
 | `view` | `scripts/create_view_scripts/create_view.hql` | View definitions |
+| `paimon_hms` | 
`scripts/create_external_paimon_scripts/create_paimon_tables.hql` | Paimon 
tables registered in HMS (`hdfs_db`, `ali_db`, …); only selected when 
`enablePaimonHms=true` |
 
 ### Layer 3 — Version-Specific File Selection
 
diff --git a/docker/thirdparties/docker-compose/hive/README_ZH.md 
b/docker/thirdparties/docker-compose/hive/README_ZH.md
index 2b1423e387b..8cc32162dc6 100644
--- a/docker/thirdparties/docker-compose/hive/README_ZH.md
+++ b/docker/thirdparties/docker-compose/hive/README_ZH.md
@@ -60,6 +60,7 @@ Hive 启动被拆分为三层互相独立的抽象:
 | `test` | `scripts/data/test/` | 轻量级冒烟测试数据 |
 | `preinstalled_hql` | `scripts/create_preinstalled_scripts/*.hql` | 约 77 个 
HQL 文件,通过 `xargs -P` 并行执行 |
 | `view` | `scripts/create_view_scripts/create_view.hql` | View 定义 |
+| `paimon_hms` | 
`scripts/create_external_paimon_scripts/create_paimon_tables.hql` | 注册到 HMS 的 
Paimon 表(`hdfs_db`、`ali_db` 等),仅在 `enablePaimonHms=true` 时纳入 |
 
 ### Layer 3 — 按版本自动选文件
 
diff --git a/docker/thirdparties/docker-compose/hive/scripts/hive-module-lib.sh 
b/docker/thirdparties/docker-compose/hive/scripts/hive-module-lib.sh
index 1cd00099c9b..beb26b1ff2e 100644
--- a/docker/thirdparties/docker-compose/hive/scripts/hive-module-lib.sh
+++ b/docker/thirdparties/docker-compose/hive/scripts/hive-module-lib.sh
@@ -22,12 +22,19 @@ set -eo pipefail
 . /mnt/scripts/hive-common-lib.sh
 
 BOOTSTRAP_GROUPS="$(bootstrap_normalize_groups "${HIVE_BOOTSTRAP_GROUPS:-}")"
-DEFAULT_MODULES=(default multi_catalog partition_type statistics tvf 
regression test preinstalled_hql view)
+DEFAULT_MODULES=(default multi_catalog partition_type statistics tvf 
regression test preinstalled_hql view paimon_hms)
 LAST_REFRESH_DETAIL=""
 HIVE_HQL_PARALLEL="${HIVE_HQL_PARALLEL:-${LOAD_PARALLEL}}"
 
 ensure_hive_state_layout
 
+# The Paimon HMS tables need the Paimon storage handler and the object storage
+# credentials that only the Hive3 stack is configured with, so the module opts
+# in via the same flag the settings env files carry.
+paimon_hms_enabled() {
+    [[ "${enablePaimonHms:-false}" == "true" ]]
+}
+
 normalize_hive_modules() {
     local raw_modules="${1:-}"
     local cleaned_modules="${raw_modules// /}"
@@ -35,14 +42,19 @@ normalize_hive_modules() {
     local normalized=()
 
     if [[ -z "${cleaned_modules}" || "${cleaned_modules}" == "all" ]]; then
-        printf '%s\n' "${DEFAULT_MODULES[@]}"
+        for module in "${DEFAULT_MODULES[@]}"; do
+            if [[ "${module}" == "paimon_hms" ]] && ! paimon_hms_enabled; then
+                continue
+            fi
+            echo "${module}"
+        done
         return 0
     fi
 
     IFS=',' read -r -a normalized <<<"${cleaned_modules}"
     for module in "${normalized[@]}"; do
         case "${module}" in
-        
default|multi_catalog|partition_type|statistics|tvf|regression|test|preinstalled_hql|view)
+        
default|multi_catalog|partition_type|statistics|tvf|regression|test|preinstalled_hql|view|paimon_hms)
             echo "${module}"
             ;;
         *)
@@ -126,6 +138,9 @@ calc_module_sha() {
     view)
         files+=("/mnt/scripts/create_view_scripts/create_view.hql")
         ;;
+    paimon_hms)
+        
files+=("/mnt/scripts/create_external_paimon_scripts/create_paimon_tables.hql")
+        ;;
     *)
         echo "Unknown module for sha: ${module}" >&2
         return 1
@@ -314,6 +329,11 @@ refresh_module() {
         LAST_REFRESH_DETAIL="create_view.hql"
         run_hive_hql /mnt/scripts/create_view_scripts/create_view.hql 
"create_view.hql"
         ;;
+    paimon_hms)
+        LAST_REFRESH_DETAIL="create_paimon_tables.hql"
+        run_hive_hql 
/mnt/scripts/create_external_paimon_scripts/create_paimon_tables.hql \
+            "create_paimon_tables.hql"
+        ;;
     *)
         echo "Unknown module for refresh: ${module}" >&2
         return 1
diff --git 
a/docker/thirdparties/docker-compose/hive/scripts/init-hive-baseline.sh 
b/docker/thirdparties/docker-compose/hive/scripts/init-hive-baseline.sh
index 49424054fbd..02ce6c42f9c 100644
--- a/docker/thirdparties/docker-compose/hive/scripts/init-hive-baseline.sh
+++ b/docker/thirdparties/docker-compose/hive/scripts/init-hive-baseline.sh
@@ -31,6 +31,10 @@ copy_to_hdfs_if_selected "paimon1"
 copy_to_hdfs_if_selected "tvf_data"
 copy_to_hdfs_if_selected "preinstalled_data"
 
-if [[ ${enablePaimonHms:-false} == "true" ]]; then
-    run_hive_hql 
/mnt/scripts/create_external_paimon_scripts/create_paimon_tables.hql 
"create_paimon_table.hql"
+# Go through the module framework rather than calling the HQL directly: this
+# script only runs on the full-init path, while a baseline restore reaches the
+# Paimon tables through refresh-hive-modules.sh. Recording the module state
+# here also keeps the follow-up refresh pass from running the same HQL twice.
+if paimon_hms_enabled; then
+    refresh_module paimon_hms
 fi
diff --git 
a/docker/thirdparties/docker-compose/kerberos/entrypoint-hive-master.sh 
b/docker/thirdparties/docker-compose/kerberos/entrypoint-hive-master.sh
index 5dd4adf46d4..6735ae7ee40 100755
--- a/docker/thirdparties/docker-compose/kerberos/entrypoint-hive-master.sh
+++ b/docker/thirdparties/docker-compose/kerberos/entrypoint-hive-master.sh
@@ -37,6 +37,7 @@ readonly HTTP_PRINCIPAL="HTTP/${HOST}@${REALM}"
 readonly HIVE_PRINCIPAL="hive/${HOST}@${REALM}"
 readonly HIVE_CLIENT_PRINCIPAL="hive/presto-master.docker.cluster@${REALM}"
 readonly 
PRESTO_CLIENT_PRINCIPAL="presto-server/presto-master.docker.cluster@${REALM}"
+readonly PRINCIPAL_PASSWORD="doris-kerberos-test"
 
 declare -a SERVICE_PIDS=()
 
@@ -70,12 +71,18 @@ wait_for_port() {
     return 1
 }
 
+# Keys must stay identical across container rebuilds. Deployments that run
+# Doris on separate hosts from this container provision /keytabs out of band,
+# so a key that is re-randomized on every start makes every such client fail
+# the AS-REP decryption with "GeneralSecurityException: Checksum failed".
+# A fixed password plus the single fixed enctype in kdc.conf yields a stable
+# key, and -norandkey exports that key instead of rolling a new one.
 create_keytab() {
     local principal=$1
     local keytab=$2
 
-    kadmin.local -r "${REALM}" -q "addprinc -randkey ${principal}"
-    kadmin.local -r "${REALM}" -q "ktadd -k ${keytab} ${principal}"
+    kadmin.local -r "${REALM}" -q "addprinc -pw ${PRINCIPAL_PASSWORD} 
${principal}"
+    kadmin.local -r "${REALM}" -q "ktadd -k ${keytab} -norandkey ${principal}"
 }
 
 report_stage() {
diff --git 
a/regression-test/suites/external_table_p0/cache/test_file_cache_query_limit.groovy
 
b/regression-test/suites/external_table_p0/cache/test_file_cache_query_limit.groovy
index 28896bed3b9..e498101a547 100644
--- 
a/regression-test/suites/external_table_p0/cache/test_file_cache_query_limit.groovy
+++ 
b/regression-test/suites/external_table_p0/cache/test_file_cache_query_limit.groovy
@@ -51,9 +51,23 @@ suite("test_file_cache_query_limit", 
"p0,external,nonConcurrent") {
     sql """set enable_file_cache=true"""
     sql """set disable_file_cache=false"""
 
-    // Check backend configuration prerequisites
     // Note: This test case assumes a single backend scenario. Testing with 
single backend is logically equivalent
     // to testing with multiple backends having identical configurations, but 
simpler in logic.
+    // The assumption is load-bearing rather than cosmetic: the HTTP calls 
below clear and inspect one backend's
+    // file cache while the queries are served by the whole cluster, so with 
several backends the inspected cache
+    // never reflects what the query actually cached. Skip instead of 
reporting a false failure.
+    def aliveBackends = sql_return_maparray("show backends").findAll {
+        it.Alive.toString().equalsIgnoreCase("true")
+    }
+    if (aliveBackends.size() != 1) {
+        logger.info("skip test_file_cache_query_limit: it assumes a single 
backend, found ${aliveBackends.size()}")
+        return
+    }
+    // The backend HTTP/brpc endpoints must be addressed by the backend's own 
host. externalEnvIp is the
+    // third-party docker host (hive/es/...), which in a multi-host deployment 
runs no backend at all.
+    String beHost = aliveBackends[0].Host
+
+    // Check backend configuration prerequisites
     def enableFileCacheResult = sql """show backend config like 
'enable_file_cache';"""
     logger.info("enable_file_cache configuration: " + enableFileCacheResult)
     assertFalse(enableFileCacheResult.size() == 0 || 
!enableFileCacheResult[0][3].equalsIgnoreCase("true"),
@@ -136,7 +150,7 @@ suite("test_file_cache_query_limit", 
"p0,external,nonConcurrent") {
     String brpc_port = brpcPortResult[0][3]
 
     // Search file cache capacity
-    def command = ["curl", "-X", "POST", "${externalEnvIp}:${brpc_port}/vars"]
+    def command = ["curl", "-X", "POST", "${beHost}:${brpc_port}/vars"]
     def stringCommand = command.collect{it.toString()}
     def process = new ProcessBuilder(stringCommand as 
String[]).redirectErrorStream(true).start()
 
@@ -161,7 +175,7 @@ suite("test_file_cache_query_limit", 
"p0,external,nonConcurrent") {
     logger.info("========================= Start running file cache base test 
========================")
 
     // Clear file cache
-    command = ["curl", "-X", "POST", 
"${externalEnvIp}:${webserver_port}/api/file_cache?op=clear&sync=true"]
+    command = ["curl", "-X", "POST", 
"${beHost}:${webserver_port}/api/file_cache?op=clear&sync=true"]
     stringCommand = command.collect{it.toString()}
     process = new ProcessBuilder(stringCommand as 
String[]).redirectErrorStream(true).start()
 
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 08ae0da44c0..4c4468dadda 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
@@ -28,8 +28,8 @@ final String HIT_RATIO_5M_METRIC_FALSE_MSG = 
HIT_RATIO_CHECK_FAILED_PREFIX + "hi
 
 // Constants for normal queue check
 final String NORMAL_QUEUE_CHECK_FAILED_PREFIX = "Normal queue check failed: "
-final String NORMAL_QUEUE_SIZE_VALIDATION_FAILED_MSG = 
NORMAL_QUEUE_CHECK_FAILED_PREFIX + "size validation failed (curr_size should be 
> 0 and < max_size)"
-final String NORMAL_QUEUE_ELEMENTS_VALIDATION_FAILED_MSG = 
NORMAL_QUEUE_CHECK_FAILED_PREFIX + "elements validation failed (curr_elements 
should be > 0 and < max_elements)"
+final String NORMAL_QUEUE_SIZE_VALIDATION_FAILED_MSG = 
NORMAL_QUEUE_CHECK_FAILED_PREFIX + "size validation failed (curr_size should be 
> 0 and <= total cache capacity)"
+final String NORMAL_QUEUE_ELEMENTS_VALIDATION_FAILED_MSG = 
NORMAL_QUEUE_CHECK_FAILED_PREFIX + "elements validation failed (curr_elements 
should be > 0)"
 
 // Constants for hit and read counts check
 final String HIT_AND_READ_COUNTS_CHECK_FAILED_PREFIX = "Hit and read counts 
check failed: "
@@ -130,12 +130,20 @@ suite("test_file_cache_statistics", 
"p0,external,nonConcurrent") {
     Double normalQueueMaxSize = cacheMetricSum('normal_queue_max_size')
     Double normalQueueCurrElements = 
cacheMetricSum('normal_queue_curr_elements')
     Double normalQueueMaxElements = cacheMetricSum('normal_queue_max_elements')
+    Double indexQueueMaxSize = cacheMetricSum('index_queue_max_size')
+    Double ttlQueueMaxSize = cacheMetricSum('ttl_queue_max_size')
+    Double disposableQueueMaxSize = cacheMetricSum('disposable_queue_max_size')
+    Double cacheCapacity = (normalQueueMaxSize == null || indexQueueMaxSize == 
null ||
+            ttlQueueMaxSize == null || disposableQueueMaxSize == null) ? null :
+            normalQueueMaxSize + indexQueueMaxSize + ttlQueueMaxSize + 
disposableQueueMaxSize
     logger.info("Normal queue metrics - curr_size: ${normalQueueCurrSize}, 
max_size: ${normalQueueMaxSize}, " +
             "curr_elements: ${normalQueueCurrElements}, max_elements: 
${normalQueueMaxElements}")
-    assertTrue(normalQueueCurrSize > 0.0 && normalQueueCurrSize < 
normalQueueMaxSize,
+    logger.info("Total file cache capacity: ${cacheCapacity}")
+    // A queue's max_size is a soft limit. A queue may borrow unused capacity 
from
+    // other queues, while the total cache capacity remains the hard upper 
bound.
+    assertTrue(normalQueueCurrSize > 0.0 && cacheCapacity != null && 
normalQueueCurrSize <= cacheCapacity,
             NORMAL_QUEUE_SIZE_VALIDATION_FAILED_MSG)
-    assertTrue(normalQueueCurrElements > 0.0 && normalQueueCurrElements < 
normalQueueMaxElements,
-            NORMAL_QUEUE_ELEMENTS_VALIDATION_FAILED_MSG)
+    assertTrue(normalQueueCurrElements > 0.0, 
NORMAL_QUEUE_ELEMENTS_VALIDATION_FAILED_MSG)
 
     Double initialHitCounts = cacheMetricSum('total_hit_counts')
     Double initialReadCounts = cacheMetricSum('total_read_counts')


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

Reply via email to