This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new 88abe68a1c5 branch-4.1: [fix](regression) match audit internal query
error by injected tablet (#65234)
88abe68a1c5 is described below
commit 88abe68a1c5becac1102f683673b3271b7fa557e
Author: shuke <[email protected]>
AuthorDate: Mon Jul 13 14:02:29 2026 +0800
branch-4.1: [fix](regression) match audit internal query error by injected
tablet (#65234)
## Proposed changes
`test_audit_log_internal_query_failure` validates that a failed internal
stats
query is audited as `ERR`. The case currently finds the audit row by
requiring
`queried_tables_and_views` to contain the exact user table name.
That auxiliary field is brittle for the failed internal-query path. In
the
latest branch-4.1 failure, the injected tablet-scoped `IO_ERROR`
happened, but
the audit lookup stayed empty because of the exact
`queried_tables_and_views`
filter.
This PR keeps the product assertion but matches the audit row by the
evidence
that uniquely belongs to this run:
- `is_internal = 1`
- `state = 'ERR'`
- `error_message` contains `IO_ERROR`
- `error_message` contains the injected `tabletId`
- `time >= startTime`
It also logs the recent internal audit rows if the lookup still fails,
so the
next failure is easier to diagnose.
## Evidence
- Branch-4.1 TeamCity history: 20 failures / 612 occurrences for
`fault_injection_p0.test_audit_log_internal_query_failure.test_audit_log_internal_query_failure`.
- Latest checked failure: build 985138 / PR 65191, assertion:
`expected an audit_log entry with state=ERR for the failed gather
query`.
- Build 985138 reached the intended injected `IO_ERROR` during
`analyze table test_audit_log_internal_query_failure_t1 with sync`.
- Build 985138 used a branch-4.1 base containing #65132 before merging
PR 65191,
so this is not only the older audit-loader batch assembly race.
## Testing
- `git diff --check`
- `groovy -e 'new GroovyShell().parse(new File(args[0])); println "OK"'
regression-test/suites/fault_injection_p0/test_audit_log_internal_query_failure.groovy`
---
.../test_audit_log_internal_query_failure.groovy | 37 +++++++++++++++++-----
1 file changed, 29 insertions(+), 8 deletions(-)
diff --git
a/regression-test/suites/fault_injection_p0/test_audit_log_internal_query_failure.groovy
b/regression-test/suites/fault_injection_p0/test_audit_log_internal_query_failure.groovy
index 233187e582e..f6d44b19227 100644
---
a/regression-test/suites/fault_injection_p0/test_audit_log_internal_query_failure.groovy
+++
b/regression-test/suites/fault_injection_p0/test_audit_log_internal_query_failure.groovy
@@ -23,7 +23,14 @@ suite('test_audit_log_internal_query_failure',
'nonConcurrent') {
def tbl = 'test_audit_log_internal_query_failure_t1'
setGlobalVarTemporary([enable_audit_plugin: true], {
+ def sqlCacheOrigValue = null
try {
+ sqlCacheOrigValue = (sql "select @@enable_sql_cache")[0][0]
+ // The audit_log table is loaded asynchronously. Repeated polling
+ // can otherwise cache an early empty result before the target
audit
+ // batch becomes visible.
+ sql "set enable_sql_cache=false"
+
sql "drop table if exists ${tbl}"
sql """
create table ${tbl} (k int, v int)
@@ -57,38 +64,52 @@ suite('test_audit_log_internal_query_failure',
'nonConcurrent') {
GetDebugPoint().clearDebugPointsForAllBEs()
}
- def currentDb = (sql_return_maparray "select database() as
db")[0].db.toString()
- def fullTableName = "internal.${currentDb}.${tbl}"
-
// Force a flush so the failed internal query is queryable from
// __internal_schema.audit_log.
// The failed gather SQL reads from our user table and runs as an
// internal query; it must show up with state=ERR. Filter by start
// time to avoid matching stale entries from previous runs.
- // Use queried_tables_and_views instead of stmt because stmt can be
- // truncated by audit_plugin_max_sql_length when running in CI.
+ // Match the injected IO error instead of queried_tables_and_views:
+ // failed internal queries do not always populate that field before
+ // the error is audited, but the propagated error message contains
+ // the tablet-scoped debug point evidence.
def query = """select state, error_code, error_message
from __internal_schema.audit_log
where is_internal = 1
- and array_contains(queried_tables_and_views,
'${fullTableName}')
and state = 'ERR'
+ and error_message like '%IO_ERROR%'
+ and error_message like '%${tabletId}%'
and `time` >= '${startTime}'
order by `time` desc limit 1"""
+ def diagnosticQuery = """select `time`, state, error_code,
error_message, queried_tables_and_views
+ from __internal_schema.audit_log
+ where is_internal = 1
+ and `time` >= '${startTime}'
+ order by `time` desc limit 5"""
def res = []
- int retry = 60
+ int retry = 90
while (res.isEmpty() && retry-- > 0) {
sql "call flush_audit_log()"
sleep(2000)
res = sql_return_maparray "${query}"
}
+ if (res.isEmpty()) {
+ logger.info("recent internal audit rows after injected IO
error: ${sql_return_maparray(diagnosticQuery)}")
+ }
assertFalse(res.isEmpty(),
- "expected an audit_log entry with state=ERR for the failed
gather query")
+ "expected an audit_log entry with state=ERR for the
injected IO_ERROR")
assertEquals('ERR', res[0].state.toString())
assertNotEquals('0', res[0].error_code.toString())
assertNotNull(res[0].error_message)
assertTrue(!res[0].error_message.toString().isEmpty(),
"audit_log error_message should not be empty, got:
${res[0].error_message}")
} finally {
+ try {
+ if (sqlCacheOrigValue != null) {
+ sql "set enable_sql_cache=${sqlCacheOrigValue}"
+ }
+ } catch (Throwable ignored) {
+ }
try {
sql "drop table if exists ${tbl}"
} catch (Throwable ignored) {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]