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]