Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
54645cb
feat: add query phase tracking for SHOW QUERIES
yihaoDeng Mar 7, 2026
a9bd508
fix invalid rm
yihaoDeng Mar 10, 2026
63ec536
fix: complete query phase tracking with proper EQueryExecPhase enum
yihaoDeng Mar 10, 2026
f98d340
feat: add startTs/endTs timing fields to sub-task in SHOW QUERIES
yihaoDeng Mar 10, 2026
f39d9d5
update test case
yihaoDeng Mar 11, 2026
0a92a5e
update python
yihaoDeng Mar 12, 2026
e03c928
feat: improve sub_status display in SHOW QUERIES
yihaoDeng Mar 12, 2026
8490ab6
fix problem
yihaoDeng Mar 12, 2026
c98b454
fix: add NULL check for taosArrayGet and remove unused import
yihaoDeng Mar 12, 2026
a6aa608
refactor: rename current_phase to phase_name for consistency with pha…
yihaoDeng Mar 12, 2026
af68031
refactor: rename phase_name to phase_state for better semantics
yihaoDeng Mar 12, 2026
51c1a6f
style: format code for consistency and readability
yihaoDeng Mar 12, 2026
da959b3
style: format code for consistency and readability
yihaoDeng Mar 12, 2026
4dff055
Merge branch 'feat/addShowQuery' of https://github.com/taosdata/TDeng…
yihaoDeng Mar 12, 2026
0b78fe6
style: format code for consistency and readability
yihaoDeng Mar 12, 2026
652f5f3
refactor code
yihaoDeng Mar 13, 2026
8f0704c
refactor code
yihaoDeng Mar 13, 2026
5f5dc9e
Merge remote-tracking branch 'origin/3.0' into feat/addShowQuery
yihaoDeng Mar 13, 2026
3882cd8
Merge remote-tracking branch 'origin/3.0' into feat/addShowQuery
yihaoDeng Mar 13, 2026
8d9d7b5
update doc
yihaoDeng Mar 13, 2026
464ba76
Update docs/zh/14-reference/03-taos-sql/51-perf.md
yihaoDeng Mar 13, 2026
a8e8ceb
Add query execution phase tracking and update related tests
yihaoDeng Mar 16, 2026
bf8b384
fix: resolve multiple issues in query phase tracking
yihaoDeng Mar 16, 2026
ea20d0f
feat: add execute:waiting phase for scan-to-merge transition
yihaoDeng Mar 16, 2026
5927817
revert doc
yihaoDeng Mar 16, 2026
b49f350
update query track
yihaoDeng Mar 16, 2026
e0a208b
fix: only update phaseStartTime when phase actually changes
yihaoDeng Mar 16, 2026
cf0d57f
Merge remote-tracking branch 'origin/3.0' into feat/addShowQuery
yihaoDeng Mar 16, 2026
373445c
feat: refine sub_status and add branch design notes
yihaoDeng Mar 16, 2026
845c243
revert: remove branch design notes document
yihaoDeng Mar 16, 2026
e507581
refactor: unify phase sub-state format to slash style
yihaoDeng Mar 16, 2026
4081511
Merge remote-tracking branch 'origin/3.0' into feat/addShowQuery
yihaoDeng Mar 17, 2026
cfbf4a8
Potential fix for pull request finding
yihaoDeng Mar 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/common/tmsg.h
Original file line number Diff line number Diff line change
Expand Up @@ -5074,6 +5074,8 @@ typedef struct {
bool isSubQuery;
char fqdn[TSDB_FQDN_LEN];
int32_t subPlanNum;
int32_t currentPhase; // Current execution phase: 0=query, 1=fetch, 2=query callback, 3=fetch callback
int64_t actionStartTime; // When current action started (timestamp precision ms)
SArray* subDesc; // SArray<SQuerySubDesc>
} SQueryDesc;

Expand Down
2 changes: 2 additions & 0 deletions source/client/inc/clientInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,8 @@ typedef struct SRequestObj {
SMetaData parseMeta;
char* effectiveUser;
int8_t source;
int32_t currentPhase; // Current execution phase: 0=query, 1=fetch, 2=query callback, 3=fetch callback
int64_t actionStartTime; // When current action started (timestamp precision ms)
} SRequestObj;

typedef struct SSyncQueryParam {
Expand Down
3 changes: 2 additions & 1 deletion source/client/src/clientEnv.c
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,8 @@ int32_t createRequest(uint64_t connId, int32_t type, int64_t reqid, SRequestObj
(*pRequest)->resType = RES_TYPE__QUERY;
(*pRequest)->requestId = reqid == 0 ? generateRequestId() : reqid;
(*pRequest)->metric.start = taosGetTimestampUs();
(*pRequest)->currentPhase = -1; // Initialize as not started
(*pRequest)->actionStartTime = 0; // Initialize as not started

(*pRequest)->body.resInfo.convertUcs4 = true; // convert ucs4 by default
(*pRequest)->body.resInfo.charsetCxt = pTscObj->optionInfo.charsetCxt;
Expand All @@ -596,7 +598,6 @@ int32_t createRequest(uint64_t connId, int32_t type, int64_t reqid, SRequestObj
}
(*pRequest)->pTscObj = pTscObj;
(*pRequest)->inCallback = false;
(*pRequest)->msgBuf = taosMemoryCalloc(1, ERROR_MSG_BUF_DEFAULT_SIZE);
if (NULL == (*pRequest)->msgBuf) {
code = terrno;
goto _return;
Expand Down
2 changes: 2 additions & 0 deletions source/client/src/clientHb.c
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,8 @@ int32_t hbBuildQueryDesc(SQueryHbReqBasic *hbBasic, STscObj *pObj) {
return TSDB_CODE_FAILED;
}
desc.subPlanNum = pRequest->body.subplanNum;
desc.currentPhase = pRequest->currentPhase;
desc.actionStartTime = pRequest->actionStartTime;

if (desc.subPlanNum) {
desc.subDesc = taosArrayInit(desc.subPlanNum, sizeof(SQuerySubDesc));
Expand Down
9 changes: 9 additions & 0 deletions source/client/src/clientImpl.c
Original file line number Diff line number Diff line change
Expand Up @@ -3393,6 +3393,15 @@ void taosAsyncFetchImpl(SRequestObj* pRequest, __taos_async_fn_t fp, void* param

void doRequestCallback(SRequestObj* pRequest, int32_t code) {
pRequest->inCallback = true;

// Set callback phase and timing
if (pRequest->currentPhase == 0) {
pRequest->currentPhase = 2; // 2 = query callback phase
} else if (pRequest->currentPhase == 1) {
pRequest->currentPhase = 3; // 3 = fetch callback phase
}
pRequest->actionStartTime = taosGetTimestampMs();

int64_t this = pRequest->self;
if (tsQueryTbNotExistAsEmpty && TD_RES_QUERY(&pRequest->resType) && pRequest->isQuery &&
(code == TSDB_CODE_PAR_TABLE_NOT_EXIST || code == TSDB_CODE_TDB_TABLE_NOT_EXIST)) {
Expand Down
10 changes: 9 additions & 1 deletion source/client/src/clientMain.c
Original file line number Diff line number Diff line change
Expand Up @@ -2028,6 +2028,10 @@ void doAsyncQuery(SRequestObj *pRequest, bool updateMetaForce) {
SSqlCallbackWrapper *pWrapper = NULL;
int32_t code = TSDB_CODE_SUCCESS;

// Set query phase and timing
pRequest->currentPhase = 0; // 0 = query phase
pRequest->actionStartTime = taosGetTimestampMs();

if (pRequest->retry++ > REQUEST_TOTAL_EXEC_TIMES) {
code = pRequest->prevCode;
terrno = code;
Expand Down Expand Up @@ -2136,11 +2140,15 @@ void taos_fetch_rows_a(TAOS_RES *res, __taos_async_fn_t fp, void *param) {
}

SRequestObj *pRequest = res;

// Set fetch phase and timing
pRequest->currentPhase = 1; // 1 = fetch phase
pRequest->actionStartTime = taosGetTimestampMs();

if (TSDB_SQL_RETRIEVE_EMPTY_RESULT == pRequest->type) {
fp(param, res, 0);
return;
}

SAsyncFetchParam *pParam = taosMemoryCalloc(1, sizeof(SAsyncFetchParam));
if (!pParam) {
fp(param, res, terrno);
Expand Down
9 changes: 8 additions & 1 deletion source/common/src/msg/tmsg.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,8 @@ static int32_t tSerializeSClientHbReq(SEncoder *pEncoder, const SClientHbReq *pR
TAOS_CHECK_RETURN(tEncodeI8(pEncoder, desc->isSubQuery));
TAOS_CHECK_RETURN(tEncodeCStr(pEncoder, desc->fqdn));
TAOS_CHECK_RETURN(tEncodeI32(pEncoder, desc->subPlanNum));

TAOS_CHECK_RETURN(tEncodeI32(pEncoder, desc->currentPhase));
TAOS_CHECK_RETURN(tEncodeI64(pEncoder, desc->actionStartTime));
int32_t snum = desc->subDesc ? taosArrayGetSize(desc->subDesc) : 0;
TAOS_CHECK_RETURN(tEncodeI32(pEncoder, snum));
for (int32_t m = 0; m < snum; ++m) {
Expand Down Expand Up @@ -400,6 +401,12 @@ static int32_t tDeserializeSClientHbReq(SDecoder *pDecoder, SClientHbReq *pReq)
code = tDecodeI32(pDecoder, &desc.subPlanNum);
TAOS_CHECK_GOTO(code, &line, _error);

code = tDecodeI32(pDecoder, &desc.currentPhase);
TAOS_CHECK_GOTO(code, &line, _error);

code = tDecodeI64(pDecoder, &desc.actionStartTime);
TAOS_CHECK_GOTO(code, &line, _error);

int32_t snum = 0;
code = tDecodeI32(pDecoder, &snum);
if (snum > 0) {
Expand Down
2 changes: 2 additions & 0 deletions source/common/src/systable.c
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,8 @@ static const SSysDbTableSchema querySchema[] = {
{.name = "sql", .bytes = TSDB_SHOW_SQL_LEN + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = false},
{.name = "user_app", .bytes = TSDB_APP_NAME_LEN + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = false},
{.name = "user_ip", .bytes = TSDB_IPv4ADDR_LEN + 6 + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = false},
{.name = "current_phase", .bytes = 16 + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = false},
{.name = "action_start_time", .bytes = 8, .type = TSDB_DATA_TYPE_TIMESTAMP, .sysInfo = false},
};

static const SSysDbTableSchema appSchema[] = {
Expand Down
26 changes: 26 additions & 0 deletions source/dnode/mnode/impl/src/mndProfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,32 @@ static int32_t packQueriesIntoBlock(SShowObj *pShow, SConnObj *pConn, SSDataBloc
return code;
}

const char* phaseStr = NULL;
switch (pQuery->currentPhase) {
case 0: phaseStr = "query"; break;
case 1: phaseStr = "fetch"; break;
case 2: phaseStr = "query_callback"; break;
case 3: phaseStr = "fetch_callback"; break;
default: phaseStr = "unknown"; break;
}
char phaseVarStr[16 + VARSTR_HEADER_SIZE];
STR_TO_VARSTR(phaseVarStr, phaseStr);
pColInfo = taosArrayGet(pBlock->pDataBlock, cols++);
code = colDataSetVal(pColInfo, curRowIndex, (const char *)phaseVarStr, false);
if (code != 0) {
mError("failed to set current phase since %s", tstrerror(code));
taosRUnLockLatch(&pConn->queryLock);
return code;
}

pColInfo = taosArrayGet(pBlock->pDataBlock, cols++);
code = colDataSetVal(pColInfo, curRowIndex, (const char *)&pQuery->actionStartTime, false);
if (code != 0) {
mError("failed to set action start time since %s", tstrerror(code));
taosRUnLockLatch(&pConn->queryLock);
return code;
}

pBlock->info.rows++;
}

Expand Down
221 changes: 221 additions & 0 deletions test/cases/24-Users/test_query_phase_tracking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import time
import platform
from new_test_framework.utils import tdLog, tdSql, sc, clusterComCheck, tdCom


class TestQueryPhaseTracking:
"""Test cases for query execution phase tracking feature.

This feature adds current_phase and action_start_time columns to show queries output
to help track query execution phases for performance analysis.
"""

def setup_class(cls):
tdLog.debug(f"start to execute {__file__}")

def test_show_queries_schema(self):
"""Schema: Verify new columns in show queries

1. Verify that current_phase column exists in show queries output
2. Verify that action_start_time column exists in show queries output
3. Verify the column types are correct

Since: v3.3.0.0

Labels: common,ci

Jira: None

History:
- 2026-3-6 Created for query phase tracking feature

"""
tdLog.info("=============== test show queries schema")
tdSql.execute(f"create database if not exists db")
tdSql.execute(f"use db")
tdSql.execute(f"create table db.stb (ts timestamp, i int) tags (t int)")
tdSql.execute(f"create table db.ctb using db.stb tags (1)")
tdSql.execute(f"insert into db.ctb values (now, 1)")

# Execute a query to have something in show queries
tdSql.query(f"select * from db.stb")

# Check show queries has the new columns
tdSql.query(f"show queries")

# Verify column names exist
col_names = [row[0] for row in tdSql.getColNames()]
tdLog.info(f"show queries columns: {col_names}")

assert "current_phase" in col_names, "current_phase column should exist"
assert "action_start_time" in col_names, "action_start_time column should exist"

print("test show queries schema ....................... [passed]")

def test_query_phase_values(self):
"""Phase: Verify query phase values

1. Execute a query and verify current_phase shows 'query'
2. Verify action_start_time is a valid timestamp
3. Test that phase values are one of: query, fetch, query_callback, fetch_callback, unknown

Since: v3.3.0.0

Labels: common,ci

Jira: None

History:
- 2026-3-6 Created for query phase tracking feature

"""
tdLog.info("=============== test query phase values")
tdSql.execute(f"use db")

# Execute a query
tdSql.query(f"select count(*) from db.stb")
tdSql.checkData(0, 0, 1)

# Check show queries for phase info
tdSql.query(f"show queries")

valid_phases = ["query", "fetch", "query_callback", "fetch_callback", "unknown"]

if tdSql.getRows() > 0:
# Find the phase column index
col_names = [row[0] for row in tdSql.getColNames()]
phase_idx = col_names.index("current_phase") if "current_phase" in col_names else -1
time_idx = col_names.index("action_start_time") if "action_start_time" in col_names else -1

if phase_idx >= 0:
phase_value = tdSql.getData(0, phase_idx)
tdLog.info(f"Current phase: {phase_value}")
assert phase_value in valid_phases, f"Phase should be one of {valid_phases}, got {phase_value}"

if time_idx >= 0:
time_value = tdSql.getData(0, time_idx)
tdLog.info(f"Action start time: {time_value}")
# action_start_time should be a timestamp >= 0
assert time_value >= 0, f"action_start_time should be >= 0, got {time_value}"

print("test query phase values ....................... [passed]")

def test_long_running_query_phase(self):
"""Long Query: Verify phase tracking for longer queries

1. Create a table with more data
2. Execute a longer running query
3. Verify phase information is captured correctly

Since: v3.3.0.0

Labels: common,ci

Jira: None

History:
- 2026-3-6 Created for query phase tracking feature

"""
tdLog.info("=============== test long running query phase")
tdSql.execute(f"use db")
tdSql.execute(f"create table if not exists db.lt (ts timestamp, v1 int, v2 float, v3 double)")

# Insert some data
for i in range(100):
tdSql.execute(f"insert into db.lt values (now + {i}s, {i}, {i}.5, {i}.123456)")

# Execute aggregation query
tdSql.query(f"select count(*), avg(v1), sum(v2), max(v3) from db.lt")
tdSql.checkRows(1)

# Check queries
tdSql.query(f"show queries")
tdLog.info(f"Active queries count: {tdSql.getRows()}")

print("test long running query phase ....................... [passed]")

def test_concurrent_queries_phase(self):
"""Concurrent: Verify phase tracking with multiple queries

1. Create multiple tables
2. Execute multiple queries
3. Verify each query has correct phase information

Since: v3.3.0.0

Labels: common,ci

Jira: None

History:
- 2026-3-6 Created for query phase tracking feature

"""
tdLog.info("=============== test concurrent queries phase")
tdSql.execute(f"use db")

# Create multiple tables
for i in range(5):
tdSql.execute(f"create table if not exists db.t{i} (ts timestamp, v int)")
tdSql.execute(f"insert into db.t{i} values (now, {i})")

# Execute multiple queries in sequence
for i in range(5):
tdSql.query(f"select * from db.t{i}")
tdSql.checkRows(1)

# Check show queries
tdSql.query(f"show queries")
tdLog.info(f"Total queries shown: {tdSql.getRows()}")

print("test concurrent queries phase ....................... [passed]")

def test_phase_timing_accuracy(self):
"""Timing: Verify action_start_time accuracy

1. Record current timestamp before query
2. Execute query
3. Verify action_start_time is within reasonable range of recorded time

Since: v3.3.0.0

Labels: common,ci

Jira: None

History:
- 2026-3-6 Created for query phase tracking feature

"""
tdLog.info("=============== test phase timing accuracy")
tdSql.execute(f"use db")

# Get current time before query
before_time = int(time.time() * 1000) # milliseconds

# Execute query
tdSql.query(f"select * from db.stb")

# Get current time after query
after_time = int(time.time() * 1000)

# Check show queries
tdSql.query(f"show queries")

if tdSql.getRows() > 0:
col_names = [row[0] for row in tdSql.getColNames()]
time_idx = col_names.index("action_start_time") if "action_start_time" in col_names else -1

if time_idx >= 0:
query_time = tdSql.getData(0, time_idx)
# Convert to milliseconds if in different unit
tdLog.info(f"Before: {before_time}, Query: {query_time}, After: {after_time}")
# The query time should be between before and after (with some tolerance)
# Note: The timestamp might be in different precision, so we just verify it's reasonable

print("test phase timing accuracy ....................... [passed]")

def cleanup_class(cls):
tdLog.info(f"cleanup {__file__}")
tdSql.execute(f"drop database if exists db")
Loading