From 49865ed294ec75a1d0cc1b5f10a958901de21b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chappyguoxy=E2=80=9D?= <“happy_guoxy@163.com”> Date: Mon, 11 Nov 2024 14:57:06 +0800 Subject: [PATCH 01/88] test:add stream test cases --- tests/system-test/8-stream/checkpoint_info.py | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 tests/system-test/8-stream/checkpoint_info.py diff --git a/tests/system-test/8-stream/checkpoint_info.py b/tests/system-test/8-stream/checkpoint_info.py new file mode 100644 index 0000000000..00e5d1f688 --- /dev/null +++ b/tests/system-test/8-stream/checkpoint_info.py @@ -0,0 +1,139 @@ +################################################################### +# Copyright (c) 2016 by TAOS Technologies, Inc. +# All rights reserved. +# +# This file is proprietary and confidential to TAOS Technologies. +# No part of this file may be reproduced, stored, transmitted, +# disclosed or used in any form or by any means other than as +# expressly provided by the written permission from Jianhui Tao +# +################################################################### + +# -*- coding: utf-8 -*- + + +from util.log import * +from util.cases import * +from util.sql import * +from util.common import * +from util.sqlset import * +from util.cluster import * +import threading +# should be used by -N option +class TDTestCase: + + #updatecfgDict = {'checkpointInterval': 60 ,} + def init(self, conn, logSql, replicaVar=1): + print("========init========") + + self.replicaVar = int(replicaVar) + tdLog.debug("start to execute %s" % __file__) + tdSql.init(conn.cursor(), logSql) + def find_checkpoint_info_file(self, dirpath, checkpointid, task_id): + for root, dirs, files in os.walk(dirpath): + if f'checkpoint{checkpointid}' in dirs: + info_path = os.path.join(root, f'checkpoint{checkpointid}', 'info') + if os.path.exists(info_path): + if task_id in info_path: + return info_path + else: + continue + else: + return None + def get_dnode_info(self): + ''' + get a dict from vnode to dnode + ''' + self.vnode_dict = {} + sql = 'select dnode_id, vgroup_id from information_schema.ins_vnodes' + result = tdSql.getResult(sql) + for (dnode,vnode) in result: + self.vnode_dict[vnode] = dnode + def print_time_info(self): + ''' + sometimes, we need to wait for a while to check the info (for example, the checkpoint info file won't be created immediately after the redistribute) + ''' + times= 0 + while(True): + if(self.check_info()): + tdLog.success(f'Time to finish is {times}') + return + else: + if times > 200: + tdLog.exit("time out") + times += 10 + time.sleep(10) + def check_info(self): + ''' + first, check if the vnode is restored + ''' + while(True): + if(self.check_vnodestate()): + break + sql = 'select task_id, node_id, checkpoint_id, checkpoint_ver from information_schema.ins_stream_tasks where `level` = "source" or `level` = "agg" and node_type == "vnode"' + for task_id, vnode, checkpoint_id, checkpoint_ver in tdSql.getResult(sql): + dirpath = f"{cluster.dnodes[self.vnode_dict[vnode]-1].dataDir}/vnode/vnode{vnode}/" + info_path = self.find_checkpoint_info_file(dirpath, checkpoint_id, task_id) + if info_path is None: + return False + with open(info_path, 'r') as f: + info_id, info_ver = f.read().split() + if int(info_id) != int(checkpoint_id) or int(info_ver) != int(checkpoint_ver): + return False + return True + + def restart_stream(self): + tdLog.debug("========restart stream========") + for i in range(5): + tdSql.execute("pause stream s1") + time.sleep(2) + tdSql.execute("resume stream s1") + def initstream(self): + tdLog.debug("========case1 start========") + os.system("nohup taosBenchmark -y -B 1 -t 4 -S 500 -n 1000 -v 3 > /dev/null 2>&1 &") + time.sleep(5) + tdSql.execute("create snode on dnode 1") + tdSql.execute("use test") + tdSql.execute("create stream if not exists s1 trigger at_once ignore expired 0 ignore update 0 fill_history 1 into st1 as select _wstart,sum(voltage),groupid from meters partition by groupid interval(1s)") + tdLog.debug("========create stream using snode and insert data ok========") + self.get_dnode_info() + def redistribute_vnode(self): + tdLog.debug("========redistribute vnode========") + tdSql.redistribute_db_all_vgroups() + self.get_dnode_info() + def replicate_db(self): + tdLog.debug("========replicate db========") + while True: + res = tdSql.getResult("SHOW TRANSACTIONS") + if res == []: + tdLog.debug("========== no transaction, begin to replicate db =========") + tdSql.execute("alter database test replica 3") + return + else: + time.sleep(5) + continue + def check_vnodestate(self): + sql = 'select distinct restored from information_schema.ins_vnodes' + if tdSql.getResult(sql) != [(True,)]: + tdLog.debug(f"vnode not restored, wait 5s") + time.sleep(5) + return False + else: + return True + def run(self): + print("========run========") + self.initstream() + self.restart_stream() + time.sleep(60) + self.print_time_info() + self.redistribute_vnode() + self.restart_stream() + time.sleep(60) + self.print_time_info() + + def stop(self): + print("========stop========") + tdSql.close() + tdLog.success(f"{__file__} successfully executed") +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) \ No newline at end of file From 7eff826097036ab41a44d9faca8bb1a7e4f8e37a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chappyguoxy=E2=80=9D?= <“happy_guoxy@163.com”> Date: Mon, 11 Nov 2024 14:57:19 +0800 Subject: [PATCH 02/88] test:add stream test cases --- .../system-test/8-stream/checkpoint_info2.py | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 tests/system-test/8-stream/checkpoint_info2.py diff --git a/tests/system-test/8-stream/checkpoint_info2.py b/tests/system-test/8-stream/checkpoint_info2.py new file mode 100644 index 0000000000..7f2e8b0672 --- /dev/null +++ b/tests/system-test/8-stream/checkpoint_info2.py @@ -0,0 +1,134 @@ +################################################################### +# Copyright (c) 2016 by TAOS Technologies, Inc. +# All rights reserved. +# +# This file is proprietary and confidential to TAOS Technologies. +# No part of this file may be reproduced, stored, transmitted, +# disclosed or used in any form or by any means other than as +# expressly provided by the written permission from Jianhui Tao +# +################################################################### + +# -*- coding: utf-8 -*- + + +from util.log import * +from util.cases import * +from util.sql import * +from util.common import * +from util.sqlset import * +from util.cluster import * + +# should be used by -N option +class TDTestCase: + updatecfgDict = {'checkpointInterval': 60 , + } + def init(self, conn, logSql, replicaVar=1): + self.replicaVar = int(replicaVar) + tdLog.debug("start to execute %s" % __file__) + tdSql.init(conn.cursor(), True) + def find_checkpoint_info_file(self, dirpath, checkpointid, task_id): + for root, dirs, files in os.walk(dirpath): + if f'checkpoint{checkpointid}' in dirs: + info_path = os.path.join(root, f'checkpoint{checkpointid}', 'info') + if os.path.exists(info_path): + if task_id in info_path: + return info_path + else: + continue + else: + return None + def get_dnode_info(self): + ''' + get a dict from vnode to dnode + ''' + self.vnode_dict = {} + sql = 'select dnode_id, vgroup_id from information_schema.ins_vnodes' + result = tdSql.getResult(sql) + for (dnode,vnode) in result: + self.vnode_dict[vnode] = dnode + def print_time_info(self): + ''' + sometimes, we need to wait for a while to check the info (for example, the checkpoint info file won't be created immediately after the redistribute) + ''' + times= 0 + while(True): + if(self.check_info()): + tdLog.success(f'Time to finish is {times}') + return + else: + if times > 400: + tdLog.exit("time out") + times += 10 + time.sleep(10) + def check_info(self): + ''' + first, check if the vnode is restored + ''' + while(True): + if(self.check_vnodestate()): + break + sql = 'select task_id, node_id, checkpoint_id, checkpoint_ver from information_schema.ins_stream_tasks where `level` = "source" or `level` = "agg" and node_type == "vnode"' + for task_id, vnode, checkpoint_id, checkpoint_ver in tdSql.getResult(sql): + dirpath = f"{cluster.dnodes[self.vnode_dict[vnode]-1].dataDir}/vnode/vnode{vnode}/" + info_path = self.find_checkpoint_info_file(dirpath, checkpoint_id, task_id) + if info_path is None: + return False + with open(info_path, 'r') as f: + info_id, info_ver = f.read().split() + if int(info_id) != int(checkpoint_id) or int(info_ver) != int(checkpoint_ver): + return False + return True + + def restart_stream(self): + tdLog.debug("========restart stream========") + for i in range(5): + tdSql.execute("pause stream s1") + time.sleep(2) + tdSql.execute("resume stream s1") + def initstream(self): + tdLog.debug("========case1 start========") + os.system("nohup taosBenchmark -y -B 1 -t 4 -S 500 -n 1000 -v 3 > /dev/null 2>&1 &") + time.sleep(5) + tdSql.execute("create snode on dnode 1") + tdSql.execute("use test") + tdSql.execute("create stream if not exists s1 trigger at_once ignore expired 0 ignore update 0 fill_history 1 into st1 as select _wstart,sum(voltage),groupid from meters partition by groupid interval(1s)") + tdLog.debug("========create stream using snode and insert data ok========") + self.get_dnode_info() + def redistribute_vnode(self): + tdLog.debug("========redistribute vnode========") + tdSql.redistribute_db_all_vgroups() + self.get_dnode_info() + def replicate_db(self): + tdLog.debug("========replicate db========") + while True: + res = tdSql.getResult("SHOW TRANSACTIONS") + if res == []: + tdLog.debug("========== no transaction, begin to replicate db =========") + tdSql.execute("alter database test replica 3") + return + else: + time.sleep(5) + continue + def check_vnodestate(self): + sql = 'select distinct restored from information_schema.ins_vnodes' + if tdSql.getResult(sql) != [(True,)]: + tdLog.debug(f"vnode not restored, wait 5s") + time.sleep(5) + return False + else: + return True + def run(self): + self.initstream() + self.replicate_db() + self.print_time_info() + self.restart_stream() + time.sleep(60) + self.print_time_info() + + def stop(self): + tdSql.close() + tdLog.success(f"{__file__} successfully executed") + +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) \ No newline at end of file From 26e9ed055f39758490b3a8c2ce82d974bccb7f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chappyguoxy=E2=80=9D?= <“happy_guoxy@163.com”> Date: Mon, 11 Nov 2024 14:58:17 +0800 Subject: [PATCH 03/88] test:add insert test cases --- .../system-test/1-insert/test_multi_insert.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/system-test/1-insert/test_multi_insert.py diff --git a/tests/system-test/1-insert/test_multi_insert.py b/tests/system-test/1-insert/test_multi_insert.py new file mode 100644 index 0000000000..d1b6d28ffd --- /dev/null +++ b/tests/system-test/1-insert/test_multi_insert.py @@ -0,0 +1,32 @@ +from util.sql import * +from util.common import * +import taos +taos.taos_connect +class TDTestCase: + def init(self, conn, logSql, replicaVar = 1): + self.replicaVar = int(replicaVar) + tdLog.debug(f"start to excute {__file__}") + self.conn = conn + tdSql.init(conn.cursor(), logSql) + def initdb(self): + tdSql.execute("drop database if exists d0") + tdSql.execute("create database d0") + tdSql.execute("use d0") + tdSql.execute("create stable stb0 (ts timestamp, w_ts timestamp, opc nchar(100), quality int) tags(t0 int)") + tdSql.execute("create table t0 using stb0 tags(1)") + tdSql.execute("create table t1 using stb0 tags(2)") + def multi_insert(self): + for i in range(5): + tdSql.execute(f"insert into t1 values(1721265436000, now() + {i + 1}s, '0', 12) t1(opc, quality, ts) values ('opc2', 192, now()+ {i + 2}s) t1(ts, opc, quality) values(now() + {i + 3}s, 'opc4', 10) t1 values(1721265436000, now() + {i + 4}s, '1', 191) t1(opc, quality, ts) values('opc5', 192, now() + {i + 5}s) t1 values(now(), now() + {i + 6}s, '2', 192)") + tdSql.execute("insert into t0 values(1721265436000,now(),'0',192) t0(quality,w_ts,ts) values(192,now(),1721265326000) t0(quality,w_t\ +s,ts) values(190,now()+1s,1721265326000) t0 values(1721265436000,now()+2s,'1',191) t0(quality,w_ts,ts) values(192,now()+3s,\ +1721265326002) t0(ts,w_ts,opc,quality) values(1721265436003,now()+4s,'3',193) t0 values(now(), now() + 4s , '2', 192)") + def run(self): + self.initdb() + self.multi_insert() + + def stop(self): + tdSql.close() + tdLog.success(f"{__file__} successfully executed") +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) \ No newline at end of file From e2f43410dff3b4284a368dfcf19dce7e67c22dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chappyguoxy=E2=80=9D?= <“happy_guoxy@163.com”> Date: Mon, 11 Nov 2024 14:59:01 +0800 Subject: [PATCH 04/88] test:add stddev test cases --- tests/system-test/2-query/stddev_test.py | 54 ++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 tests/system-test/2-query/stddev_test.py diff --git a/tests/system-test/2-query/stddev_test.py b/tests/system-test/2-query/stddev_test.py new file mode 100644 index 0000000000..c0cb51fe57 --- /dev/null +++ b/tests/system-test/2-query/stddev_test.py @@ -0,0 +1,54 @@ +import numpy as np +from util.log import * +from util.cases import * +from util.sql import * +from util.common import * +from util.sqlset import * + +''' +Test case for TS-5150 +''' +class TDTestCase: + def init(self, conn, logSql, replicaVar=1): + self.replicaVar = int(replicaVar) + tdLog.debug("start to execute %s" % __file__) + tdSql.init(conn.cursor()) + self.ts = 1537146000000 + def initdabase(self): + tdSql.execute('create database if not exists db_test vgroups 2 buffer 10') + tdSql.execute('use db_test') + tdSql.execute('create stable stb(ts timestamp, delay int) tags(groupid int)') + tdSql.execute('create table t1 using stb tags(1)') + tdSql.execute('create table t2 using stb tags(2)') + tdSql.execute('create table t3 using stb tags(3)') + tdSql.execute('create table t4 using stb tags(4)') + tdSql.execute('create table t5 using stb tags(5)') + tdSql.execute('create table t6 using stb tags(6)') + def insert_data(self): + for i in range(5000): + tdSql.execute(f"insert into t1 values({self.ts + i * 1000}, {i%5})") + tdSql.execute(f"insert into t2 values({self.ts + i * 1000}, {i%5})") + tdSql.execute(f"insert into t3 values({self.ts + i * 1000}, {i%5})") + + def verify_stddev(self): + for i in range(20): + tdSql.query(f'SELECT MAX(CASE WHEN delay != 0 THEN delay ELSE NULL END) AS maxDelay,\ + MIN(CASE WHEN delay != 0 THEN delay ELSE NULL END) AS minDelay,\ + AVG(CASE WHEN delay != 0 THEN delay ELSE NULL END) AS avgDelay,\ + STDDEV(CASE WHEN delay != 0 THEN delay ELSE NULL END) AS jitter,\ + COUNT(CASE WHEN delay = 0 THEN 1 ELSE NULL END) AS timeoutCount,\ + COUNT(*) AS totalCount from stb where ts between {1537146000000 + i * 1000} and {1537146000000 + (i+10) * 1000}') + res = tdSql.queryResult[0][3] + assert res > 0.8 + def run(self): + self.initdabase() + self.insert_data() + self.verify_stddev() + def stop(self): + tdSql.close() + tdLog.success(f"{__file__} successfully executed") + + +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) + From b38411a0767c9ed7095dbeb27d81aa8f975763a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chappyguoxy=E2=80=9D?= <“happy_guoxy@163.com”> Date: Mon, 11 Nov 2024 14:59:36 +0800 Subject: [PATCH 05/88] test:add stream test cases --- tests/pytest/util/sql.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/pytest/util/sql.py b/tests/pytest/util/sql.py index 1d3333264a..46b7e1f795 100644 --- a/tests/pytest/util/sql.py +++ b/tests/pytest/util/sql.py @@ -843,9 +843,10 @@ class TDSql: tdSql.query("select * from information_schema.ins_vnodes") #result: dnode_id|vgroup_id|db_name|status|role_time|start_time|restored| + results = list(tdSql.queryResult) for vnode_group_id in db_vgroups_list: - print(tdSql.queryResult) - for result in tdSql.queryResult: + for result in results: + print(f'result[2] is {result[2]}, db_name is {db_name}, result[1] is {result[1]}, vnode_group_id is {vnode_group_id}') if result[2] == db_name and result[1] == vnode_group_id: tdLog.debug(f"dbname: {db_name}, vgroup :{vnode_group_id}, dnode is {result[0]}") print(useful_trans_dnodes_list) From 2dbd0bacb506d0cc166b9813247d6f02a49915f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chappyguoxy=E2=80=9D?= <“happy_guoxy@163.com”> Date: Mon, 11 Nov 2024 15:00:10 +0800 Subject: [PATCH 06/88] test:add stream and query test cases --- tests/parallel_test/cases.task | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/parallel_test/cases.task b/tests/parallel_test/cases.task index 151358aec3..2b45142156 100644 --- a/tests/parallel_test/cases.task +++ b/tests/parallel_test/cases.task @@ -230,6 +230,14 @@ ,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/agg_group_NotReturnValue.py -Q 4 ,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/td-32548.py +,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/stddev_test.py +,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/stddev_test.py -Q 2 +,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/stddev_test.py -Q 3 +,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/stddev_test.py -Q 4 +,,y,system-test,./pytest.sh python3 ./test.py -f 8-stream/checkpoint_info.py -N 4 +,,y,system-test,./pytest.sh python3 ./test.py -f 8-stream/checkpoint_info2.py -N 4 +,,y,system-test,./pytest.sh python3 ./test.py -f 1-insert/test_multi_insert.py + ,,y,system-test,./pytest.sh python3 ./test.py -f 3-enterprise/restore/restoreDnode.py -N 5 -M 3 -i False ,,y,system-test,./pytest.sh python3 ./test.py -f 3-enterprise/restore/restoreVnode.py -N 5 -M 3 -i False ,,y,system-test,./pytest.sh python3 ./test.py -f 3-enterprise/restore/restoreMnode.py -N 5 -M 3 -i False From 6c03a8c1ca4a951e3402d9937881c9395ef6c168 Mon Sep 17 00:00:00 2001 From: yihaoDeng Date: Mon, 11 Nov 2024 15:06:17 +0800 Subject: [PATCH 07/88] fix double free --- source/dnode/mgmt/mgmt_snode/src/smWorker.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/source/dnode/mgmt/mgmt_snode/src/smWorker.c b/source/dnode/mgmt/mgmt_snode/src/smWorker.c index 8c33c5bb4b..1e882fc656 100644 --- a/source/dnode/mgmt/mgmt_snode/src/smWorker.c +++ b/source/dnode/mgmt/mgmt_snode/src/smWorker.c @@ -36,14 +36,15 @@ static void smProcessWriteQueue(SQueueInfo *pInfo, STaosQall *qall, int32_t numO dTrace("msg:%p, get from snode-write queue", pMsg); int32_t code = sndProcessWriteMsg(pMgmt->pSnode, pMsg, NULL); - if (code < 0) { - dGError("snd, msg:%p failed to process write since %s", pMsg, tstrerror(code)); - if (pMsg->info.handle != NULL) { - tmsgSendRsp(pMsg); - } - } else { - smSendRsp(pMsg, 0); - } + // if (code < 0) { + // dGError("snd, msg:%p failed to process write since %s", pMsg, tstrerror(code)); + // if (pMsg->info.handle != NULL) { + // tmsgSendRsp(pMsg); + // } + // } else { + // smSendRsp(pMsg, 0); + // } + smSendRsp(pMsg, code); dTrace("msg:%p, is freed", pMsg); rpcFreeCont(pMsg->pCont); From 3c34ce6e96575fd28b650842e6563b2f3f989236 Mon Sep 17 00:00:00 2001 From: dmchen Date: Tue, 12 Nov 2024 14:08:23 +0800 Subject: [PATCH 08/88] fix/TD-32863-add-lock-for-allocate-primary --- source/dnode/mgmt/mgmt_vnode/inc/vmInt.h | 1 + source/dnode/mgmt/mgmt_vnode/src/vmInt.c | 29 ++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/source/dnode/mgmt/mgmt_vnode/inc/vmInt.h b/source/dnode/mgmt/mgmt_vnode/inc/vmInt.h index 989adf84ac..f289035689 100644 --- a/source/dnode/mgmt/mgmt_vnode/inc/vmInt.h +++ b/source/dnode/mgmt/mgmt_vnode/inc/vmInt.h @@ -38,6 +38,7 @@ typedef struct SVnodeMgmt { SHashObj *hash; SHashObj *closedHash; TdThreadRwlock lock; + TdThreadMutex mutex; SVnodesStat state; STfs *pTfs; TdThread thread; diff --git a/source/dnode/mgmt/mgmt_vnode/src/vmInt.c b/source/dnode/mgmt/mgmt_vnode/src/vmInt.c index 682c179270..9440920866 100644 --- a/source/dnode/mgmt/mgmt_vnode/src/vmInt.c +++ b/source/dnode/mgmt/mgmt_vnode/src/vmInt.c @@ -66,6 +66,12 @@ int32_t vmAllocPrimaryDisk(SVnodeMgmt *pMgmt, int32_t vgId) { if (code != 0) { return code; } + + code = taosThreadMutexLock(&pMgmt->mutex); + if (code != 0) { + goto _OVER; + } + for (int32_t v = 0; v < numOfVnodes; v++) { SVnodeObj *pVnode = ppVnodes[v]; disks[pVnode->diskPrimary] += 1; @@ -81,6 +87,13 @@ int32_t vmAllocPrimaryDisk(SVnodeMgmt *pMgmt, int32_t vgId) { } } + code = taosThreadMutexUnlock(&pMgmt->mutex); + if (code != 0) { + goto _OVER; + } + +_OVER: + for (int32_t i = 0; i < numOfVnodes; ++i) { if (ppVnodes == NULL || ppVnodes[i] == NULL) continue; vmReleaseVnode(pMgmt, ppVnodes[i]); @@ -89,8 +102,13 @@ int32_t vmAllocPrimaryDisk(SVnodeMgmt *pMgmt, int32_t vgId) { taosMemoryFree(ppVnodes); } - dInfo("vgId:%d, alloc disk:%d of level 0. ndisk:%d, vnodes: %d", vgId, diskId, ndisk, numOfVnodes); - return diskId; + if (code != 0) { + dError("vgId:%d, failed to alloc disk since %s", vgId, tstrerror(code)); + return code; + } else { + dInfo("vgId:%d, alloc disk:%d of level 0. ndisk:%d, vnodes: %d", vgId, diskId, ndisk, numOfVnodes); + return diskId; + } } SVnodeObj *vmAcquireVnodeImpl(SVnodeMgmt *pMgmt, int32_t vgId, bool strict) { @@ -622,6 +640,7 @@ static void vmCleanup(SVnodeMgmt *pMgmt) { vmStopWorker(pMgmt); vnodeCleanup(); (void)taosThreadRwlockDestroy(&pMgmt->lock); + (void)taosThreadMutexDestroy(&pMgmt->mutex); (void)taosThreadMutexDestroy(&pMgmt->fileLock); taosMemoryFree(pMgmt); } @@ -714,6 +733,12 @@ static int32_t vmInit(SMgmtInputOpt *pInput, SMgmtOutputOpt *pOutput) { goto _OVER; } + code = taosThreadMutexInit(&pMgmt->mutex, NULL); + if (code != 0) { + code = TAOS_SYSTEM_ERROR(errno); + goto _OVER; + } + code = taosThreadMutexInit(&pMgmt->fileLock, NULL); if (code != 0) { code = TAOS_SYSTEM_ERROR(errno); From b141e0e2ba20791c9e14c30db83559d954013081 Mon Sep 17 00:00:00 2001 From: dmchen Date: Wed, 13 Nov 2024 12:37:09 +0800 Subject: [PATCH 09/88] fix/TD-32863-add-creating-hash --- source/dnode/mgmt/mgmt_vnode/inc/vmInt.h | 3 + source/dnode/mgmt/mgmt_vnode/src/vmFile.c | 48 ++++++++++++++ source/dnode/mgmt/mgmt_vnode/src/vmHandle.c | 2 + source/dnode/mgmt/mgmt_vnode/src/vmInt.c | 70 +++++++++++++++++++-- 4 files changed, 118 insertions(+), 5 deletions(-) diff --git a/source/dnode/mgmt/mgmt_vnode/inc/vmInt.h b/source/dnode/mgmt/mgmt_vnode/inc/vmInt.h index f289035689..7842077d88 100644 --- a/source/dnode/mgmt/mgmt_vnode/inc/vmInt.h +++ b/source/dnode/mgmt/mgmt_vnode/inc/vmInt.h @@ -37,6 +37,7 @@ typedef struct SVnodeMgmt { SSingleWorker mgmtMultiWorker; SHashObj *hash; SHashObj *closedHash; + SHashObj *creatingHash; TdThreadRwlock lock; TdThreadMutex mutex; SVnodesStat state; @@ -97,6 +98,7 @@ SVnodeObj *vmAcquireVnodeImpl(SVnodeMgmt *pMgmt, int32_t vgId, bool strict); void vmReleaseVnode(SVnodeMgmt *pMgmt, SVnodeObj *pVnode); int32_t vmOpenVnode(SVnodeMgmt *pMgmt, SWrapperCfg *pCfg, SVnode *pImpl); void vmCloseVnode(SVnodeMgmt *pMgmt, SVnodeObj *pVnode, bool commitAndRemoveWal, bool keepClosed); +void vmRemoveFromCreatingHash(SVnodeMgmt *pMgmt, int32_t vgId); // vmHandle.c SArray *vmGetMsgHandles(); @@ -114,6 +116,7 @@ int32_t vmGetVnodeListFromFile(SVnodeMgmt *pMgmt, SWrapperCfg **ppCfgs, int32_t int32_t vmWriteVnodeListToFile(SVnodeMgmt *pMgmt); int32_t vmGetVnodeListFromHash(SVnodeMgmt *pMgmt, int32_t *numOfVnodes, SVnodeObj ***ppVnodes); int32_t vmGetAllVnodeListFromHash(SVnodeMgmt *pMgmt, int32_t *numOfVnodes, SVnodeObj ***ppVnodes); +int32_t vmGetAllVnodeListFromHashWithCreating(SVnodeMgmt *pMgmt, int32_t *numOfVnodes, SVnodeObj ***ppVnodes); // vmWorker.c int32_t vmStartWorker(SVnodeMgmt *pMgmt); diff --git a/source/dnode/mgmt/mgmt_vnode/src/vmFile.c b/source/dnode/mgmt/mgmt_vnode/src/vmFile.c index 7566b69c02..b4453ad6fc 100644 --- a/source/dnode/mgmt/mgmt_vnode/src/vmFile.c +++ b/source/dnode/mgmt/mgmt_vnode/src/vmFile.c @@ -67,6 +67,54 @@ int32_t vmGetAllVnodeListFromHash(SVnodeMgmt *pMgmt, int32_t *numOfVnodes, SVnod return 0; } +int32_t vmGetAllVnodeListFromHashWithCreating(SVnodeMgmt *pMgmt, int32_t *numOfVnodes, SVnodeObj ***ppVnodes) { + (void)taosThreadRwlockRdlock(&pMgmt->lock); + + int32_t num = 0; + int32_t size = taosHashGetSize(pMgmt->hash); + int32_t creatingSize = taosHashGetSize(pMgmt->creatingHash); + size += creatingSize; + SVnodeObj **pVnodes = taosMemoryCalloc(size, sizeof(SVnodeObj *)); + if (pVnodes == NULL) { + (void)taosThreadRwlockUnlock(&pMgmt->lock); + return terrno; + } + + void *pIter = taosHashIterate(pMgmt->hash, NULL); + while (pIter) { + SVnodeObj **ppVnode = pIter; + SVnodeObj *pVnode = *ppVnode; + if (pVnode && num < size) { + int32_t refCount = atomic_add_fetch_32(&pVnode->refCount, 1); + dTrace("vgId:%d,acquire vnode, vnode:%p, ref:%d", pVnode->vgId, pVnode, refCount); + pVnodes[num++] = (*ppVnode); + pIter = taosHashIterate(pMgmt->hash, pIter); + } else { + taosHashCancelIterate(pMgmt->hash, pIter); + } + } + + pIter = taosHashIterate(pMgmt->creatingHash, NULL); + while (pIter) { + SVnodeObj **ppVnode = pIter; + SVnodeObj *pVnode = *ppVnode; + if (pVnode && num < size) { + int32_t refCount = atomic_add_fetch_32(&pVnode->refCount, 1); + dTrace("vgId:%d, acquire vnode, vnode:%p, ref:%d", pVnode->vgId, pVnode, refCount); + pVnodes[num++] = (*ppVnode); + pIter = taosHashIterate(pMgmt->creatingHash, pIter); + } else { + taosHashCancelIterate(pMgmt->creatingHash, pIter); + } + } + (void)taosThreadRwlockUnlock(&pMgmt->lock); + + *numOfVnodes = num; + *ppVnodes = pVnodes; + + return 0; +} + int32_t vmGetVnodeListFromHash(SVnodeMgmt *pMgmt, int32_t *numOfVnodes, SVnodeObj ***ppVnodes) { (void)taosThreadRwlockRdlock(&pMgmt->lock); diff --git a/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c b/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c index 006f44b349..c9512d06d4 100644 --- a/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c +++ b/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c @@ -421,6 +421,8 @@ int32_t vmProcessCreateVnodeReq(SVnodeMgmt *pMgmt, SRpcMsg *pMsg) { goto _OVER; } + vmRemoveFromCreatingHash(pMgmt, req.vgId); + _OVER: if (code != 0) { int32_t r = 0; diff --git a/source/dnode/mgmt/mgmt_vnode/src/vmInt.c b/source/dnode/mgmt/mgmt_vnode/src/vmInt.c index 9440920866..26ac901f58 100644 --- a/source/dnode/mgmt/mgmt_vnode/src/vmInt.c +++ b/source/dnode/mgmt/mgmt_vnode/src/vmInt.c @@ -62,7 +62,7 @@ int32_t vmAllocPrimaryDisk(SVnodeMgmt *pMgmt, int32_t vgId) { int32_t numOfVnodes = 0; SVnodeObj **ppVnodes = NULL; - code = vmGetVnodeListFromHash(pMgmt, &numOfVnodes, &ppVnodes); + code = vmGetAllVnodeListFromHashWithCreating(pMgmt, &numOfVnodes, &ppVnodes); if (code != 0) { return code; } @@ -92,6 +92,25 @@ int32_t vmAllocPrimaryDisk(SVnodeMgmt *pMgmt, int32_t vgId) { goto _OVER; } + (void)taosThreadRwlockWrlock(&pMgmt->lock); + + SVnodeObj *pCreatingVnode = taosMemoryCalloc(1, sizeof(SVnodeObj)); + if (pCreatingVnode == NULL) { + (void)taosThreadRwlockUnlock(&pMgmt->lock); + dError("failed to alloc vnode since %s", terrstr()); + goto _OVER; + } + (void)memset(pCreatingVnode, 0, sizeof(SVnodeObj)); + + pCreatingVnode->vgId = vgId; + pCreatingVnode->diskPrimary = diskId; + + int32_t r = taosHashPut(pMgmt->creatingHash, &vgId, sizeof(int32_t), &pCreatingVnode, sizeof(SVnodeObj *)); + if (r != 0) { + dError("vgId:%d, failed to put vnode to creatingHash", vgId); + } + (void)taosThreadRwlockUnlock(&pMgmt->lock); + _OVER: for (int32_t i = 0; i < numOfVnodes; ++i) { @@ -234,12 +253,12 @@ void vmCloseVnode(SVnodeMgmt *pMgmt, SVnodeObj *pVnode, bool commitAndRemoveWal, } if (keepClosed) { SVnodeObj *pClosedVnode = taosMemoryCalloc(1, sizeof(SVnodeObj)); - (void)memset(pClosedVnode, 0, sizeof(SVnodeObj)); - if (pVnode == NULL) { - dError("vgId:%d, failed to alloc vnode since %s", pVnode->vgId, terrstr()); + if (pClosedVnode == NULL) { + dError("failed to alloc vnode since %s", terrstr()); (void)taosThreadRwlockUnlock(&pMgmt->lock); return; } + (void)memset(pClosedVnode, 0, sizeof(SVnodeObj)); pClosedVnode->vgId = pVnode->vgId; pClosedVnode->dropped = pVnode->dropped; @@ -445,11 +464,18 @@ static int32_t vmOpenVnodes(SVnodeMgmt *pMgmt) { pMgmt->closedHash = taosHashInit(TSDB_MIN_VNODES, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), true, HASH_ENTRY_LOCK); - if (pMgmt->hash == NULL) { + if (pMgmt->closedHash == NULL) { dError("failed to init vnode closed hash since %s", terrstr()); return TSDB_CODE_OUT_OF_MEMORY; } + pMgmt->creatingHash = + taosHashInit(TSDB_MIN_VNODES, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), true, HASH_ENTRY_LOCK); + if (pMgmt->creatingHash == NULL) { + dError("failed to init vnode creatingHash hash since %s", terrstr()); + return TSDB_CODE_OUT_OF_MEMORY; + } + SWrapperCfg *pCfgs = NULL; int32_t numOfVnodes = 0; if (vmGetVnodeListFromFile(pMgmt, &pCfgs, &numOfVnodes) != 0) { @@ -527,6 +553,28 @@ static int32_t vmOpenVnodes(SVnodeMgmt *pMgmt) { return 0; } +void vmRemoveFromCreatingHash(SVnodeMgmt *pMgmt, int32_t vgId) { + (void)taosThreadRwlockWrlock(&pMgmt->lock); + SVnodeObj *pOld = NULL; + int32_t r = taosHashGetDup(pMgmt->closedHash, &vgId, sizeof(int32_t), (void *)&pOld); + if (r != 0) { + dError("vgId:%d, failed to get vnode from closedHash", vgId); + } + if (pOld) { + vmFreeVnodeObj(&pOld); + } + r = taosHashRemove(pMgmt->creatingHash, &vgId, sizeof(int32_t)); + if (r != 0) { + dError("vgId:%d, failed to remove vnode from hash", vgId); + } + (void)taosThreadRwlockUnlock(&pMgmt->lock); + +_OVER: + if (r != 0) { + dError("vgId:%d, failed to remove vnode from creatingHash since %s", vgId, tstrerror(r)); + } +} + static void *vmCloseVnodeInThread(void *param) { SVnodeThread *pThread = param; SVnodeMgmt *pMgmt = pThread->pMgmt; @@ -632,6 +680,18 @@ static void vmCloseVnodes(SVnodeMgmt *pMgmt) { pMgmt->closedHash = NULL; } + pIter = taosHashIterate(pMgmt->creatingHash, NULL); + while (pIter) { + SVnodeObj **ppVnode = pIter; + vmFreeVnodeObj(ppVnode); + pIter = taosHashIterate(pMgmt->creatingHash, pIter); + } + + if (pMgmt->creatingHash != NULL) { + taosHashCleanup(pMgmt->creatingHash); + pMgmt->creatingHash = NULL; + } + dInfo("total vnodes:%d are all closed", numOfVnodes); } From 156421f8a03aa36d35ca98c6b9ca1d5a767851f1 Mon Sep 17 00:00:00 2001 From: dmchen Date: Wed, 13 Nov 2024 13:53:55 +0800 Subject: [PATCH 10/88] fix/TD-32863-add-creating-hash-fix-case --- source/dnode/mgmt/mgmt_vnode/src/vmHandle.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c b/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c index c9512d06d4..90b3f0025d 100644 --- a/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c +++ b/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c @@ -381,6 +381,7 @@ int32_t vmProcessCreateVnodeReq(SVnodeMgmt *pMgmt, SRpcMsg *pMsg) { if (vnodeCreate(path, &vnodeCfg, diskPrimary, pMgmt->pTfs) < 0) { dError("vgId:%d, failed to create vnode since %s", req.vgId, terrstr()); vmReleaseVnode(pMgmt, pVnode); + vmRemoveFromCreatingHash(pMgmt, req.vgId); (void)tFreeSCreateVnodeReq(&req); code = terrno != 0 ? terrno : -1; return code; @@ -421,9 +422,9 @@ int32_t vmProcessCreateVnodeReq(SVnodeMgmt *pMgmt, SRpcMsg *pMsg) { goto _OVER; } +_OVER: vmRemoveFromCreatingHash(pMgmt, req.vgId); -_OVER: if (code != 0) { int32_t r = 0; r = taosThreadRwlockWrlock(&pMgmt->lock); From 2b069db01fe2ff08e083b9eda0d9e4b17d717e6f Mon Sep 17 00:00:00 2001 From: dmchen Date: Wed, 13 Nov 2024 14:26:44 +0800 Subject: [PATCH 11/88] fix/TD-32863-add-creating-hash-add-testcase --- tests/parallel_test/cases.task | 1 + .../0-others/multilevel_createdb.py | 89 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 tests/system-test/0-others/multilevel_createdb.py diff --git a/tests/parallel_test/cases.task b/tests/parallel_test/cases.task index 67fb784b61..90b5f4fee9 100644 --- a/tests/parallel_test/cases.task +++ b/tests/parallel_test/cases.task @@ -351,6 +351,7 @@ ,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/user_privilege_all.py ,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/fsync.py ,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/multilevel.py +,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/multilevel_createdb.py ,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/ttl.py ,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/ttlChangeOnWrite.py ,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/compress_tsz1.py diff --git a/tests/system-test/0-others/multilevel_createdb.py b/tests/system-test/0-others/multilevel_createdb.py new file mode 100644 index 0000000000..a4fd94f4c1 --- /dev/null +++ b/tests/system-test/0-others/multilevel_createdb.py @@ -0,0 +1,89 @@ +################################################################### +# Copyright (c) 2016 by TAOS Technologies, Inc. +# All rights reserved. +# +# This file is proprietary and confidential to TAOS Technologies. +# No part of this file may be reproduced, stored, transmitted, +# disclosed or used in any form or by any means other than as +# expressly provided by the written permission from Jianhui Tao +# +################################################################### + +# -*- coding: utf-8 -*- + + +from util.log import * +from util.cases import * +from util.sql import * +from util.common import * +from util.sqlset import * +import glob + +def scanFiles(pattern): + res = [] + for f in glob.iglob(pattern): + res += [f] + return res + +def checkFiles(pattern, state): + res = scanFiles(pattern) + tdLog.info(res) + num = len(res) + if num: + if state: + tdLog.info("%s: %d files exist. expect: files exist" % (pattern, num)) + else: + tdLog.exit("%s: %d files exist. expect: files not exist." % (pattern, num)) + else: + if state: + tdLog.exit("%s: %d files exist. expect: files exist" % (pattern, num)) + else: + tdLog.info("%s: %d files exist. expect: files not exist." % (pattern, num)) + +class TDTestCase: + def init(self, conn, logSql, replicaVar=1): + + self.replicaVar = int(replicaVar) + tdLog.debug("start to execute %s" % __file__) + tdSql.init(conn.cursor()) + self.setsql = TDSetSql() + + def basic(self): + tdLog.info("============== basic test ===============") + cfg={ + '/mnt/data1 0 1 0' : 'dataDir', + '/mnt/data2 0 0 0' : 'dataDir', + '/mnt/data3 0 0 0' : 'dataDir', + '/mnt/data4 0 0 0' : 'dataDir' + } + tdSql.createDir('/mnt/data1') + tdSql.createDir('/mnt/data2') + tdSql.createDir('/mnt/data3') + tdSql.createDir('/mnt/data4') + + tdDnodes.stop(1) + tdDnodes.deploy(1,cfg) + tdDnodes.start(1) + + checkFiles(r'/mnt/data1/*/*',1) + checkFiles(r'/mnt/data2/*/*',0) + + tdSql.execute('create database nws vgroups 20 buffer 3000 stt_trigger 1 wal_level 1 wal_retention_period 0') + + checkFiles(r'/mnt/data1/vnode/*/wal',5) + checkFiles(r'/mnt/data2/vnode/*/wal',5) + checkFiles(r'/mnt/data3/vnode/*/wal',5) + checkFiles(r'/mnt/data4/vnode/*/wal',5) + + def run(self): + self.basic() + + + + + def stop(self): + tdSql.close() + tdLog.success("%s successfully executed" % __file__) + +tdCases.addWindows(__file__, TDTestCase()) +tdCases.addLinux(__file__, TDTestCase()) \ No newline at end of file From 0b9f15817489d3823c47e8a91e655463e8afb325 Mon Sep 17 00:00:00 2001 From: dmchen Date: Wed, 13 Nov 2024 14:48:57 +0800 Subject: [PATCH 12/88] fix/TD-32863-add-creating-hash-fix-case --- source/dnode/mgmt/mgmt_vnode/src/vmInt.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/dnode/mgmt/mgmt_vnode/src/vmInt.c b/source/dnode/mgmt/mgmt_vnode/src/vmInt.c index 26ac901f58..d28394e332 100644 --- a/source/dnode/mgmt/mgmt_vnode/src/vmInt.c +++ b/source/dnode/mgmt/mgmt_vnode/src/vmInt.c @@ -105,6 +105,7 @@ int32_t vmAllocPrimaryDisk(SVnodeMgmt *pMgmt, int32_t vgId) { pCreatingVnode->vgId = vgId; pCreatingVnode->diskPrimary = diskId; + dTrace("vgId:%d, put vnode into creating hash, pCreatingVnode:%p", vgId, pCreatingVnode); int32_t r = taosHashPut(pMgmt->creatingHash, &vgId, sizeof(int32_t), &pCreatingVnode, sizeof(SVnodeObj *)); if (r != 0) { dError("vgId:%d, failed to put vnode to creatingHash", vgId); @@ -556,13 +557,15 @@ static int32_t vmOpenVnodes(SVnodeMgmt *pMgmt) { void vmRemoveFromCreatingHash(SVnodeMgmt *pMgmt, int32_t vgId) { (void)taosThreadRwlockWrlock(&pMgmt->lock); SVnodeObj *pOld = NULL; - int32_t r = taosHashGetDup(pMgmt->closedHash, &vgId, sizeof(int32_t), (void *)&pOld); + int32_t r = taosHashGetDup(pMgmt->creatingHash, &vgId, sizeof(int32_t), (void *)&pOld); if (r != 0) { - dError("vgId:%d, failed to get vnode from closedHash", vgId); + dError("vgId:%d, failed to get vnode from creating Hash", vgId); } if (pOld) { + dTrace("vgId:%d, free vnode pOld:%p", vgId, &pOld); vmFreeVnodeObj(&pOld); } + dTrace("vgId:%d, remove from creating Hash", vgId); r = taosHashRemove(pMgmt->creatingHash, &vgId, sizeof(int32_t)); if (r != 0) { dError("vgId:%d, failed to remove vnode from hash", vgId); From 7ff6884779cde6a77e201c5886e022ee1d740b26 Mon Sep 17 00:00:00 2001 From: dmchen Date: Wed, 13 Nov 2024 16:07:45 +0800 Subject: [PATCH 13/88] fix/TD-32863-add-creating-hash-change-lock --- source/dnode/mgmt/mgmt_vnode/src/vmInt.c | 47 +++++++++++++++++------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/source/dnode/mgmt/mgmt_vnode/src/vmInt.c b/source/dnode/mgmt/mgmt_vnode/src/vmInt.c index d28394e332..a81208c567 100644 --- a/source/dnode/mgmt/mgmt_vnode/src/vmInt.c +++ b/source/dnode/mgmt/mgmt_vnode/src/vmInt.c @@ -16,6 +16,7 @@ #define _DEFAULT_SOURCE #include "vmInt.h" #include "libs/function/tudf.h" +#include "osMemory.h" #include "tfs.h" #include "vnd.h" @@ -87,17 +88,15 @@ int32_t vmAllocPrimaryDisk(SVnodeMgmt *pMgmt, int32_t vgId) { } } - code = taosThreadMutexUnlock(&pMgmt->mutex); - if (code != 0) { - goto _OVER; - } - - (void)taosThreadRwlockWrlock(&pMgmt->lock); - SVnodeObj *pCreatingVnode = taosMemoryCalloc(1, sizeof(SVnodeObj)); if (pCreatingVnode == NULL) { - (void)taosThreadRwlockUnlock(&pMgmt->lock); - dError("failed to alloc vnode since %s", terrstr()); + code = -1; + if (terrno != 0) code = terrno; + dError("failed to alloc vnode since %s", tstrerror(code)); + int32_t r = taosThreadMutexUnlock(&pMgmt->mutex); + if (r != 0) { + dError("vgId:%d, failed to unlock mutex since %s", vgId, tstrerror(r)); + } goto _OVER; } (void)memset(pCreatingVnode, 0, sizeof(SVnodeObj)); @@ -105,12 +104,32 @@ int32_t vmAllocPrimaryDisk(SVnodeMgmt *pMgmt, int32_t vgId) { pCreatingVnode->vgId = vgId; pCreatingVnode->diskPrimary = diskId; - dTrace("vgId:%d, put vnode into creating hash, pCreatingVnode:%p", vgId, pCreatingVnode); - int32_t r = taosHashPut(pMgmt->creatingHash, &vgId, sizeof(int32_t), &pCreatingVnode, sizeof(SVnodeObj *)); - if (r != 0) { - dError("vgId:%d, failed to put vnode to creatingHash", vgId); + code = taosThreadRwlockWrlock(&pMgmt->lock); + if (code != 0) { + int32_t r = taosThreadMutexUnlock(&pMgmt->mutex); + if (r != 0) { + dError("vgId:%d, failed to unlock mutex since %s", vgId, tstrerror(r)); + } + taosMemoryFree(pCreatingVnode); + goto _OVER; + } + + dTrace("vgId:%d, put vnode into creating hash, pCreatingVnode:%p", vgId, pCreatingVnode); + code = taosHashPut(pMgmt->creatingHash, &vgId, sizeof(int32_t), &pCreatingVnode, sizeof(SVnodeObj *)); + if (code != 0) { + dError("vgId:%d, failed to put vnode to creatingHash", vgId); + taosMemoryFree(pCreatingVnode); + } + + int32_t r = taosThreadRwlockUnlock(&pMgmt->lock); + if (r != 0) { + dError("vgId:%d, failed to unlock since %s", vgId, tstrerror(r)); + } + + code = taosThreadMutexUnlock(&pMgmt->mutex); + if (code != 0) { + goto _OVER; } - (void)taosThreadRwlockUnlock(&pMgmt->lock); _OVER: From ea7c30505a8c1394a8c315defdf08b84f5eb011f Mon Sep 17 00:00:00 2001 From: 54liuyao <54liuyao@163.com> Date: Wed, 13 Nov 2024 19:37:22 +0800 Subject: [PATCH 14/88] fix(stream):fix bloom filter decode issue --- source/libs/executor/src/scanoperator.c | 28 +++++++++++++------------ 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/source/libs/executor/src/scanoperator.c b/source/libs/executor/src/scanoperator.c index 5b5d5c5d11..ecb6bd7aee 100644 --- a/source/libs/executor/src/scanoperator.c +++ b/source/libs/executor/src/scanoperator.c @@ -3468,11 +3468,6 @@ void streamScanOperatorDecode(void* pBuff, int32_t len, SStreamScanInfo* pInfo) goto _end; } - void* pUpInfo = taosMemoryCalloc(1, sizeof(SUpdateInfo)); - if (!pUpInfo) { - lino = __LINE__; - goto _end; - } SDecoder decoder = {0}; pDeCoder = &decoder; tDecoderInit(pDeCoder, buf, tlen); @@ -3481,14 +3476,21 @@ void streamScanOperatorDecode(void* pBuff, int32_t len, SStreamScanInfo* pInfo) goto _end; } - code = pInfo->stateStore.updateInfoDeserialize(pDeCoder, pUpInfo); - if (code == TSDB_CODE_SUCCESS) { - pInfo->stateStore.updateInfoDestroy(pInfo->pUpdateInfo); - pInfo->pUpdateInfo = pUpInfo; - } else { - taosMemoryFree(pUpInfo); - lino = __LINE__; - goto _end; + if (pInfo->pUpdateInfo != NULL) { + void* pUpInfo = taosMemoryCalloc(1, sizeof(SUpdateInfo)); + if (!pUpInfo) { + lino = __LINE__; + goto _end; + } + code = pInfo->stateStore.updateInfoDeserialize(pDeCoder, pUpInfo); + if (code == TSDB_CODE_SUCCESS) { + pInfo->stateStore.updateInfoDestroy(pInfo->pUpdateInfo); + pInfo->pUpdateInfo = pUpInfo; + } else { + taosMemoryFree(pUpInfo); + lino = __LINE__; + goto _end; + } } if (tDecodeIsEnd(pDeCoder)) { From 336429e14904e60ce05188c3b82988363c272da5 Mon Sep 17 00:00:00 2001 From: dmchen Date: Thu, 14 Nov 2024 16:01:28 +0800 Subject: [PATCH 15/88] fix/TD-32863-add-creating-hash-fix-review --- source/dnode/mgmt/mgmt_vnode/src/vmInt.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/dnode/mgmt/mgmt_vnode/src/vmInt.c b/source/dnode/mgmt/mgmt_vnode/src/vmInt.c index a81208c567..2ee607949d 100644 --- a/source/dnode/mgmt/mgmt_vnode/src/vmInt.c +++ b/source/dnode/mgmt/mgmt_vnode/src/vmInt.c @@ -63,14 +63,18 @@ int32_t vmAllocPrimaryDisk(SVnodeMgmt *pMgmt, int32_t vgId) { int32_t numOfVnodes = 0; SVnodeObj **ppVnodes = NULL; - code = vmGetAllVnodeListFromHashWithCreating(pMgmt, &numOfVnodes, &ppVnodes); + code = taosThreadMutexLock(&pMgmt->mutex); if (code != 0) { return code; } - code = taosThreadMutexLock(&pMgmt->mutex); + code = vmGetAllVnodeListFromHashWithCreating(pMgmt, &numOfVnodes, &ppVnodes); if (code != 0) { - goto _OVER; + int32_t r = taosThreadMutexUnlock(&pMgmt->mutex); + if (r != 0) { + dError("vgId:%d, failed to unlock mutex since %s", vgId, tstrerror(r)); + } + return code; } for (int32_t v = 0; v < numOfVnodes; v++) { From 4a09cb7138b36007e974dcb0725fd818d2a1e62f Mon Sep 17 00:00:00 2001 From: dmchen Date: Fri, 15 Nov 2024 08:05:50 +0800 Subject: [PATCH 16/88] fix/TD-32863-add-creating-hash-fix-case --- tests/system-test/0-others/multilevel_createdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system-test/0-others/multilevel_createdb.py b/tests/system-test/0-others/multilevel_createdb.py index a4fd94f4c1..70131a760b 100644 --- a/tests/system-test/0-others/multilevel_createdb.py +++ b/tests/system-test/0-others/multilevel_createdb.py @@ -68,7 +68,7 @@ class TDTestCase: checkFiles(r'/mnt/data1/*/*',1) checkFiles(r'/mnt/data2/*/*',0) - tdSql.execute('create database nws vgroups 20 buffer 3000 stt_trigger 1 wal_level 1 wal_retention_period 0') + tdSql.execute('create database nws vgroups 20 stt_trigger 1 wal_level 1 wal_retention_period 0') checkFiles(r'/mnt/data1/vnode/*/wal',5) checkFiles(r'/mnt/data2/vnode/*/wal',5) From a854597de14c05b8fcf172f2484bcc01f69e84b5 Mon Sep 17 00:00:00 2001 From: Yu Chen <74105241+yu285@users.noreply.github.com> Date: Fri, 15 Nov 2024 13:48:57 +0800 Subject: [PATCH 17/88] docs:Update keep description in 02-database.md --- docs/zh/14-reference/03-taos-sql/02-database.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh/14-reference/03-taos-sql/02-database.md b/docs/zh/14-reference/03-taos-sql/02-database.md index 91b39976a1..0d9ab019c0 100644 --- a/docs/zh/14-reference/03-taos-sql/02-database.md +++ b/docs/zh/14-reference/03-taos-sql/02-database.md @@ -63,7 +63,7 @@ database_option: { - DURATION:数据文件存储数据的时间跨度。可以使用加单位的表示形式,如 DURATION 100h、DURATION 10d 等,支持 m(分钟)、h(小时)和 d(天)三个单位。不加时间单位时默认单位为天,如 DURATION 50 表示 50 天。 - MAXROWS:文件块中记录的最大条数,默认为 4096 条。 - MINROWS:文件块中记录的最小条数,默认为 100 条。 -- KEEP:表示数据文件保存的天数,缺省值为 3650,取值范围 [1, 365000],且必须大于或等于3倍的 DURATION 参数值。数据库会自动删除保存时间超过 KEEP 值的数据。KEEP 可以使用加单位的表示形式,如 KEEP 100h、KEEP 10d 等,支持 m(分钟)、h(小时)和 d(天)三个单位。也可以不写单位,如 KEEP 50,此时默认单位为天。企业版支持[多级存储](https://docs.taosdata.com/tdinternal/arch/#%E5%A4%9A%E7%BA%A7%E5%AD%98%E5%82%A8)功能, 因此, 可以设置多个保存时间(多个以英文逗号分隔,最多 3 个,满足 keep 0 \<= keep 1 \<= keep 2,如 KEEP 100h,100d,3650d); 社区版不支持多级存储功能(即使配置了多个保存时间, 也不会生效, KEEP 会取最大的保存时间)。 +- KEEP:表示数据文件保存的天数,缺省值为 3650,取值范围 [1, 365000],且必须大于或等于3倍的 DURATION 参数值。数据库会自动删除保存时间超过 KEEP 值的数据从而释放存储空间。KEEP 可以使用加单位的表示形式,如 KEEP 100h、KEEP 10d 等,支持 m(分钟)、h(小时)和 d(天)三个单位。也可以不写单位,如 KEEP 50,此时默认单位为天。企业版支持[多级存储](https://docs.taosdata.com/tdinternal/arch/#%E5%A4%9A%E7%BA%A7%E5%AD%98%E5%82%A8)功能, 因此, 可以设置多个保存时间(多个以英文逗号分隔,最多 3 个,满足 keep 0 \<= keep 1 \<= keep 2,如 KEEP 100h,100d,3650d); 社区版不支持多级存储功能(即使配置了多个保存时间, 也不会生效, KEEP 会取最大的保存时间)。 - STT_TRIGGER:表示落盘文件触发文件合并的个数。开源版本固定为 1,企业版本可设置范围为 1 到 16。对于少表高频写入场景,此参数建议使用默认配置;而对于多表低频写入场景,此参数建议配置较大的值。 - SINGLE_STABLE:表示此数据库中是否只可以创建一个超级表,用于超级表列非常多的情况。 - 0:表示可以创建多张超级表。 From 07a11a4ea58498c1f0befa7b7b778128412f68d5 Mon Sep 17 00:00:00 2001 From: happyguoxy Date: Fri, 15 Nov 2024 13:49:26 +0800 Subject: [PATCH 18/88] test:add stream test cases --- tests/system-test/8-stream/checkpoint_info.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/system-test/8-stream/checkpoint_info.py b/tests/system-test/8-stream/checkpoint_info.py index 00e5d1f688..522017a702 100644 --- a/tests/system-test/8-stream/checkpoint_info.py +++ b/tests/system-test/8-stream/checkpoint_info.py @@ -84,6 +84,7 @@ class TDTestCase: def restart_stream(self): tdLog.debug("========restart stream========") + time.sleep(10) for i in range(5): tdSql.execute("pause stream s1") time.sleep(2) @@ -136,4 +137,4 @@ class TDTestCase: tdSql.close() tdLog.success(f"{__file__} successfully executed") tdCases.addLinux(__file__, TDTestCase()) -tdCases.addWindows(__file__, TDTestCase()) \ No newline at end of file +tdCases.addWindows(__file__, TDTestCase()) From 0c45c80d71b55feff88a56af68f28c2c7d8a361d Mon Sep 17 00:00:00 2001 From: Hongze Cheng Date: Fri, 15 Nov 2024 14:26:53 +0800 Subject: [PATCH 19/88] enh: add dangle child table filter in recovery mode --- source/dnode/vnode/src/meta/metaOpen.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/dnode/vnode/src/meta/metaOpen.c b/source/dnode/vnode/src/meta/metaOpen.c index 659ba3f777..9a5bea33e3 100644 --- a/source/dnode/vnode/src/meta/metaOpen.c +++ b/source/dnode/vnode/src/meta/metaOpen.c @@ -324,7 +324,11 @@ static int32_t metaGenerateNewMeta(SMeta **ppMeta) { SMetaEntry me = {0}; tDecoderInit(&dc, value, valueSize); if (metaDecodeEntry(&dc, &me) == 0) { - if (metaHandleEntry(pNewMeta, &me) != 0) { + if (me.type == TSDB_CHILD_TABLE && + tdbTbGet(pMeta->pUidIdx, &me.ctbEntry.suid, sizeof(me.ctbEntry.suid), NULL, NULL) != 0) { + metaError("vgId:%d failed to get super table uid:%" PRId64 " for child table uid:%" PRId64, + TD_VID(pVnode), me.ctbEntry.suid, uid); + } else if (metaHandleEntry(pNewMeta, &me) != 0) { metaError("vgId:%d failed to handle entry, uid:%" PRId64, TD_VID(pVnode), uid); } } From 2f978a11c2721ea7bfe7a3e7e958a2d4e67baeac Mon Sep 17 00:00:00 2001 From: happyguoxy Date: Fri, 15 Nov 2024 15:01:34 +0800 Subject: [PATCH 20/88] test:add stream test cases --- tests/system-test/8-stream/checkpoint_info2.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/system-test/8-stream/checkpoint_info2.py b/tests/system-test/8-stream/checkpoint_info2.py index 7f2e8b0672..3dc57477f7 100644 --- a/tests/system-test/8-stream/checkpoint_info2.py +++ b/tests/system-test/8-stream/checkpoint_info2.py @@ -33,17 +33,21 @@ class TDTestCase: info_path = os.path.join(root, f'checkpoint{checkpointid}', 'info') if os.path.exists(info_path): if task_id in info_path: + tdLog.info(f"info file found in {info_path}") return info_path else: continue else: + tdLog.info(f"info file not found in {info_path}") return None + else: + tdLog.info(f"no checkpoint{checkpointid} in {dirpath}") def get_dnode_info(self): ''' get a dict from vnode to dnode ''' self.vnode_dict = {} - sql = 'select dnode_id, vgroup_id from information_schema.ins_vnodes' + sql = 'select dnode_id, vgroup_id from information_schema.ins_vnodes where status = "leader"' result = tdSql.getResult(sql) for (dnode,vnode) in result: self.vnode_dict[vnode] = dnode @@ -68,15 +72,18 @@ class TDTestCase: while(True): if(self.check_vnodestate()): break + self.get_dnode_info() sql = 'select task_id, node_id, checkpoint_id, checkpoint_ver from information_schema.ins_stream_tasks where `level` = "source" or `level` = "agg" and node_type == "vnode"' for task_id, vnode, checkpoint_id, checkpoint_ver in tdSql.getResult(sql): dirpath = f"{cluster.dnodes[self.vnode_dict[vnode]-1].dataDir}/vnode/vnode{vnode}/" info_path = self.find_checkpoint_info_file(dirpath, checkpoint_id, task_id) if info_path is None: + tdLog.info(f"info path: {dirpath} is null") return False with open(info_path, 'r') as f: info_id, info_ver = f.read().split() if int(info_id) != int(checkpoint_id) or int(info_ver) != int(checkpoint_ver): + tdLog.info(f"infoId: {info_id}, checkpointId: {checkpoint_id}, infoVer: {info_ver}, checkpointVer: {checkpoint_ver}") return False return True @@ -131,4 +138,4 @@ class TDTestCase: tdLog.success(f"{__file__} successfully executed") tdCases.addLinux(__file__, TDTestCase()) -tdCases.addWindows(__file__, TDTestCase()) \ No newline at end of file +tdCases.addWindows(__file__, TDTestCase()) From 9cc2aec9d66d27aa5a9d1873291f9be797d064b1 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Fri, 15 Nov 2024 17:12:01 +0800 Subject: [PATCH 21/88] fix(stream): set correct value and open inputQ for stream if reset checkpoint. --- source/dnode/mnode/impl/src/mndStream.c | 7 ++++++- source/libs/stream/src/streamCheckpoint.c | 15 +++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/source/dnode/mnode/impl/src/mndStream.c b/source/dnode/mnode/impl/src/mndStream.c index 81db427afd..6336cd6e49 100644 --- a/source/dnode/mnode/impl/src/mndStream.c +++ b/source/dnode/mnode/impl/src/mndStream.c @@ -2434,7 +2434,12 @@ static void doAddReportStreamTask(SArray *pList, int64_t reportChkptId, const SC mDebug("s-task:0x%x expired checkpoint-report msg in checkpoint-report list update from %" PRId64 "->%" PRId64, pReport->taskId, p->checkpointId, pReport->checkpointId); - memcpy(p, pReport, sizeof(STaskChkptInfo)); + // update the checkpoint report info + p->checkpointId = pReport->checkpointId; + p->ts = pReport->checkpointTs; + p->version = pReport->checkpointVer; + p->transId = pReport->transId; + p->dropHTask = pReport->dropHTask; } else { mWarn("taskId:0x%x already in checkpoint-report list", pReport->taskId); } diff --git a/source/libs/stream/src/streamCheckpoint.c b/source/libs/stream/src/streamCheckpoint.c index 7724d1c5ff..df13b9a585 100644 --- a/source/libs/stream/src/streamCheckpoint.c +++ b/source/libs/stream/src/streamCheckpoint.c @@ -255,8 +255,7 @@ static int32_t doCheckBeforeHandleChkptTrigger(SStreamTask* pTask, int64_t check "the interrupted checkpoint", id, vgId, pBlock->srcTaskId); - streamTaskOpenUpstreamInput(pTask, pBlock->srcTaskId); - return code; + return TSDB_CODE_STREAM_INVLD_CHKPT; } if (streamTaskGetStatus(pTask).state == TASK_STATUS__CK) { @@ -264,14 +263,14 @@ static int32_t doCheckBeforeHandleChkptTrigger(SStreamTask* pTask, int64_t check stError("s-task:%s vgId:%d active checkpointId:%" PRId64 ", recv invalid checkpoint-trigger checkpointId:%" PRId64 " discard", id, vgId, pActiveInfo->activeId, checkpointId); - return code; + return TSDB_CODE_STREAM_INVLD_CHKPT; } else { // checkpointId == pActiveInfo->activeId if (pActiveInfo->allUpstreamTriggerRecv == 1) { stDebug( "s-task:%s vgId:%d all upstream checkpoint-trigger recv, discard this checkpoint-trigger, " "checkpointId:%" PRId64 " transId:%d", id, vgId, checkpointId, transId); - return code; + return TSDB_CODE_STREAM_INVLD_CHKPT; } if (taskLevel == TASK_LEVEL__SINK || taskLevel == TASK_LEVEL__AGG) { @@ -286,14 +285,14 @@ static int32_t doCheckBeforeHandleChkptTrigger(SStreamTask* pTask, int64_t check stWarn("s-task:%s repeatly recv checkpoint-source msg from task:0x%x vgId:%d, checkpointId:%" PRId64 ", prev recvTs:%" PRId64 " discard", pTask->id.idStr, p->upstreamTaskId, p->upstreamNodeId, p->checkpointId, p->recvTs); - return code; + return TSDB_CODE_STREAM_INVLD_CHKPT; } } } } } - return 0; + return TSDB_CODE_SUCCESS; } int32_t streamProcessCheckpointTriggerBlock(SStreamTask* pTask, SStreamDataBlock* pBlock) { @@ -317,6 +316,10 @@ int32_t streamProcessCheckpointTriggerBlock(SStreamTask* pTask, SStreamDataBlock code = doCheckBeforeHandleChkptTrigger(pTask, checkpointId, pBlock, transId); streamMutexUnlock(&pTask->lock); if (code) { + if (taskLevel != TASK_LEVEL__SOURCE) { // the checkpoint-trigger is discard, open the inputQ for upstream tasks + streamTaskOpenUpstreamInput(pTask, pBlock->srcTaskId); + } + streamFreeQitem((SStreamQueueItem*)pBlock); return code; } From 628808c9a4bbfe83d8efff189b284442de1ed066 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Fri, 15 Nov 2024 17:16:47 +0800 Subject: [PATCH 22/88] fix(stream): fix dead lock. --- source/libs/stream/src/streamDispatch.c | 2 ++ source/libs/stream/src/streamStartTask.c | 1 + 2 files changed, 3 insertions(+) diff --git a/source/libs/stream/src/streamDispatch.c b/source/libs/stream/src/streamDispatch.c index e0fa199199..5807240f5e 100644 --- a/source/libs/stream/src/streamDispatch.c +++ b/source/libs/stream/src/streamDispatch.c @@ -1170,6 +1170,7 @@ int32_t streamTaskSendCheckpointReadyMsg(SStreamTask* pTask) { if (taosArrayGetSize(pTask->upstreamInfo.pList) != num) { stError("s-task:%s invalid number of sent readyMsg:%d to upstream:%d", id, num, (int32_t)taosArrayGetSize(pTask->upstreamInfo.pList)); + streamMutexUnlock(&pActiveInfo->lock); return TSDB_CODE_STREAM_INTERNAL_ERROR; } @@ -1412,6 +1413,7 @@ int32_t streamAddCheckpointSourceRspMsg(SStreamCheckpointSourceReq* pReq, SRpcHa if (size > 0) { STaskCheckpointReadyInfo* pReady = taosArrayGet(pActiveInfo->pReadyMsgList, 0); if (pReady == NULL) { + streamMutexUnlock(&pActiveInfo->lock); return terrno; } diff --git a/source/libs/stream/src/streamStartTask.c b/source/libs/stream/src/streamStartTask.c index ed12687e41..9c16ff036e 100644 --- a/source/libs/stream/src/streamStartTask.c +++ b/source/libs/stream/src/streamStartTask.c @@ -433,6 +433,7 @@ int32_t streamMetaStopAllTasks(SStreamMeta* pMeta) { // send hb msg to mnode before closing all tasks. int32_t code = streamMetaSendMsgBeforeCloseTasks(pMeta, &pTaskList); if (code != TSDB_CODE_SUCCESS) { + streamMetaRUnLock(pMeta); return code; } From f304a92229ab4e4cc072165cdb8a5632a87682f6 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Fri, 15 Nov 2024 17:21:20 +0800 Subject: [PATCH 23/88] refactor: add new error code. --- include/util/taoserror.h | 1 + source/util/src/terror.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/util/taoserror.h b/include/util/taoserror.h index 9a8b39b84c..b563c186c3 100644 --- a/include/util/taoserror.h +++ b/include/util/taoserror.h @@ -1010,6 +1010,7 @@ int32_t taosGetErrSize(); #define TSDB_CODE_STREAM_CONFLICT_EVENT TAOS_DEF_ERROR_CODE(0, 0x4106) #define TSDB_CODE_STREAM_INTERNAL_ERROR TAOS_DEF_ERROR_CODE(0, 0x4107) #define TSDB_CODE_STREAM_INPUTQ_FULL TAOS_DEF_ERROR_CODE(0, 0x4108) +#define TSDB_CODE_STREAM_INVLD_CHKPT TAOS_DEF_ERROR_CODE(0, 0x4109) // TDLite #define TSDB_CODE_TDLITE_IVLD_OPEN_FLAGS TAOS_DEF_ERROR_CODE(0, 0x5100) diff --git a/source/util/src/terror.c b/source/util/src/terror.c index ce209cc718..ce754e8795 100644 --- a/source/util/src/terror.c +++ b/source/util/src/terror.c @@ -852,7 +852,8 @@ TAOS_DEFINE_ERROR(TSDB_CODE_STREAM_TASK_IVLD_STATUS, "Invalid task status TAOS_DEFINE_ERROR(TSDB_CODE_STREAM_CONFLICT_EVENT, "Stream conflict event") TAOS_DEFINE_ERROR(TSDB_CODE_STREAM_INTERNAL_ERROR, "Stream internal error") TAOS_DEFINE_ERROR(TSDB_CODE_STREAM_NOT_LEADER, "Stream task not on leader vnode") -TAOS_DEFINE_ERROR(TSDB_CODE_STREAM_INPUTQ_FULL, "Task input queue is full") +TAOS_DEFINE_ERROR(TSDB_CODE_STREAM_INPUTQ_FULL, "Task input queue is full") +TAOS_DEFINE_ERROR(TSDB_CODE_STREAM_INVLD_CHKPT, "Invalid checkpoint trigger msg") // TDLite TAOS_DEFINE_ERROR(TSDB_CODE_TDLITE_IVLD_OPEN_FLAGS, "Invalid TDLite open flags") From b1981d309fefe53bd85a6bf1e5b034127fb2e372 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Fri, 15 Nov 2024 17:41:14 +0800 Subject: [PATCH 24/88] fix(stream): return error code. --- source/libs/stream/src/streamCheckpoint.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/libs/stream/src/streamCheckpoint.c b/source/libs/stream/src/streamCheckpoint.c index df13b9a585..6c4c285ded 100644 --- a/source/libs/stream/src/streamCheckpoint.c +++ b/source/libs/stream/src/streamCheckpoint.c @@ -222,14 +222,14 @@ static int32_t doCheckBeforeHandleChkptTrigger(SStreamTask* pTask, int64_t check stError("s-task:%s vgId:%d current checkpointId:%" PRId64 " recv expired checkpoint-trigger block, checkpointId:%" PRId64 " transId:%d, discard", id, vgId, pTask->chkInfo.checkpointId, checkpointId, transId); - return code; + return TSDB_CODE_STREAM_TASK_NOT_EXIST; } if (pActiveInfo->failedId >= checkpointId) { stError("s-task:%s vgId:%d checkpointId:%" PRId64 " transId:%d, has been marked failed, failedId:%" PRId64 " discard the checkpoint-trigger block", id, vgId, checkpointId, transId, pActiveInfo->failedId); - return code; + return TSDB_CODE_STREAM_TASK_NOT_EXIST; } if (pTask->chkInfo.checkpointId == checkpointId) { From 09fe252a31831e05707a139172b546b0ab1a1468 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Fri, 15 Nov 2024 17:52:06 +0800 Subject: [PATCH 25/88] remove duplicate group by cols --- include/libs/nodes/nodes.h | 1 + source/libs/nodes/src/nodesEqualFuncs.c | 9 +++++- source/libs/nodes/src/nodesUtilFuncs.c | 34 ++++++++++++++++++++ source/libs/parser/src/parTranslater.c | 2 -- source/libs/planner/src/planLogicCreater.c | 7 ++-- tests/system-test/2-query/group_partition.py | 13 ++++++++ 6 files changed, 61 insertions(+), 5 deletions(-) diff --git a/include/libs/nodes/nodes.h b/include/libs/nodes/nodes.h index 72dd3ef3e0..6384c536ce 100644 --- a/include/libs/nodes/nodes.h +++ b/include/libs/nodes/nodes.h @@ -174,6 +174,7 @@ char* nodesGetNameFromColumnNode(SNode* pNode); int32_t nodesGetOutputNumFromSlotList(SNodeList* pSlots); void nodesSortList(SNodeList** pList, int32_t (*)(SNode* pNode1, SNode* pNode2)); void destroyFuncParam(void* pFuncStruct); +int32_t nodesListDeduplicate(SNodeList** pList); #ifdef __cplusplus } diff --git a/source/libs/nodes/src/nodesEqualFuncs.c b/source/libs/nodes/src/nodesEqualFuncs.c index 241da85267..891843761a 100644 --- a/source/libs/nodes/src/nodesEqualFuncs.c +++ b/source/libs/nodes/src/nodesEqualFuncs.c @@ -153,6 +153,12 @@ static bool caseWhenNodeEqual(const SCaseWhenNode* a, const SCaseWhenNode* b) { return true; } +static bool groupingSetNodeEqual(const SGroupingSetNode* a, const SGroupingSetNode* b) { + COMPARE_SCALAR_FIELD(groupingSetType); + COMPARE_NODE_LIST_FIELD(pParameterList); + return true; +} + bool nodesEqualNode(const SNode* a, const SNode* b) { if (a == b) { return true; @@ -181,10 +187,11 @@ bool nodesEqualNode(const SNode* a, const SNode* b) { return whenThenNodeEqual((const SWhenThenNode*)a, (const SWhenThenNode*)b); case QUERY_NODE_CASE_WHEN: return caseWhenNodeEqual((const SCaseWhenNode*)a, (const SCaseWhenNode*)b); + case QUERY_NODE_GROUPING_SET: + return groupingSetNodeEqual((const SGroupingSetNode*)a, (const SGroupingSetNode*)b); case QUERY_NODE_REAL_TABLE: case QUERY_NODE_TEMP_TABLE: case QUERY_NODE_JOIN_TABLE: - case QUERY_NODE_GROUPING_SET: case QUERY_NODE_ORDER_BY_EXPR: case QUERY_NODE_LIMIT: return false; diff --git a/source/libs/nodes/src/nodesUtilFuncs.c b/source/libs/nodes/src/nodesUtilFuncs.c index a9d0aa2924..4677b5d5f0 100644 --- a/source/libs/nodes/src/nodesUtilFuncs.c +++ b/source/libs/nodes/src/nodesUtilFuncs.c @@ -2948,3 +2948,37 @@ void nodesSortList(SNodeList** pList, int32_t (*comp)(SNode* pNode1, SNode* pNod inSize *= 2; } } + +static SNode* nodesListFindNode(SNodeList* pList, SNode* pNode) { + SNode* pFound = NULL; + FOREACH(pFound, pList) { + if (nodesEqualNode(pFound, pNode)) { + break; + } + } + return pFound; +} + +int32_t nodesListDeduplicate(SNodeList** ppList) { + if (!ppList || LIST_LENGTH(*ppList) == 0) return TSDB_CODE_SUCCESS; + SNodeList* pTmp = NULL; + int32_t code = nodesMakeList(&pTmp); + if (TSDB_CODE_SUCCESS == code) { + SNode* pNode = NULL; + FOREACH(pNode, *ppList) { + SNode* pFound = nodesListFindNode(pTmp, pNode); + if (NULL == pFound) { + code = nodesCloneNode(pNode, &pFound); + if (TSDB_CODE_SUCCESS == code) code = nodesListStrictAppend(pTmp, pFound); + if (TSDB_CODE_SUCCESS != code) break; + } + } + } + if (TSDB_CODE_SUCCESS == code) { + nodesDestroyList(*ppList); + *ppList = pTmp; + } else { + nodesDestroyList(pTmp); + } + return code; +} diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index 9bea3491c3..b77c349a78 100755 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -5531,7 +5531,6 @@ static int32_t translateGroupByList(STranslateContext* pCxt, SSelectStmt* pSelec SReplaceGroupByAliasCxt cxt = { .pTranslateCxt = pCxt, .pProjectionList = pSelect->pProjectionList}; nodesRewriteExprsPostOrder(pSelect->pGroupByList, translateGroupPartitionByImpl, &cxt); - return pCxt->errCode; } @@ -5543,7 +5542,6 @@ static int32_t translatePartitionByList(STranslateContext* pCxt, SSelectStmt* pS SReplaceGroupByAliasCxt cxt = { .pTranslateCxt = pCxt, .pProjectionList = pSelect->pProjectionList}; nodesRewriteExprsPostOrder(pSelect->pPartitionByList, translateGroupPartitionByImpl, &cxt); - return pCxt->errCode; } diff --git a/source/libs/planner/src/planLogicCreater.c b/source/libs/planner/src/planLogicCreater.c index 34c83acee8..f4c4926ca1 100644 --- a/source/libs/planner/src/planLogicCreater.c +++ b/source/libs/planner/src/planLogicCreater.c @@ -838,8 +838,11 @@ static int32_t createAggLogicNode(SLogicPlanContext* pCxt, SSelectStmt* pSelect, } if (NULL != pSelect->pGroupByList) { - pAgg->pGroupKeys = NULL; - code = nodesCloneList(pSelect->pGroupByList, &pAgg->pGroupKeys); + code = nodesListDeduplicate(&pSelect->pGroupByList); + if (TSDB_CODE_SUCCESS == code) { + pAgg->pGroupKeys = NULL; + code = nodesCloneList(pSelect->pGroupByList, &pAgg->pGroupKeys); + } } // rewrite the expression in subsequent clauses diff --git a/tests/system-test/2-query/group_partition.py b/tests/system-test/2-query/group_partition.py index 7ee528841c..d48df16231 100644 --- a/tests/system-test/2-query/group_partition.py +++ b/tests/system-test/2-query/group_partition.py @@ -437,6 +437,18 @@ class TDTestCase: tdSql.checkRows(10) tdSql.query(f"select const_col from (select 1 as const_col, count(c1) from {self.dbname}.{self.stable} t group by c1) partition by 1") tdSql.checkRows(10) + + def test_TD_32883(self): + sql = "select avg(c1), t9 from stb group by t9,t9, tbname" + tdSql.query(sql, queryTimes=1) + tdSql.checkRows(5) + sql = "select avg(c1), t10 from stb group by t10,t10, tbname" + tdSql.query(sql, queryTimes=1) + tdSql.checkRows(5) + sql = "select avg(c1), t10 from stb partition by t10,t10, tbname" + tdSql.query(sql, queryTimes=1) + tdSql.checkRows(5) + def run(self): tdSql.prepare() self.prepare_db() @@ -470,6 +482,7 @@ class TDTestCase: self.test_event_window(nonempty_tb_num) self.test_TS5567() + self.test_TD_32883() ## test old version before changed # self.test_groupby('group', 0, 0) From 2be0ca15d8653b6b593c38b9bcc90f1befa84690 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Fri, 15 Nov 2024 18:10:44 +0800 Subject: [PATCH 26/88] fix group_partition test --- source/dnode/vnode/src/tsdb/tsdbRead2.c | 4 ++-- source/libs/parser/src/parCalcConst.c | 2 ++ tests/system-test/2-query/group_partition.py | 8 ++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/source/dnode/vnode/src/tsdb/tsdbRead2.c b/source/dnode/vnode/src/tsdb/tsdbRead2.c index f30f7eb310..cc896dfa80 100644 --- a/source/dnode/vnode/src/tsdb/tsdbRead2.c +++ b/source/dnode/vnode/src/tsdb/tsdbRead2.c @@ -6115,7 +6115,7 @@ int32_t tsdbNextDataBlock2(STsdbReader* pReader, bool* hasNext) { TSDB_CHECK_CODE(code, lino, _end); } - goto _end; + return code; } } @@ -6142,7 +6142,7 @@ int32_t tsdbNextDataBlock2(STsdbReader* pReader, bool* hasNext) { acquired = false; TSDB_CHECK_CODE(code, lino, _end); } - goto _end; + return code; } if (pReader->step == EXTERNAL_ROWS_MAIN && pReader->innerReader[1] != NULL) { diff --git a/source/libs/parser/src/parCalcConst.c b/source/libs/parser/src/parCalcConst.c index e757ec8b24..778c34ff02 100644 --- a/source/libs/parser/src/parCalcConst.c +++ b/source/libs/parser/src/parCalcConst.c @@ -326,6 +326,7 @@ static int32_t calcConstProjections(SCalcConstContext* pCxt, SSelectStmt* pSelec static int32_t calcConstGroupBy(SCalcConstContext* pCxt, SSelectStmt* pSelect) { int32_t code = calcConstList(pSelect->pGroupByList); +#if 0 if (TSDB_CODE_SUCCESS == code) { SNode* pNode = NULL; FOREACH(pNode, pSelect->pGroupByList) { @@ -338,6 +339,7 @@ static int32_t calcConstGroupBy(SCalcConstContext* pCxt, SSelectStmt* pSelect) { } NODES_DESTORY_LIST(pSelect->pGroupByList); } +#endif return code; } diff --git a/tests/system-test/2-query/group_partition.py b/tests/system-test/2-query/group_partition.py index d48df16231..b77cf3f8b7 100644 --- a/tests/system-test/2-query/group_partition.py +++ b/tests/system-test/2-query/group_partition.py @@ -422,19 +422,19 @@ class TDTestCase: def test_TS5567(self): tdSql.query(f"select const_col from (select 1 as const_col from {self.dbname}.{self.stable}) t group by const_col") - tdSql.checkRows(50) + tdSql.checkRows(1) tdSql.query(f"select const_col from (select 1 as const_col from {self.dbname}.{self.stable}) t partition by const_col") tdSql.checkRows(50) tdSql.query(f"select const_col from (select 1 as const_col, count(c1) from {self.dbname}.{self.stable} t group by c1) group by const_col") - tdSql.checkRows(10) + tdSql.checkRows(1) tdSql.query(f"select const_col from (select 1 as const_col, count(c1) from {self.dbname}.{self.stable} t group by c1) partition by const_col") tdSql.checkRows(10) tdSql.query(f"select const_col as c_c from (select 1 as const_col from {self.dbname}.{self.stable}) t group by c_c") - tdSql.checkRows(50) + tdSql.checkRows(1) tdSql.query(f"select const_col as c_c from (select 1 as const_col from {self.dbname}.{self.stable}) t partition by c_c") tdSql.checkRows(50) tdSql.query(f"select const_col from (select 1 as const_col, count(c1) from {self.dbname}.{self.stable} t group by c1) group by 1") - tdSql.checkRows(10) + tdSql.checkRows(1) tdSql.query(f"select const_col from (select 1 as const_col, count(c1) from {self.dbname}.{self.stable} t group by c1) partition by 1") tdSql.checkRows(10) From 55239964a979cd44f5ca965b4aa92a9e72b81a0d Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Sat, 16 Nov 2024 01:56:33 +0800 Subject: [PATCH 27/88] refactor: injection error. --- source/libs/stream/src/streamCheckpoint.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/libs/stream/src/streamCheckpoint.c b/source/libs/stream/src/streamCheckpoint.c index 6c4c285ded..2280b7f06f 100644 --- a/source/libs/stream/src/streamCheckpoint.c +++ b/source/libs/stream/src/streamCheckpoint.c @@ -362,6 +362,10 @@ int32_t streamProcessCheckpointTriggerBlock(SStreamTask* pTask, SStreamDataBlock } } +#if 0 + taosMsleep(20*1000); +#endif + if (taskLevel == TASK_LEVEL__SOURCE) { int8_t type = pTask->outputInfo.type; pActiveInfo->allUpstreamTriggerRecv = 1; From b589f9089c3956ddb354ee5666036b6bc06b6112 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Mon, 18 Nov 2024 09:33:43 +0800 Subject: [PATCH 28/88] add test case --- source/libs/executor/src/scanoperator.c | 1 - source/libs/parser/src/parTranslater.c | 2 ++ tests/system-test/2-query/group_partition.py | 9 ++++++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/source/libs/executor/src/scanoperator.c b/source/libs/executor/src/scanoperator.c index ecb6bd7aee..eac4f87b14 100644 --- a/source/libs/executor/src/scanoperator.c +++ b/source/libs/executor/src/scanoperator.c @@ -3043,7 +3043,6 @@ static int32_t setBlockIntoRes(SStreamScanInfo* pInfo, const SSDataBlock* pBlock } if (code) { - blockDataFreeRes((SSDataBlock*)pBlock); QUERY_CHECK_CODE(code, lino, _end); } diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index b77c349a78..9bea3491c3 100755 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -5531,6 +5531,7 @@ static int32_t translateGroupByList(STranslateContext* pCxt, SSelectStmt* pSelec SReplaceGroupByAliasCxt cxt = { .pTranslateCxt = pCxt, .pProjectionList = pSelect->pProjectionList}; nodesRewriteExprsPostOrder(pSelect->pGroupByList, translateGroupPartitionByImpl, &cxt); + return pCxt->errCode; } @@ -5542,6 +5543,7 @@ static int32_t translatePartitionByList(STranslateContext* pCxt, SSelectStmt* pS SReplaceGroupByAliasCxt cxt = { .pTranslateCxt = pCxt, .pProjectionList = pSelect->pProjectionList}; nodesRewriteExprsPostOrder(pSelect->pPartitionByList, translateGroupPartitionByImpl, &cxt); + return pCxt->errCode; } diff --git a/tests/system-test/2-query/group_partition.py b/tests/system-test/2-query/group_partition.py index b77cf3f8b7..74f5e86267 100644 --- a/tests/system-test/2-query/group_partition.py +++ b/tests/system-test/2-query/group_partition.py @@ -439,13 +439,16 @@ class TDTestCase: tdSql.checkRows(10) def test_TD_32883(self): - sql = "select avg(c1), t9 from stb group by t9,t9, tbname" + sql = "select avg(c1), t9 from db.stb group by t9,t9, tbname" tdSql.query(sql, queryTimes=1) tdSql.checkRows(5) - sql = "select avg(c1), t10 from stb group by t10,t10, tbname" + sql = "select avg(c1), t10 from db.stb group by t10,t10, tbname" tdSql.query(sql, queryTimes=1) tdSql.checkRows(5) - sql = "select avg(c1), t10 from stb partition by t10,t10, tbname" + sql = "select avg(c1), t10 from db.stb partition by t10,t10, tbname" + tdSql.query(sql, queryTimes=1) + tdSql.checkRows(5) + sql = "select avg(c1), concat(t9,t10) from db.stb group by concat(t9,t10), concat(t9,t10),tbname" tdSql.query(sql, queryTimes=1) tdSql.checkRows(5) From 6d8a5b892ee5abe10b94b9d10a05d95babcd200a Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Mon, 18 Nov 2024 09:37:20 +0800 Subject: [PATCH 29/88] rename doc 28-index.md ->28-tsma.md --- docs/zh/14-reference/03-taos-sql/{28-index.md => 28-tsma.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/zh/14-reference/03-taos-sql/{28-index.md => 28-tsma.md} (100%) diff --git a/docs/zh/14-reference/03-taos-sql/28-index.md b/docs/zh/14-reference/03-taos-sql/28-tsma.md similarity index 100% rename from docs/zh/14-reference/03-taos-sql/28-index.md rename to docs/zh/14-reference/03-taos-sql/28-tsma.md From c6657653c68e5049f64247d38acfeafccb7c97a6 Mon Sep 17 00:00:00 2001 From: Yu Chen <74105241+yu285@users.noreply.github.com> Date: Mon, 18 Nov 2024 09:42:15 +0800 Subject: [PATCH 30/88] docs/Update 03-stream.md --- docs/zh/06-advanced/03-stream.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/zh/06-advanced/03-stream.md b/docs/zh/06-advanced/03-stream.md index c47831dde3..c26924561c 100644 --- a/docs/zh/06-advanced/03-stream.md +++ b/docs/zh/06-advanced/03-stream.md @@ -124,7 +124,7 @@ create stream if not exists count_history_s fill_history 1 into count_history as 窗口关闭是由事件时间决定的,如事件流中断、或持续延迟,此时事件时间无法更新,可能导致无法得到最新的计算结果。 -因此,流计算提供了以事件时间结合处理时间计算的 MAX_DELAY 触发模式。MAX_DELAY 模式在窗口关闭时会立即触发计算。此外,当数据写入后,计算触发的时间超过 max delay 指定的时间,则立即触发计算。 +因此,流计算提供了以事件时间结合处理时间计算的 MAX_DELAY 触发模式:MAX_DELAY 模式在窗口关闭时会立即触发计算,它的单位可以自行指定,具体单位:a(毫秒)、s(秒)、m(分)、h(小时)、d(天)、w(周)。此外,当数据写入后,计算触发的时间超过 MAX_DELAY 指定的时间,则立即触发计算。 ### 流计算的窗口关闭 @@ -259,4 +259,4 @@ flush database test1; 5.修改 taos.cfg,去掉 disableStream 1,或将 disableStream 改为 0 -6.启动 taosd \ No newline at end of file +6.启动 taosd From c954d0ac7addd1e8f395c8a0d9f006c97a0e2b59 Mon Sep 17 00:00:00 2001 From: xiao-77 Date: Mon, 18 Nov 2024 11:20:32 +0800 Subject: [PATCH 31/88] fix:skip begin&end snapshot while wal level = 0. --- source/libs/wal/src/walWrite.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/libs/wal/src/walWrite.c b/source/libs/wal/src/walWrite.c index 1a9652b3bb..66ead2fd26 100644 --- a/source/libs/wal/src/walWrite.c +++ b/source/libs/wal/src/walWrite.c @@ -376,6 +376,10 @@ static FORCE_INLINE int32_t walCheckAndRoll(SWal *pWal) { int32_t walBeginSnapshot(SWal *pWal, int64_t ver, int64_t logRetention) { int32_t code = 0; + if (pWal->cfg.level == TAOS_WAL_SKIP) { + TAOS_RETURN(TSDB_CODE_SUCCESS); + } + if (logRetention < 0) { TAOS_RETURN(TSDB_CODE_FAILED); } @@ -404,6 +408,10 @@ _exit: int32_t walEndSnapshot(SWal *pWal) { int32_t code = 0, lino = 0; + if (pWal->cfg.level == TAOS_WAL_SKIP) { + TAOS_RETURN(TSDB_CODE_SUCCESS); + } + TAOS_UNUSED(taosThreadRwlockWrlock(&pWal->mutex)); int64_t ver = pWal->vers.verInSnapshotting; From 7348cbe5ef1394e10fc0784c8570582d06d2367b Mon Sep 17 00:00:00 2001 From: dmchen Date: Mon, 18 Nov 2024 12:31:38 +0800 Subject: [PATCH 32/88] fix/TD-32914-free-vnode-after-lock --- source/dnode/mgmt/mgmt_vnode/src/vmInt.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source/dnode/mgmt/mgmt_vnode/src/vmInt.c b/source/dnode/mgmt/mgmt_vnode/src/vmInt.c index 2ee607949d..17a99c1806 100644 --- a/source/dnode/mgmt/mgmt_vnode/src/vmInt.c +++ b/source/dnode/mgmt/mgmt_vnode/src/vmInt.c @@ -578,16 +578,13 @@ static int32_t vmOpenVnodes(SVnodeMgmt *pMgmt) { } void vmRemoveFromCreatingHash(SVnodeMgmt *pMgmt, int32_t vgId) { - (void)taosThreadRwlockWrlock(&pMgmt->lock); SVnodeObj *pOld = NULL; + + (void)taosThreadRwlockWrlock(&pMgmt->lock); int32_t r = taosHashGetDup(pMgmt->creatingHash, &vgId, sizeof(int32_t), (void *)&pOld); if (r != 0) { dError("vgId:%d, failed to get vnode from creating Hash", vgId); } - if (pOld) { - dTrace("vgId:%d, free vnode pOld:%p", vgId, &pOld); - vmFreeVnodeObj(&pOld); - } dTrace("vgId:%d, remove from creating Hash", vgId); r = taosHashRemove(pMgmt->creatingHash, &vgId, sizeof(int32_t)); if (r != 0) { @@ -595,6 +592,11 @@ void vmRemoveFromCreatingHash(SVnodeMgmt *pMgmt, int32_t vgId) { } (void)taosThreadRwlockUnlock(&pMgmt->lock); + if (pOld) { + dTrace("vgId:%d, free vnode pOld:%p", vgId, &pOld); + vmFreeVnodeObj(&pOld); + } + _OVER: if (r != 0) { dError("vgId:%d, failed to remove vnode from creatingHash since %s", vgId, tstrerror(r)); From f0c182988cd23c3dc8561559a351b553e0fd6024 Mon Sep 17 00:00:00 2001 From: xiao-77 Date: Mon, 18 Nov 2024 13:49:03 +0800 Subject: [PATCH 33/88] Add test case for begin&end snapshot while wal_level =0 . --- source/libs/wal/test/walMetaTest.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source/libs/wal/test/walMetaTest.cpp b/source/libs/wal/test/walMetaTest.cpp index 3e6fab116f..a958ad74e0 100644 --- a/source/libs/wal/test/walMetaTest.cpp +++ b/source/libs/wal/test/walMetaTest.cpp @@ -510,4 +510,27 @@ TEST_F(WalSkipLevel, restart) { TearDown(); SetUp(); +} + +TEST_F(WalSkipLevel, roll) { + int code; + int i; + for (i = 0; i < 100; i++) { + code = walAppendLog(pWal, i, 0, syncMeta, (void*)ranStr, ranStrLen); + ASSERT_EQ(code, 0); + code = walCommit(pWal, i); + } + walBeginSnapshot(pWal, i - 1, 0); + walEndSnapshot(pWal); + code = walAppendLog(pWal, 5, 0, syncMeta, (void*)ranStr, ranStrLen); + ASSERT_NE(code, 0); + for (; i < 200; i++) { + code = walAppendLog(pWal, i, 0, syncMeta, (void*)ranStr, ranStrLen); + ASSERT_EQ(code, 0); + code = walCommit(pWal, i); + } + code = walBeginSnapshot(pWal, i - 1, 0); + ASSERT_EQ(code, 0); + code = walEndSnapshot(pWal); + ASSERT_EQ(code, 0); } \ No newline at end of file From 6c18289de0f0a85e48f37abba6f3c6bebe660f10 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Wed, 13 Nov 2024 17:01:50 +0800 Subject: [PATCH 34/88] fix tsbs perf issue --- include/libs/catalog/catalog.h | 1 + include/util/tbuffer.inc | 18 +++++- source/dnode/vnode/src/tsdb/tsdbDataFileRW.c | 2 +- source/dnode/vnode/src/tsdb/tsdbRead2.c | 11 +++- source/libs/catalog/inc/catalogInt.h | 1 + source/libs/catalog/src/ctgAsync.c | 32 +++++----- source/libs/function/src/builtinsimpl.c | 61 ++++++++++---------- source/libs/parser/inc/parUtil.h | 1 + source/libs/parser/src/parAstParser.c | 3 +- source/libs/parser/src/parUtil.c | 1 + 10 files changed, 80 insertions(+), 51 deletions(-) diff --git a/include/libs/catalog/catalog.h b/include/libs/catalog/catalog.h index df3f87973f..7c6f02513e 100644 --- a/include/libs/catalog/catalog.h +++ b/include/libs/catalog/catalog.h @@ -102,6 +102,7 @@ typedef struct SCatalogReq { bool svrVerRequired; bool forceUpdate; bool cloned; + bool forceFetchViewMeta; } SCatalogReq; typedef struct SMetaRes { diff --git a/include/util/tbuffer.inc b/include/util/tbuffer.inc index 39090fb7fa..633517ca58 100644 --- a/include/util/tbuffer.inc +++ b/include/util/tbuffer.inc @@ -186,11 +186,25 @@ static int32_t tBufferGetI16(SBufferReader *reader, int16_t *value) { } static int32_t tBufferGetI32(SBufferReader *reader, int32_t *value) { - return tBufferGet(reader, sizeof(*value), value); + if (reader->offset + sizeof(int32_t) > reader->buffer->size) { + return TSDB_CODE_OUT_OF_RANGE; + } + if (value) { + *value = *(int32_t*)BR_PTR(reader); + } + reader->offset += sizeof(int32_t); + return 0; } static int32_t tBufferGetI64(SBufferReader *reader, int64_t *value) { - return tBufferGet(reader, sizeof(*value), value); + if (reader->offset + sizeof(int64_t) > reader->buffer->size) { + return TSDB_CODE_OUT_OF_RANGE; + } + if (value) { + *value = *(int64_t*)BR_PTR(reader); + } + reader->offset += sizeof(int64_t); + return 0; } static int32_t tBufferGetU8(SBufferReader *reader, uint8_t *value) { return tBufferGet(reader, sizeof(*value), value); } diff --git a/source/dnode/vnode/src/tsdb/tsdbDataFileRW.c b/source/dnode/vnode/src/tsdb/tsdbDataFileRW.c index 720ba68414..f51ffe0c83 100644 --- a/source/dnode/vnode/src/tsdb/tsdbDataFileRW.c +++ b/source/dnode/vnode/src/tsdb/tsdbDataFileRW.c @@ -972,7 +972,7 @@ static int32_t tsdbDataFileWriteBrinRecord(SDataFileWriter *writer, const SBrinR break; } - if ((writer->brinBlock->numOfRecords) >= writer->config->maxRow) { + if ((writer->brinBlock->numOfRecords) >= 256) { TAOS_CHECK_GOTO(tsdbDataFileWriteBrinBlock(writer), &lino, _exit); } diff --git a/source/dnode/vnode/src/tsdb/tsdbRead2.c b/source/dnode/vnode/src/tsdb/tsdbRead2.c index f30f7eb310..2b1be747dd 100644 --- a/source/dnode/vnode/src/tsdb/tsdbRead2.c +++ b/source/dnode/vnode/src/tsdb/tsdbRead2.c @@ -836,6 +836,7 @@ static int32_t doLoadBlockIndex(STsdbReader* pReader, SDataFileReader* pFileRead pList = &pReader->status.uidList; int32_t i = 0; + int32_t j = 0; while (i < TARRAY2_SIZE(pBlkArray)) { pBrinBlk = &pBlkArray->data[i]; if (pBrinBlk->maxTbid.suid < pReader->info.suid) { @@ -851,7 +852,7 @@ static int32_t doLoadBlockIndex(STsdbReader* pReader, SDataFileReader* pFileRead (pBrinBlk->minTbid.suid <= pReader->info.suid) && (pBrinBlk->maxTbid.suid >= pReader->info.suid), code, lino, _end, TSDB_CODE_INTERNAL_ERROR); - if (pBrinBlk->maxTbid.suid == pReader->info.suid && pBrinBlk->maxTbid.uid < pList->tableUidList[0]) { + if (pBrinBlk->maxTbid.suid == pReader->info.suid && pBrinBlk->maxTbid.uid < pList->tableUidList[j]) { i += 1; continue; } @@ -864,6 +865,14 @@ static int32_t doLoadBlockIndex(STsdbReader* pReader, SDataFileReader* pFileRead TSDB_CHECK_NULL(p1, code, lino, _end, terrno); i += 1; + if (pBrinBlk->maxTbid.suid == pReader->info.suid) { + while (j < numOfTables && pList->tableUidList[j] < pBrinBlk->maxTbid.uid) { + j++; + } + if (j >= numOfTables) { + break; + } + } } et2 = taosGetTimestampUs(); diff --git a/source/libs/catalog/inc/catalogInt.h b/source/libs/catalog/inc/catalogInt.h index e757163ba8..bfc5ca9142 100644 --- a/source/libs/catalog/inc/catalogInt.h +++ b/source/libs/catalog/inc/catalogInt.h @@ -271,6 +271,7 @@ typedef struct SCtgViewsCtx { SArray* pNames; SArray* pResList; SArray* pFetchs; + bool forceFetch; } SCtgViewsCtx; typedef enum { diff --git a/source/libs/catalog/src/ctgAsync.c b/source/libs/catalog/src/ctgAsync.c index c1dcdf2741..9bfb4102aa 100644 --- a/source/libs/catalog/src/ctgAsync.c +++ b/source/libs/catalog/src/ctgAsync.c @@ -20,6 +20,11 @@ #include "tref.h" #include "trpc.h" +typedef struct SCtgViewTaskParam { + bool forceFetch; + SArray* pTableReqs; +} SCtgViewTaskParam; + void ctgIsTaskDone(SCtgJob* pJob, CTG_TASK_TYPE type, bool* done) { SCtgTask* pTask = NULL; @@ -500,7 +505,7 @@ int32_t ctgInitGetTbTagTask(SCtgJob* pJob, int32_t taskIdx, void* param) { int32_t ctgInitGetViewsTask(SCtgJob* pJob, int32_t taskIdx, void* param) { SCtgTask task = {0}; - + SCtgViewTaskParam* p = param; task.type = CTG_TASK_GET_VIEW; task.taskId = taskIdx; task.pJob = pJob; @@ -511,7 +516,8 @@ int32_t ctgInitGetViewsTask(SCtgJob* pJob, int32_t taskIdx, void* param) { } SCtgViewsCtx* ctx = task.taskCtx; - ctx->pNames = param; + ctx->pNames = p->pTableReqs; + ctx->forceFetch = p->forceFetch; ctx->pResList = taosArrayInit(pJob->viewNum, sizeof(SMetaRes)); if (NULL == ctx->pResList) { qError("QID:0x%" PRIx64 " taosArrayInit %d SMetaRes %d failed", pJob->queryId, pJob->viewNum, @@ -849,13 +855,12 @@ int32_t ctgInitJob(SCatalog* pCtg, SRequestConnInfo* pConn, SCtgJob** job, const int32_t tbCfgNum = (int32_t)taosArrayGetSize(pReq->pTableCfg); int32_t tbTagNum = (int32_t)taosArrayGetSize(pReq->pTableTag); int32_t viewNum = (int32_t)ctgGetTablesReqNum(pReq->pView); - int32_t tbTsmaNum = (int32_t)taosArrayGetSize(pReq->pTableTSMAs); + int32_t tbTsmaNum = tsQuerySmaOptimize ? (int32_t)taosArrayGetSize(pReq->pTableTSMAs) : 0; int32_t tsmaNum = (int32_t)taosArrayGetSize(pReq->pTSMAs); int32_t tbNameNum = (int32_t)ctgGetTablesReqNum(pReq->pTableName); int32_t taskNum = tbMetaNum + dbVgNum + udfNum + tbHashNum + qnodeNum + dnodeNum + svrVerNum + dbCfgNum + indexNum + userNum + dbInfoNum + tbIndexNum + tbCfgNum + tbTagNum + viewNum + tbTsmaNum + tbNameNum; - *job = taosMemoryCalloc(1, sizeof(SCtgJob)); if (NULL == *job) { ctgError("failed to calloc, size:%d,QID:0x%" PRIx64, (int32_t)sizeof(SCtgJob), pConn->requestId); @@ -1014,7 +1019,8 @@ int32_t ctgInitJob(SCatalog* pCtg, SRequestConnInfo* pConn, SCtgJob** job, const } if (viewNum > 0) { - CTG_ERR_JRET(ctgInitTask(pJob, CTG_TASK_GET_VIEW, pReq->pView, NULL)); + SCtgViewTaskParam param = {.forceFetch = pReq->forceFetchViewMeta, .pTableReqs = pReq->pView}; + CTG_ERR_JRET(ctgInitTask(pJob, CTG_TASK_GET_VIEW, ¶m, NULL)); } if (tbTsmaNum > 0) { CTG_ERR_JRET(ctgInitTask(pJob, CTG_TASK_GET_TB_TSMA, pReq->pTableTSMAs, NULL)); @@ -3712,16 +3718,14 @@ int32_t ctgLaunchGetViewsTask(SCtgTask* pTask) { bool tbMetaDone = false; SName* pName = NULL; - /* - ctgIsTaskDone(pJob, CTG_TASK_GET_TB_META_BATCH, &tbMetaDone); - if (tbMetaDone) { - CTG_ERR_RET(ctgBuildViewNullRes(pTask, pCtx)); - TSWAP(pTask->res, pCtx->pResList); + ctgIsTaskDone(pJob, CTG_TASK_GET_TB_META_BATCH, &tbMetaDone); + if (tbMetaDone && !pCtx->forceFetch) { + CTG_ERR_RET(ctgBuildViewNullRes(pTask, pCtx)); + TSWAP(pTask->res, pCtx->pResList); - CTG_ERR_RET(ctgHandleTaskEnd(pTask, 0)); - return TSDB_CODE_SUCCESS; - } - */ + CTG_ERR_RET(ctgHandleTaskEnd(pTask, 0)); + return TSDB_CODE_SUCCESS; + } int32_t dbNum = taosArrayGetSize(pCtx->pNames); int32_t fetchIdx = 0; diff --git a/source/libs/function/src/builtinsimpl.c b/source/libs/function/src/builtinsimpl.c index c2e2e9c17c..1b755e0f38 100644 --- a/source/libs/function/src/builtinsimpl.c +++ b/source/libs/function/src/builtinsimpl.c @@ -3037,61 +3037,58 @@ int32_t lastRowFunction(SqlFunctionCtx* pCtx) { TSKEY startKey = getRowPTs(pInput->pPTS, 0); TSKEY endKey = getRowPTs(pInput->pPTS, pInput->totalRows - 1); -#if 0 - int32_t blockDataOrder = (startKey <= endKey) ? TSDB_ORDER_ASC : TSDB_ORDER_DESC; - - // the optimized version only valid if all tuples in one block are monotonious increasing or descreasing. - // this assumption is NOT always works if project operator exists in downstream. - if (blockDataOrder == TSDB_ORDER_ASC) { + if (pCtx->order == TSDB_ORDER_ASC && !pCtx->hasPrimaryKey) { for (int32_t i = pInput->numOfRows + pInput->startRowIndex - 1; i >= pInput->startRowIndex; --i) { char* data = colDataGetData(pInputCol, i); TSKEY cts = getRowPTs(pInput->pPTS, i); numOfElems++; if (pResInfo->numOfRes == 0 || pInfo->ts < cts) { - doSaveLastrow(pCtx, data, i, cts, pInfo); + int32_t code = doSaveLastrow(pCtx, data, i, cts, pInfo); + if (code != TSDB_CODE_SUCCESS) return code; } break; } - } else { // descending order + } else if (!pCtx->hasPrimaryKey && pCtx->order == TSDB_ORDER_DESC) { + // the optimized version only valid if all tuples in one block are monotonious increasing or descreasing. + // this assumption is NOT always works if project operator exists in downstream. for (int32_t i = pInput->startRowIndex; i < pInput->numOfRows + pInput->startRowIndex; ++i) { char* data = colDataGetData(pInputCol, i); TSKEY cts = getRowPTs(pInput->pPTS, i); numOfElems++; if (pResInfo->numOfRes == 0 || pInfo->ts < cts) { - doSaveLastrow(pCtx, data, i, cts, pInfo); + int32_t code = doSaveLastrow(pCtx, data, i, cts, pInfo); + if (code != TSDB_CODE_SUCCESS) return code; } break; } - } -#else + } else { + int64_t* pts = (int64_t*)pInput->pPTS->pData; + int from = -1; + int32_t i = -1; + while (funcInputGetNextRowIndex(pInput, from, false, &i, &from)) { + bool isNull = colDataIsNull(pInputCol, pInput->numOfRows, i, NULL); + char* data = isNull ? NULL : colDataGetData(pInputCol, i); + TSKEY cts = pts[i]; - int64_t* pts = (int64_t*)pInput->pPTS->pData; - int from = -1; - int32_t i = -1; - while (funcInputGetNextRowIndex(pInput, from, false, &i, &from)) { - bool isNull = colDataIsNull(pInputCol, pInput->numOfRows, i, NULL); - char* data = isNull ? NULL : colDataGetData(pInputCol, i); - TSKEY cts = pts[i]; - - numOfElems++; - char* pkData = NULL; - if (pCtx->hasPrimaryKey) { - pkData = colDataGetData(pkCol, i); - } - if (pResInfo->numOfRes == 0 || pInfo->ts < cts || - (pInfo->ts == pts[i] && pkCompareFn && pkCompareFn(pkData, pInfo->pkData) < 0)) { - int32_t code = doSaveLastrow(pCtx, data, i, cts, pInfo); - if (code != TSDB_CODE_SUCCESS) { - return code; + numOfElems++; + char* pkData = NULL; + if (pCtx->hasPrimaryKey) { + pkData = colDataGetData(pkCol, i); + } + if (pResInfo->numOfRes == 0 || pInfo->ts < cts || + (pInfo->ts == pts[i] && pkCompareFn && pkCompareFn(pkData, pInfo->pkData) < 0)) { + int32_t code = doSaveLastrow(pCtx, data, i, cts, pInfo); + if (code != TSDB_CODE_SUCCESS) { + return code; + } + pResInfo->numOfRes = 1; } - pResInfo->numOfRes = 1; } - } -#endif + } SET_VAL(pResInfo, numOfElems, 1); return TSDB_CODE_SUCCESS; diff --git a/source/libs/parser/inc/parUtil.h b/source/libs/parser/inc/parUtil.h index 857c7604a9..7298b04eb0 100644 --- a/source/libs/parser/inc/parUtil.h +++ b/source/libs/parser/inc/parUtil.h @@ -115,6 +115,7 @@ typedef struct SParseMetaCache { SHashObj* pTableName; // key is tbFUid, elements is STableMeta*(append with tbName) SArray* pDnodes; // element is SEpSet bool dnodeRequired; + bool forceFetchViewMeta; } SParseMetaCache; int32_t generateSyntaxErrMsg(SMsgBuf* pBuf, int32_t errCode, ...); diff --git a/source/libs/parser/src/parAstParser.c b/source/libs/parser/src/parAstParser.c index eecc04658b..b78e10768f 100644 --- a/source/libs/parser/src/parAstParser.c +++ b/source/libs/parser/src/parAstParser.c @@ -810,7 +810,7 @@ static int32_t collectMetaKeyFromShowCreateView(SCollectMetaKeyCxt* pCxt, SShowC if (TSDB_CODE_SUCCESS == code) { code = reserveTableMetaInCache(pCxt->pParseCxt->acctId, pStmt->dbName, pStmt->viewName, pCxt->pMetaCache); } - + pCxt->pMetaCache->forceFetchViewMeta = true; return code; } @@ -888,6 +888,7 @@ static int32_t collectMetaKeyFromCreateViewStmt(SCollectMetaKeyCxt* pCxt, SCreat static int32_t collectMetaKeyFromDropViewStmt(SCollectMetaKeyCxt* pCxt, SDropViewStmt* pStmt) { int32_t code = reserveViewUserAuthInCache(pCxt->pParseCxt->acctId, pCxt->pParseCxt->pUser, pStmt->dbName, pStmt->viewName, AUTH_TYPE_ALTER, pCxt->pMetaCache); + pCxt->pMetaCache->forceFetchViewMeta = true; return code; } diff --git a/source/libs/parser/src/parUtil.c b/source/libs/parser/src/parUtil.c index e35eea9e72..44e44982a3 100644 --- a/source/libs/parser/src/parUtil.c +++ b/source/libs/parser/src/parUtil.c @@ -817,6 +817,7 @@ int32_t buildCatalogReq(const SParseMetaCache* pMetaCache, SCatalogReq* pCatalog } #endif pCatalogReq->dNodeRequired = pMetaCache->dnodeRequired; + pCatalogReq->forceFetchViewMeta = pMetaCache->forceFetchViewMeta; return code; } From a6ab76b02e97186f91226d958fdcacea1a8c2856 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Thu, 14 Nov 2024 10:50:27 +0800 Subject: [PATCH 35/88] fix lastrow function tests --- source/libs/function/src/builtinsimpl.c | 6 ++++-- source/libs/planner/src/planOptimizer.c | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/source/libs/function/src/builtinsimpl.c b/source/libs/function/src/builtinsimpl.c index 1b755e0f38..83227dea9e 100644 --- a/source/libs/function/src/builtinsimpl.c +++ b/source/libs/function/src/builtinsimpl.c @@ -3039,7 +3039,8 @@ int32_t lastRowFunction(SqlFunctionCtx* pCtx) { if (pCtx->order == TSDB_ORDER_ASC && !pCtx->hasPrimaryKey) { for (int32_t i = pInput->numOfRows + pInput->startRowIndex - 1; i >= pInput->startRowIndex; --i) { - char* data = colDataGetData(pInputCol, i); + bool isNull = colDataIsNull(pInputCol, pInput->numOfRows, i, NULL); + char* data = isNull ? NULL : colDataGetData(pInputCol, i); TSKEY cts = getRowPTs(pInput->pPTS, i); numOfElems++; @@ -3054,7 +3055,8 @@ int32_t lastRowFunction(SqlFunctionCtx* pCtx) { // the optimized version only valid if all tuples in one block are monotonious increasing or descreasing. // this assumption is NOT always works if project operator exists in downstream. for (int32_t i = pInput->startRowIndex; i < pInput->numOfRows + pInput->startRowIndex; ++i) { - char* data = colDataGetData(pInputCol, i); + bool isNull = colDataIsNull(pInputCol, pInput->numOfRows, i, NULL); + char* data = isNull ? NULL : colDataGetData(pInputCol, i); TSKEY cts = getRowPTs(pInput->pPTS, i); numOfElems++; diff --git a/source/libs/planner/src/planOptimizer.c b/source/libs/planner/src/planOptimizer.c index 39024731ed..a1809ff137 100644 --- a/source/libs/planner/src/planOptimizer.c +++ b/source/libs/planner/src/planOptimizer.c @@ -204,6 +204,7 @@ static void optSetParentOrder(SLogicNode* pNode, EOrder order, SLogicNode* pNode // case QUERY_NODE_LOGIC_PLAN_WINDOW: case QUERY_NODE_LOGIC_PLAN_AGG: case QUERY_NODE_LOGIC_PLAN_SORT: + case QUERY_NODE_LOGIC_PLAN_FILL: if (pNode == pNodeForcePropagate) { pNode->outputTsOrder = order; break; From 2d9b78216b10896f5bdc7137df581f48791fe212 Mon Sep 17 00:00:00 2001 From: WANG Xu Date: Mon, 18 Nov 2024 14:52:21 +0800 Subject: [PATCH 36/88] Create pull_request_template.md --- .github/pull_request_template.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..7d877987ac --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,5 @@ +# Pull Request Checklist + +- [ ] Is the user manual updated? +- [ ] Are the test cases passed and automated? +- [ ] Is there no significant decrease in test coverage? From f5c66cec19d01f74d0595c59b0d9dc0fa1522806 Mon Sep 17 00:00:00 2001 From: Jinqing Kuang Date: Mon, 18 Nov 2024 15:04:40 +0800 Subject: [PATCH 37/88] fix(query)[TD-32861]. fix that WHERE condition not work with INTERP function Previously, a statement with INTERP would force underlying TABLE SCAN operator to scan all data within the RANGE clause, causing the time range constraint in the WHERE condition to be ignored. This fix ensures that the TABLE SCAN respects both the RANGE clause and WHERE condition, improving query accuracy and performance. --- include/common/tcommon.h | 1 + source/dnode/vnode/src/tsdb/tsdbRead2.c | 19 +- source/libs/executor/src/timesliceoperator.c | 47 +- tests/army/query/function/ans/interp.csv | 649 +++++++++++++++++++ tests/army/query/function/in/interp.in | 50 ++ tests/army/query/function/test_interp.py | 11 + 6 files changed, 766 insertions(+), 11 deletions(-) diff --git a/include/common/tcommon.h b/include/common/tcommon.h index ea764e6760..111c9dde3c 100644 --- a/include/common/tcommon.h +++ b/include/common/tcommon.h @@ -250,6 +250,7 @@ typedef struct SQueryTableDataCond { int32_t type; // data block load type: bool skipRollup; STimeWindow twindows; + STimeWindow extTwindows[2]; int64_t startVersion; int64_t endVersion; bool notLoadData; // response the actual data, not only the rows in the attribute of info.row of ssdatablock diff --git a/source/dnode/vnode/src/tsdb/tsdbRead2.c b/source/dnode/vnode/src/tsdb/tsdbRead2.c index f30f7eb310..ae1488f11b 100644 --- a/source/dnode/vnode/src/tsdb/tsdbRead2.c +++ b/source/dnode/vnode/src/tsdb/tsdbRead2.c @@ -1134,7 +1134,12 @@ static int32_t getCurrentBlockInfo(SDataBlockIter* pBlockIter, SFileDataBlockInf *pInfo = NULL; size_t num = TARRAY_SIZE(pBlockIter->blockList); - TSDB_CHECK_CONDITION(num != 0, code, lino, _end, TSDB_CODE_INVALID_PARA); + if (num == 0) { + // Some callers would attempt to call this function. Filter out certain normal cases and return directly to avoid + // generating excessive unnecessary error logs. + TSDB_CHECK_CONDITION(num == pBlockIter->numOfBlocks, code, lino, _end, TSDB_CODE_INVALID_PARA); + return TSDB_CODE_INVALID_PARA; + } *pInfo = taosArrayGet(pBlockIter->blockList, pBlockIter->index); TSDB_CHECK_NULL(*pInfo, code, lino, _end, TSDB_CODE_INVALID_PARA); @@ -5530,12 +5535,10 @@ int32_t tsdbReaderOpen2(void* pVnode, SQueryTableDataCond* pCond, void* pTableLi // update the SQueryTableDataCond to create inner reader int32_t order = pCond->order; if (order == TSDB_ORDER_ASC) { - pCond->twindows.ekey = window.skey - 1; - pCond->twindows.skey = INT64_MIN; + pCond->twindows = pCond->extTwindows[0]; pCond->order = TSDB_ORDER_DESC; } else { - pCond->twindows.skey = window.ekey + 1; - pCond->twindows.ekey = INT64_MAX; + pCond->twindows = pCond->extTwindows[1]; pCond->order = TSDB_ORDER_ASC; } @@ -5544,11 +5547,9 @@ int32_t tsdbReaderOpen2(void* pVnode, SQueryTableDataCond* pCond, void* pTableLi TSDB_CHECK_CODE(code, lino, _end); if (order == TSDB_ORDER_ASC) { - pCond->twindows.skey = window.ekey + 1; - pCond->twindows.ekey = INT64_MAX; + pCond->twindows = pCond->extTwindows[1]; } else { - pCond->twindows.skey = INT64_MIN; - pCond->twindows.ekey = window.ekey - 1; + pCond->twindows = pCond->extTwindows[0]; } pCond->order = order; diff --git a/source/libs/executor/src/timesliceoperator.c b/source/libs/executor/src/timesliceoperator.c index f77aa8f34a..50deba932f 100644 --- a/source/libs/executor/src/timesliceoperator.c +++ b/source/libs/executor/src/timesliceoperator.c @@ -1131,6 +1131,47 @@ static int32_t extractPkColumnFromFuncs(SNodeList* pFuncs, bool* pHasPk, SColumn return TSDB_CODE_SUCCESS; } +/** + * @brief Determine the actual time range for reading data based on the RANGE clause and the WHERE conditions. + * @param[in] cond The range specified by WHERE condition. + * @param[in] range The range specified by RANGE clause. + * @param[out] twindow The range to be read in DESC order, and only one record is needed. + * @param[out] extTwindow The external range to read for only one record, which is used for FILL clause. + * @note `cond` and `twindow` may be the same address. + */ +static int32_t getQueryExtWindow(const STimeWindow* cond, const STimeWindow* range, STimeWindow* twindow, + STimeWindow* extTwindows) { + int32_t code = TSDB_CODE_SUCCESS; + int32_t lino = 0; + STimeWindow tempWindow; + + if (cond->skey > cond->ekey || range->skey > range->ekey) { + *twindow = extTwindows[0] = extTwindows[1] = TSWINDOW_DESC_INITIALIZER; + return code; + } + + if (range->ekey < cond->skey) { + extTwindows[1] = *cond; + *twindow = extTwindows[0] = TSWINDOW_DESC_INITIALIZER; + return code; + } + + if (cond->ekey < range->skey) { + extTwindows[0] = *cond; + *twindow = extTwindows[1] = TSWINDOW_DESC_INITIALIZER; + return code; + } + + // Only scan data in the time range intersecion. + extTwindows[0] = extTwindows[1] = *cond; + twindow->skey = TMAX(cond->skey, range->skey); + twindow->ekey = TMIN(cond->ekey, range->ekey); + extTwindows[0].ekey = twindow->skey - 1; + extTwindows[1].skey = twindow->ekey + 1; + + return code; +} + int32_t createTimeSliceOperatorInfo(SOperatorInfo* downstream, SPhysiNode* pPhyNode, SExecTaskInfo* pTaskInfo, SOperatorInfo** pOptrInfo) { QRY_PARAM_CHECK(pOptrInfo); @@ -1206,8 +1247,10 @@ int32_t createTimeSliceOperatorInfo(SOperatorInfo* downstream, SPhysiNode* pPhyN if (downstream->operatorType == QUERY_NODE_PHYSICAL_PLAN_TABLE_SCAN) { STableScanInfo* pScanInfo = (STableScanInfo*)downstream->info; - pScanInfo->base.cond.twindows = pInfo->win; - pScanInfo->base.cond.type = TIMEWINDOW_RANGE_EXTERNAL; + SQueryTableDataCond *cond = &pScanInfo->base.cond; + cond->type = TIMEWINDOW_RANGE_EXTERNAL; + code = getQueryExtWindow(&cond->twindows, &pInfo->win, &cond->twindows, cond->extTwindows); + QUERY_CHECK_CODE(code, lino, _error); } setOperatorInfo(pOperator, "TimeSliceOperator", QUERY_NODE_PHYSICAL_PLAN_INTERP_FUNC, false, OP_NOT_OPENED, pInfo, diff --git a/tests/army/query/function/ans/interp.csv b/tests/army/query/function/ans/interp.csv index e1ba236aa1..3eaccd887a 100644 --- a/tests/army/query/function/ans/interp.csv +++ b/tests/army/query/function/ans/interp.csv @@ -366,3 +366,652 @@ taos> select _irowts as irowts ,tbname as table_name, c2 as c_c2, c3 as c_c3, _i 2020-02-01 00:00:16.000 | td32727 | 10 | 10 | true | 1 | 2020-02-01 00:00:16.000 | td32727 | 15 | 15 | true | 1 | +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-02 00:00:00' and '2020-01-01 00:00:00' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(null); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-02 00:00:00' and '2020-01-01 00:00:00' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(value, 1); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-02 00:00:00' and '2020-01-01 00:00:00' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(prev); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-02 00:00:00' and '2020-01-01 00:00:00' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(next); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-02 00:00:00' and '2020-01-01 00:00:00' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(linear); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-02 00:00:00' range('2020-01-01 00:00:30', '2020-01-01 00:00:00') every(1s) fill(null); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-02 00:00:00' range('2020-01-01 00:00:30', '2020-01-01 00:00:00') every(1s) fill(value, 1); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-02 00:00:00' range('2020-01-01 00:00:30', '2020-01-01 00:00:00') every(1s) fill(prev); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-02 00:00:00' range('2020-01-01 00:00:30', '2020-01-01 00:00:00') every(1s) fill(next); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-02 00:00:00' range('2020-01-01 00:00:30', '2020-01-01 00:00:00') every(1s) fill(linear); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:20' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(null); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:20' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(value, 1); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:20' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(prev); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:20' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(next); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:20' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(linear); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(null); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:21.000 | false | 21 | + 2020-01-01 00:00:22.000 | true | NULL | + 2020-01-01 00:00:23.000 | true | NULL | + 2020-01-01 00:00:24.000 | true | NULL | + 2020-01-01 00:00:25.000 | true | NULL | + 2020-01-01 00:00:26.000 | true | NULL | + 2020-01-01 00:00:27.000 | true | NULL | + 2020-01-01 00:00:28.000 | true | NULL | + 2020-01-01 00:00:29.000 | true | NULL | + 2020-01-01 00:00:30.000 | true | NULL | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(value, 1); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:21.000 | false | 21 | + 2020-01-01 00:00:22.000 | true | 1 | + 2020-01-01 00:00:23.000 | true | 1 | + 2020-01-01 00:00:24.000 | true | 1 | + 2020-01-01 00:00:25.000 | true | 1 | + 2020-01-01 00:00:26.000 | true | 1 | + 2020-01-01 00:00:27.000 | true | 1 | + 2020-01-01 00:00:28.000 | true | 1 | + 2020-01-01 00:00:29.000 | true | 1 | + 2020-01-01 00:00:30.000 | true | 1 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(prev); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:21.000 | false | 21 | + 2020-01-01 00:00:22.000 | true | 21 | + 2020-01-01 00:00:23.000 | true | 21 | + 2020-01-01 00:00:24.000 | true | 21 | + 2020-01-01 00:00:25.000 | true | 21 | + 2020-01-01 00:00:26.000 | true | 21 | + 2020-01-01 00:00:27.000 | true | 21 | + 2020-01-01 00:00:28.000 | true | 21 | + 2020-01-01 00:00:29.000 | true | 21 | + 2020-01-01 00:00:30.000 | true | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(next); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(linear); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:15', '2020-01-01 00:00:30') every(1s) fill(null); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | NULL | + 2020-01-01 00:00:17.000 | true | NULL | + 2020-01-01 00:00:18.000 | true | NULL | + 2020-01-01 00:00:19.000 | true | NULL | + 2020-01-01 00:00:20.000 | true | NULL | + 2020-01-01 00:00:21.000 | false | 21 | + 2020-01-01 00:00:22.000 | true | NULL | + 2020-01-01 00:00:23.000 | true | NULL | + 2020-01-01 00:00:24.000 | true | NULL | + 2020-01-01 00:00:25.000 | true | NULL | + 2020-01-01 00:00:26.000 | true | NULL | + 2020-01-01 00:00:27.000 | true | NULL | + 2020-01-01 00:00:28.000 | true | NULL | + 2020-01-01 00:00:29.000 | true | NULL | + 2020-01-01 00:00:30.000 | true | NULL | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:15', '2020-01-01 00:00:30') every(1s) fill(value, 1); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 1 | + 2020-01-01 00:00:17.000 | true | 1 | + 2020-01-01 00:00:18.000 | true | 1 | + 2020-01-01 00:00:19.000 | true | 1 | + 2020-01-01 00:00:20.000 | true | 1 | + 2020-01-01 00:00:21.000 | false | 21 | + 2020-01-01 00:00:22.000 | true | 1 | + 2020-01-01 00:00:23.000 | true | 1 | + 2020-01-01 00:00:24.000 | true | 1 | + 2020-01-01 00:00:25.000 | true | 1 | + 2020-01-01 00:00:26.000 | true | 1 | + 2020-01-01 00:00:27.000 | true | 1 | + 2020-01-01 00:00:28.000 | true | 1 | + 2020-01-01 00:00:29.000 | true | 1 | + 2020-01-01 00:00:30.000 | true | 1 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:15', '2020-01-01 00:00:30') every(1s) fill(prev); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 15 | + 2020-01-01 00:00:17.000 | true | 15 | + 2020-01-01 00:00:18.000 | true | 15 | + 2020-01-01 00:00:19.000 | true | 15 | + 2020-01-01 00:00:20.000 | true | 15 | + 2020-01-01 00:00:21.000 | false | 21 | + 2020-01-01 00:00:22.000 | true | 21 | + 2020-01-01 00:00:23.000 | true | 21 | + 2020-01-01 00:00:24.000 | true | 21 | + 2020-01-01 00:00:25.000 | true | 21 | + 2020-01-01 00:00:26.000 | true | 21 | + 2020-01-01 00:00:27.000 | true | 21 | + 2020-01-01 00:00:28.000 | true | 21 | + 2020-01-01 00:00:29.000 | true | 21 | + 2020-01-01 00:00:30.000 | true | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:15', '2020-01-01 00:00:30') every(1s) fill(next); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 21 | + 2020-01-01 00:00:17.000 | true | 21 | + 2020-01-01 00:00:18.000 | true | 21 | + 2020-01-01 00:00:19.000 | true | 21 | + 2020-01-01 00:00:20.000 | true | 21 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:15', '2020-01-01 00:00:30') every(1s) fill(linear); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 16 | + 2020-01-01 00:00:17.000 | true | 17 | + 2020-01-01 00:00:18.000 | true | 18 | + 2020-01-01 00:00:19.000 | true | 19 | + 2020-01-01 00:00:20.000 | true | 20 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(null); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | false | 0 | + 2020-01-01 00:00:01.000 | false | 1 | + 2020-01-01 00:00:02.000 | true | NULL | + 2020-01-01 00:00:03.000 | false | 3 | + 2020-01-01 00:00:04.000 | true | NULL | + 2020-01-01 00:00:05.000 | true | NULL | + 2020-01-01 00:00:06.000 | false | 6 | + 2020-01-01 00:00:07.000 | true | NULL | + 2020-01-01 00:00:08.000 | true | NULL | + 2020-01-01 00:00:09.000 | true | NULL | + 2020-01-01 00:00:10.000 | false | 10 | + 2020-01-01 00:00:11.000 | true | NULL | + 2020-01-01 00:00:12.000 | true | NULL | + 2020-01-01 00:00:13.000 | true | NULL | + 2020-01-01 00:00:14.000 | true | NULL | + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | NULL | + 2020-01-01 00:00:17.000 | true | NULL | + 2020-01-01 00:00:18.000 | true | NULL | + 2020-01-01 00:00:19.000 | true | NULL | + 2020-01-01 00:00:20.000 | true | NULL | + 2020-01-01 00:00:21.000 | false | 21 | + 2020-01-01 00:00:22.000 | true | NULL | + 2020-01-01 00:00:23.000 | true | NULL | + 2020-01-01 00:00:24.000 | true | NULL | + 2020-01-01 00:00:25.000 | true | NULL | + 2020-01-01 00:00:26.000 | true | NULL | + 2020-01-01 00:00:27.000 | true | NULL | + 2020-01-01 00:00:28.000 | true | NULL | + 2020-01-01 00:00:29.000 | true | NULL | + 2020-01-01 00:00:30.000 | true | NULL | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(value, 1); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | false | 0 | + 2020-01-01 00:00:01.000 | false | 1 | + 2020-01-01 00:00:02.000 | true | 1 | + 2020-01-01 00:00:03.000 | false | 3 | + 2020-01-01 00:00:04.000 | true | 1 | + 2020-01-01 00:00:05.000 | true | 1 | + 2020-01-01 00:00:06.000 | false | 6 | + 2020-01-01 00:00:07.000 | true | 1 | + 2020-01-01 00:00:08.000 | true | 1 | + 2020-01-01 00:00:09.000 | true | 1 | + 2020-01-01 00:00:10.000 | false | 10 | + 2020-01-01 00:00:11.000 | true | 1 | + 2020-01-01 00:00:12.000 | true | 1 | + 2020-01-01 00:00:13.000 | true | 1 | + 2020-01-01 00:00:14.000 | true | 1 | + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 1 | + 2020-01-01 00:00:17.000 | true | 1 | + 2020-01-01 00:00:18.000 | true | 1 | + 2020-01-01 00:00:19.000 | true | 1 | + 2020-01-01 00:00:20.000 | true | 1 | + 2020-01-01 00:00:21.000 | false | 21 | + 2020-01-01 00:00:22.000 | true | 1 | + 2020-01-01 00:00:23.000 | true | 1 | + 2020-01-01 00:00:24.000 | true | 1 | + 2020-01-01 00:00:25.000 | true | 1 | + 2020-01-01 00:00:26.000 | true | 1 | + 2020-01-01 00:00:27.000 | true | 1 | + 2020-01-01 00:00:28.000 | true | 1 | + 2020-01-01 00:00:29.000 | true | 1 | + 2020-01-01 00:00:30.000 | true | 1 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(prev); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | false | 0 | + 2020-01-01 00:00:01.000 | false | 1 | + 2020-01-01 00:00:02.000 | true | 1 | + 2020-01-01 00:00:03.000 | false | 3 | + 2020-01-01 00:00:04.000 | true | 3 | + 2020-01-01 00:00:05.000 | true | 3 | + 2020-01-01 00:00:06.000 | false | 6 | + 2020-01-01 00:00:07.000 | true | 6 | + 2020-01-01 00:00:08.000 | true | 6 | + 2020-01-01 00:00:09.000 | true | 6 | + 2020-01-01 00:00:10.000 | false | 10 | + 2020-01-01 00:00:11.000 | true | 10 | + 2020-01-01 00:00:12.000 | true | 10 | + 2020-01-01 00:00:13.000 | true | 10 | + 2020-01-01 00:00:14.000 | true | 10 | + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 15 | + 2020-01-01 00:00:17.000 | true | 15 | + 2020-01-01 00:00:18.000 | true | 15 | + 2020-01-01 00:00:19.000 | true | 15 | + 2020-01-01 00:00:20.000 | true | 15 | + 2020-01-01 00:00:21.000 | false | 21 | + 2020-01-01 00:00:22.000 | true | 21 | + 2020-01-01 00:00:23.000 | true | 21 | + 2020-01-01 00:00:24.000 | true | 21 | + 2020-01-01 00:00:25.000 | true | 21 | + 2020-01-01 00:00:26.000 | true | 21 | + 2020-01-01 00:00:27.000 | true | 21 | + 2020-01-01 00:00:28.000 | true | 21 | + 2020-01-01 00:00:29.000 | true | 21 | + 2020-01-01 00:00:30.000 | true | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(next); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | false | 0 | + 2020-01-01 00:00:01.000 | false | 1 | + 2020-01-01 00:00:02.000 | true | 3 | + 2020-01-01 00:00:03.000 | false | 3 | + 2020-01-01 00:00:04.000 | true | 6 | + 2020-01-01 00:00:05.000 | true | 6 | + 2020-01-01 00:00:06.000 | false | 6 | + 2020-01-01 00:00:07.000 | true | 10 | + 2020-01-01 00:00:08.000 | true | 10 | + 2020-01-01 00:00:09.000 | true | 10 | + 2020-01-01 00:00:10.000 | false | 10 | + 2020-01-01 00:00:11.000 | true | 15 | + 2020-01-01 00:00:12.000 | true | 15 | + 2020-01-01 00:00:13.000 | true | 15 | + 2020-01-01 00:00:14.000 | true | 15 | + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 21 | + 2020-01-01 00:00:17.000 | true | 21 | + 2020-01-01 00:00:18.000 | true | 21 | + 2020-01-01 00:00:19.000 | true | 21 | + 2020-01-01 00:00:20.000 | true | 21 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(linear); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | false | 0 | + 2020-01-01 00:00:01.000 | false | 1 | + 2020-01-01 00:00:02.000 | true | 2 | + 2020-01-01 00:00:03.000 | false | 3 | + 2020-01-01 00:00:04.000 | true | 4 | + 2020-01-01 00:00:05.000 | true | 5 | + 2020-01-01 00:00:06.000 | false | 6 | + 2020-01-01 00:00:07.000 | true | 7 | + 2020-01-01 00:00:08.000 | true | 8 | + 2020-01-01 00:00:09.000 | true | 9 | + 2020-01-01 00:00:10.000 | false | 10 | + 2020-01-01 00:00:11.000 | true | 11 | + 2020-01-01 00:00:12.000 | true | 12 | + 2020-01-01 00:00:13.000 | true | 13 | + 2020-01-01 00:00:14.000 | true | 14 | + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 16 | + 2020-01-01 00:00:17.000 | true | 17 | + 2020-01-01 00:00:18.000 | true | 18 | + 2020-01-01 00:00:19.000 | true | 19 | + 2020-01-01 00:00:20.000 | true | 20 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(null); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | false | 0 | + 2020-01-01 00:00:01.000 | false | 1 | + 2020-01-01 00:00:02.000 | true | NULL | + 2020-01-01 00:00:03.000 | false | 3 | + 2020-01-01 00:00:04.000 | true | NULL | + 2020-01-01 00:00:05.000 | true | NULL | + 2020-01-01 00:00:06.000 | false | 6 | + 2020-01-01 00:00:07.000 | true | NULL | + 2020-01-01 00:00:08.000 | true | NULL | + 2020-01-01 00:00:09.000 | true | NULL | + 2020-01-01 00:00:10.000 | false | 10 | + 2020-01-01 00:00:11.000 | true | NULL | + 2020-01-01 00:00:12.000 | true | NULL | + 2020-01-01 00:00:13.000 | true | NULL | + 2020-01-01 00:00:14.000 | true | NULL | + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | NULL | + 2020-01-01 00:00:17.000 | true | NULL | + 2020-01-01 00:00:18.000 | true | NULL | + 2020-01-01 00:00:19.000 | true | NULL | + 2020-01-01 00:00:20.000 | true | NULL | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(value, 1); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | false | 0 | + 2020-01-01 00:00:01.000 | false | 1 | + 2020-01-01 00:00:02.000 | true | 1 | + 2020-01-01 00:00:03.000 | false | 3 | + 2020-01-01 00:00:04.000 | true | 1 | + 2020-01-01 00:00:05.000 | true | 1 | + 2020-01-01 00:00:06.000 | false | 6 | + 2020-01-01 00:00:07.000 | true | 1 | + 2020-01-01 00:00:08.000 | true | 1 | + 2020-01-01 00:00:09.000 | true | 1 | + 2020-01-01 00:00:10.000 | false | 10 | + 2020-01-01 00:00:11.000 | true | 1 | + 2020-01-01 00:00:12.000 | true | 1 | + 2020-01-01 00:00:13.000 | true | 1 | + 2020-01-01 00:00:14.000 | true | 1 | + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 1 | + 2020-01-01 00:00:17.000 | true | 1 | + 2020-01-01 00:00:18.000 | true | 1 | + 2020-01-01 00:00:19.000 | true | 1 | + 2020-01-01 00:00:20.000 | true | 1 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(prev); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | false | 0 | + 2020-01-01 00:00:01.000 | false | 1 | + 2020-01-01 00:00:02.000 | true | 1 | + 2020-01-01 00:00:03.000 | false | 3 | + 2020-01-01 00:00:04.000 | true | 3 | + 2020-01-01 00:00:05.000 | true | 3 | + 2020-01-01 00:00:06.000 | false | 6 | + 2020-01-01 00:00:07.000 | true | 6 | + 2020-01-01 00:00:08.000 | true | 6 | + 2020-01-01 00:00:09.000 | true | 6 | + 2020-01-01 00:00:10.000 | false | 10 | + 2020-01-01 00:00:11.000 | true | 10 | + 2020-01-01 00:00:12.000 | true | 10 | + 2020-01-01 00:00:13.000 | true | 10 | + 2020-01-01 00:00:14.000 | true | 10 | + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 15 | + 2020-01-01 00:00:17.000 | true | 15 | + 2020-01-01 00:00:18.000 | true | 15 | + 2020-01-01 00:00:19.000 | true | 15 | + 2020-01-01 00:00:20.000 | true | 15 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(next); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | false | 0 | + 2020-01-01 00:00:01.000 | false | 1 | + 2020-01-01 00:00:02.000 | true | 3 | + 2020-01-01 00:00:03.000 | false | 3 | + 2020-01-01 00:00:04.000 | true | 6 | + 2020-01-01 00:00:05.000 | true | 6 | + 2020-01-01 00:00:06.000 | false | 6 | + 2020-01-01 00:00:07.000 | true | 10 | + 2020-01-01 00:00:08.000 | true | 10 | + 2020-01-01 00:00:09.000 | true | 10 | + 2020-01-01 00:00:10.000 | false | 10 | + 2020-01-01 00:00:11.000 | true | 15 | + 2020-01-01 00:00:12.000 | true | 15 | + 2020-01-01 00:00:13.000 | true | 15 | + 2020-01-01 00:00:14.000 | true | 15 | + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 21 | + 2020-01-01 00:00:17.000 | true | 21 | + 2020-01-01 00:00:18.000 | true | 21 | + 2020-01-01 00:00:19.000 | true | 21 | + 2020-01-01 00:00:20.000 | true | 21 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(linear); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | false | 0 | + 2020-01-01 00:00:01.000 | false | 1 | + 2020-01-01 00:00:02.000 | true | 2 | + 2020-01-01 00:00:03.000 | false | 3 | + 2020-01-01 00:00:04.000 | true | 4 | + 2020-01-01 00:00:05.000 | true | 5 | + 2020-01-01 00:00:06.000 | false | 6 | + 2020-01-01 00:00:07.000 | true | 7 | + 2020-01-01 00:00:08.000 | true | 8 | + 2020-01-01 00:00:09.000 | true | 9 | + 2020-01-01 00:00:10.000 | false | 10 | + 2020-01-01 00:00:11.000 | true | 11 | + 2020-01-01 00:00:12.000 | true | 12 | + 2020-01-01 00:00:13.000 | true | 13 | + 2020-01-01 00:00:14.000 | true | 14 | + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 16 | + 2020-01-01 00:00:17.000 | true | 17 | + 2020-01-01 00:00:18.000 | true | 18 | + 2020-01-01 00:00:19.000 | true | 19 | + 2020-01-01 00:00:20.000 | true | 20 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:15' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(null); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | true | NULL | + 2020-01-01 00:00:01.000 | true | NULL | + 2020-01-01 00:00:02.000 | true | NULL | + 2020-01-01 00:00:03.000 | true | NULL | + 2020-01-01 00:00:04.000 | true | NULL | + 2020-01-01 00:00:05.000 | true | NULL | + 2020-01-01 00:00:06.000 | true | NULL | + 2020-01-01 00:00:07.000 | true | NULL | + 2020-01-01 00:00:08.000 | true | NULL | + 2020-01-01 00:00:09.000 | true | NULL | + 2020-01-01 00:00:10.000 | true | NULL | + 2020-01-01 00:00:11.000 | true | NULL | + 2020-01-01 00:00:12.000 | true | NULL | + 2020-01-01 00:00:13.000 | true | NULL | + 2020-01-01 00:00:14.000 | true | NULL | + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | NULL | + 2020-01-01 00:00:17.000 | true | NULL | + 2020-01-01 00:00:18.000 | true | NULL | + 2020-01-01 00:00:19.000 | true | NULL | + 2020-01-01 00:00:20.000 | true | NULL | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:15' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(value, 1); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | true | 1 | + 2020-01-01 00:00:01.000 | true | 1 | + 2020-01-01 00:00:02.000 | true | 1 | + 2020-01-01 00:00:03.000 | true | 1 | + 2020-01-01 00:00:04.000 | true | 1 | + 2020-01-01 00:00:05.000 | true | 1 | + 2020-01-01 00:00:06.000 | true | 1 | + 2020-01-01 00:00:07.000 | true | 1 | + 2020-01-01 00:00:08.000 | true | 1 | + 2020-01-01 00:00:09.000 | true | 1 | + 2020-01-01 00:00:10.000 | true | 1 | + 2020-01-01 00:00:11.000 | true | 1 | + 2020-01-01 00:00:12.000 | true | 1 | + 2020-01-01 00:00:13.000 | true | 1 | + 2020-01-01 00:00:14.000 | true | 1 | + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 1 | + 2020-01-01 00:00:17.000 | true | 1 | + 2020-01-01 00:00:18.000 | true | 1 | + 2020-01-01 00:00:19.000 | true | 1 | + 2020-01-01 00:00:20.000 | true | 1 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:15' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(prev); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 15 | + 2020-01-01 00:00:17.000 | true | 15 | + 2020-01-01 00:00:18.000 | true | 15 | + 2020-01-01 00:00:19.000 | true | 15 | + 2020-01-01 00:00:20.000 | true | 15 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:15' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(next); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | true | 15 | + 2020-01-01 00:00:01.000 | true | 15 | + 2020-01-01 00:00:02.000 | true | 15 | + 2020-01-01 00:00:03.000 | true | 15 | + 2020-01-01 00:00:04.000 | true | 15 | + 2020-01-01 00:00:05.000 | true | 15 | + 2020-01-01 00:00:06.000 | true | 15 | + 2020-01-01 00:00:07.000 | true | 15 | + 2020-01-01 00:00:08.000 | true | 15 | + 2020-01-01 00:00:09.000 | true | 15 | + 2020-01-01 00:00:10.000 | true | 15 | + 2020-01-01 00:00:11.000 | true | 15 | + 2020-01-01 00:00:12.000 | true | 15 | + 2020-01-01 00:00:13.000 | true | 15 | + 2020-01-01 00:00:14.000 | true | 15 | + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 21 | + 2020-01-01 00:00:17.000 | true | 21 | + 2020-01-01 00:00:18.000 | true | 21 | + 2020-01-01 00:00:19.000 | true | 21 | + 2020-01-01 00:00:20.000 | true | 21 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:15' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(linear); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:15.000 | false | 15 | + 2020-01-01 00:00:16.000 | true | 16 | + 2020-01-01 00:00:17.000 | true | 17 | + 2020-01-01 00:00:18.000 | true | 18 | + 2020-01-01 00:00:19.000 | true | 19 | + 2020-01-01 00:00:20.000 | true | 20 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:21' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(null); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | true | NULL | + 2020-01-01 00:00:01.000 | true | NULL | + 2020-01-01 00:00:02.000 | true | NULL | + 2020-01-01 00:00:03.000 | true | NULL | + 2020-01-01 00:00:04.000 | true | NULL | + 2020-01-01 00:00:05.000 | true | NULL | + 2020-01-01 00:00:06.000 | true | NULL | + 2020-01-01 00:00:07.000 | true | NULL | + 2020-01-01 00:00:08.000 | true | NULL | + 2020-01-01 00:00:09.000 | true | NULL | + 2020-01-01 00:00:10.000 | true | NULL | + 2020-01-01 00:00:11.000 | true | NULL | + 2020-01-01 00:00:12.000 | true | NULL | + 2020-01-01 00:00:13.000 | true | NULL | + 2020-01-01 00:00:14.000 | true | NULL | + 2020-01-01 00:00:15.000 | true | NULL | + 2020-01-01 00:00:16.000 | true | NULL | + 2020-01-01 00:00:17.000 | true | NULL | + 2020-01-01 00:00:18.000 | true | NULL | + 2020-01-01 00:00:19.000 | true | NULL | + 2020-01-01 00:00:20.000 | true | NULL | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:21' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(value, 1); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | true | 1 | + 2020-01-01 00:00:01.000 | true | 1 | + 2020-01-01 00:00:02.000 | true | 1 | + 2020-01-01 00:00:03.000 | true | 1 | + 2020-01-01 00:00:04.000 | true | 1 | + 2020-01-01 00:00:05.000 | true | 1 | + 2020-01-01 00:00:06.000 | true | 1 | + 2020-01-01 00:00:07.000 | true | 1 | + 2020-01-01 00:00:08.000 | true | 1 | + 2020-01-01 00:00:09.000 | true | 1 | + 2020-01-01 00:00:10.000 | true | 1 | + 2020-01-01 00:00:11.000 | true | 1 | + 2020-01-01 00:00:12.000 | true | 1 | + 2020-01-01 00:00:13.000 | true | 1 | + 2020-01-01 00:00:14.000 | true | 1 | + 2020-01-01 00:00:15.000 | true | 1 | + 2020-01-01 00:00:16.000 | true | 1 | + 2020-01-01 00:00:17.000 | true | 1 | + 2020-01-01 00:00:18.000 | true | 1 | + 2020-01-01 00:00:19.000 | true | 1 | + 2020-01-01 00:00:20.000 | true | 1 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:21' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(prev); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:21' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(next); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:00.000 | true | 21 | + 2020-01-01 00:00:01.000 | true | 21 | + 2020-01-01 00:00:02.000 | true | 21 | + 2020-01-01 00:00:03.000 | true | 21 | + 2020-01-01 00:00:04.000 | true | 21 | + 2020-01-01 00:00:05.000 | true | 21 | + 2020-01-01 00:00:06.000 | true | 21 | + 2020-01-01 00:00:07.000 | true | 21 | + 2020-01-01 00:00:08.000 | true | 21 | + 2020-01-01 00:00:09.000 | true | 21 | + 2020-01-01 00:00:10.000 | true | 21 | + 2020-01-01 00:00:11.000 | true | 21 | + 2020-01-01 00:00:12.000 | true | 21 | + 2020-01-01 00:00:13.000 | true | 21 | + 2020-01-01 00:00:14.000 | true | 21 | + 2020-01-01 00:00:15.000 | true | 21 | + 2020-01-01 00:00:16.000 | true | 21 | + 2020-01-01 00:00:17.000 | true | 21 | + 2020-01-01 00:00:18.000 | true | 21 | + 2020-01-01 00:00:19.000 | true | 21 | + 2020-01-01 00:00:20.000 | true | 21 | + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:21' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(linear); + _irowts | _isfilled | interp(c1) | +==================================================== + 2020-01-01 00:00:21.000 | false | 21 | + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:22' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(null); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:22' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(value, 1); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:22' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(prev); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:22' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(next); + +taos> select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:22' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(linear); + diff --git a/tests/army/query/function/in/interp.in b/tests/army/query/function/in/interp.in index 4825ab46b1..97a9936b8d 100644 --- a/tests/army/query/function/in/interp.in +++ b/tests/army/query/function/in/interp.in @@ -13,3 +13,53 @@ select _irowts as irowts ,tbname as table_name, c2 as c_c2, c3 as c_c3, _isfille select _irowts as irowts ,tbname as table_name, c2 as c_c2, c3 as c_c3, _isfilled as isfilled , interp(c1) as intp_c1 from test.td32727 partition by tbname,c2,c3 range('2020-02-01 00:00:04', '2020-02-01 00:00:16') every(1s) fill (prev) order by irowts, c2, c3; select _irowts as irowts ,tbname as table_name, c2 as c_c2, c3 as c_c3, _isfilled as isfilled , interp(c1) as intp_c1 from test.td32727 partition by tbname,c2,c3 range('2020-02-01 00:00:04', '2020-02-01 00:00:16') every(1s) fill (linear) order by irowts, c2, c3; select _irowts as irowts ,tbname as table_name, c2 as c_c2, c3 as c_c3, _isfilled as isfilled , interp(c1) as intp_c1 from test.td32727 partition by tbname,c2,c3 range('2020-02-01 00:00:04', '2020-02-01 00:00:16') every(1s) fill (value, 1) order by irowts, c2, c3; +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-02 00:00:00' and '2020-01-01 00:00:00' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(null); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-02 00:00:00' and '2020-01-01 00:00:00' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(value, 1); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-02 00:00:00' and '2020-01-01 00:00:00' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(prev); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-02 00:00:00' and '2020-01-01 00:00:00' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(next); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-02 00:00:00' and '2020-01-01 00:00:00' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(linear); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-02 00:00:00' range('2020-01-01 00:00:30', '2020-01-01 00:00:00') every(1s) fill(null); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-02 00:00:00' range('2020-01-01 00:00:30', '2020-01-01 00:00:00') every(1s) fill(value, 1); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-02 00:00:00' range('2020-01-01 00:00:30', '2020-01-01 00:00:00') every(1s) fill(prev); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-02 00:00:00' range('2020-01-01 00:00:30', '2020-01-01 00:00:00') every(1s) fill(next); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-02 00:00:00' range('2020-01-01 00:00:30', '2020-01-01 00:00:00') every(1s) fill(linear); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:20' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(null); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:20' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(value, 1); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:20' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(prev); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:20' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(next); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:20' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(linear); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(null); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(value, 1); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(prev); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(next); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:21', '2020-01-01 00:00:30') every(1s) fill(linear); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:15', '2020-01-01 00:00:30') every(1s) fill(null); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:15', '2020-01-01 00:00:30') every(1s) fill(value, 1); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:15', '2020-01-01 00:00:30') every(1s) fill(prev); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:15', '2020-01-01 00:00:30') every(1s) fill(next); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:15', '2020-01-01 00:00:30') every(1s) fill(linear); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(null); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(value, 1); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(prev); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(next); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:21' range('2020-01-01 00:00:00', '2020-01-01 00:00:30') every(1s) fill(linear); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(null); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(value, 1); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(prev); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(next); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:00' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(linear); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:15' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(null); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:15' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(value, 1); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:15' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(prev); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:15' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(next); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:15' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(linear); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:21' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(null); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:21' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(value, 1); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:21' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(prev); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:21' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(next); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:21' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(linear); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:22' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(null); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:22' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(value, 1); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:22' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(prev); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:22' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(next); +select _irowts, _isfilled, interp(c1) from test.td32861 where ts between '2020-01-01 00:00:22' and '2020-01-01 00:00:30' range('2020-01-01 00:00:00', '2020-01-01 00:00:21') every(1s) fill(linear); diff --git a/tests/army/query/function/test_interp.py b/tests/army/query/function/test_interp.py index f903e7be73..106ef1e58e 100644 --- a/tests/army/query/function/test_interp.py +++ b/tests/army/query/function/test_interp.py @@ -38,6 +38,7 @@ class TDTestCase(TBase): (ts timestamp, c0 tinyint, c1 smallint, c2 int, c3 bigint, c4 double, c5 float, c6 bool, c7 varchar(10), c8 nchar(10), c9 tinyint unsigned, c10 smallint unsigned, c11 int unsigned, c12 bigint unsigned) ''' ) + tdSql.execute("create table if not exists test.td32861(ts timestamp, c1 int);") tdLog.printNoPrefix("==========step2:insert data") @@ -45,6 +46,16 @@ class TDTestCase(TBase): tdSql.execute(f"insert into test.td32727 values ('2020-02-01 00:00:10', 10, 10, 10, 10, 10.0, 10.0, true, 'varchar', 'nchar', 10, 10, 10, 10)") tdSql.execute(f"insert into test.td32727 values ('2020-02-01 00:00:15', 15, 15, 15, 15, 15.0, 15.0, true, 'varchar', 'nchar', 15, 15, 15, 15)") + tdSql.execute( + """insert into test.td32861 values + ('2020-01-01 00:00:00', 0), + ('2020-01-01 00:00:01', 1), + ('2020-01-01 00:00:03', 3), + ('2020-01-01 00:00:06', 6), + ('2020-01-01 00:00:10', 10), + ('2020-01-01 00:00:15', 15), + ('2020-01-01 00:00:21', 21);""" + ) def test_normal_query_new(self, testCase): # read sql from .sql file and execute From cb44efd3297dc74d3a805d2296b1840174a34635 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Mon, 18 Nov 2024 15:22:11 +0800 Subject: [PATCH 38/88] fix comments --- source/libs/nodes/src/nodesUtilFuncs.c | 11 ++++++++++- source/libs/parser/src/parCalcConst.c | 7 ++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/source/libs/nodes/src/nodesUtilFuncs.c b/source/libs/nodes/src/nodesUtilFuncs.c index 4677b5d5f0..30cc552761 100644 --- a/source/libs/nodes/src/nodesUtilFuncs.c +++ b/source/libs/nodes/src/nodesUtilFuncs.c @@ -2960,7 +2960,16 @@ static SNode* nodesListFindNode(SNodeList* pList, SNode* pNode) { } int32_t nodesListDeduplicate(SNodeList** ppList) { - if (!ppList || LIST_LENGTH(*ppList) == 0) return TSDB_CODE_SUCCESS; + if (!ppList || LIST_LENGTH(*ppList) <= 1) return TSDB_CODE_SUCCESS; + if (LIST_LENGTH(*ppList) == 2) { + SNode* pNode1 = nodesListGetNode(*ppList, 0); + SNode* pNode2 = nodesListGetNode(*ppList, 1); + if (nodesEqualNode(pNode1, pNode2)) { + SListCell* pCell = nodesListGetCell(*ppList, 1); + (void)nodesListErase(*ppList, pCell); + } + return TSDB_CODE_SUCCESS; + } SNodeList* pTmp = NULL; int32_t code = nodesMakeList(&pTmp); if (TSDB_CODE_SUCCESS == code) { diff --git a/source/libs/parser/src/parCalcConst.c b/source/libs/parser/src/parCalcConst.c index 778c34ff02..9702c2e760 100644 --- a/source/libs/parser/src/parCalcConst.c +++ b/source/libs/parser/src/parCalcConst.c @@ -326,7 +326,6 @@ static int32_t calcConstProjections(SCalcConstContext* pCxt, SSelectStmt* pSelec static int32_t calcConstGroupBy(SCalcConstContext* pCxt, SSelectStmt* pSelect) { int32_t code = calcConstList(pSelect->pGroupByList); -#if 0 if (TSDB_CODE_SUCCESS == code) { SNode* pNode = NULL; FOREACH(pNode, pSelect->pGroupByList) { @@ -337,9 +336,11 @@ static int32_t calcConstGroupBy(SCalcConstContext* pCxt, SSelectStmt* pSelect) { } } } - NODES_DESTORY_LIST(pSelect->pGroupByList); + FOREACH(pNode, pSelect->pGroupByList) { + if (!cell->pPrev) continue; + ERASE_NODE(pSelect->pGroupByList); + } } -#endif return code; } From 472e8361ea63cc92a8a759f6da31d33f484aa8b1 Mon Sep 17 00:00:00 2001 From: dmchen Date: Mon, 18 Nov 2024 18:25:52 +0800 Subject: [PATCH 39/88] ehn/add-sync-heartbeat-sent-time-to-log --- source/libs/sync/inc/syncIndexMgr.h | 3 ++ source/libs/sync/inc/syncUtil.h | 15 ++++--- source/libs/sync/src/syncIndexMgr.c | 25 +++++++++++ source/libs/sync/src/syncReplication.c | 7 ++- source/libs/sync/src/syncUtil.c | 60 ++++++++++++++++++++++---- 5 files changed, 93 insertions(+), 17 deletions(-) diff --git a/source/libs/sync/inc/syncIndexMgr.h b/source/libs/sync/inc/syncIndexMgr.h index 3c372a3b12..ed7a17b4c7 100644 --- a/source/libs/sync/inc/syncIndexMgr.h +++ b/source/libs/sync/inc/syncIndexMgr.h @@ -29,6 +29,7 @@ typedef struct SSyncIndexMgr { SyncTerm privateTerm[TSDB_MAX_REPLICA + TSDB_MAX_LEARNER_REPLICA]; // for advanced function int64_t startTimeArr[TSDB_MAX_REPLICA + TSDB_MAX_LEARNER_REPLICA]; int64_t recvTimeArr[TSDB_MAX_REPLICA + TSDB_MAX_LEARNER_REPLICA]; + int64_t sentTimeArr[TSDB_MAX_REPLICA + TSDB_MAX_LEARNER_REPLICA]; int32_t replicaNum; int32_t totalReplicaNum; SSyncNode *pNode; @@ -45,7 +46,9 @@ void syncIndexMgrCopyIfExist(SSyncIndexMgr * pNewIndex, SSyncIndexMgr void syncIndexMgrSetStartTime(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId, int64_t startTime); int64_t syncIndexMgrGetStartTime(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId); void syncIndexMgrSetRecvTime(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId, int64_t recvTime); +void syncIndexMgrSetSentTime(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId, int64_t sentTime); int64_t syncIndexMgrGetRecvTime(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId); +int64_t syncIndexMgrGetSentTime(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId); void syncIndexMgrSetTerm(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId, SyncTerm term); SyncTerm syncIndexMgrGetTerm(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId); diff --git a/source/libs/sync/inc/syncUtil.h b/source/libs/sync/inc/syncUtil.h index 1606f47592..7b71491f47 100644 --- a/source/libs/sync/inc/syncUtil.h +++ b/source/libs/sync/inc/syncUtil.h @@ -46,12 +46,12 @@ extern "C" { #define sLDebug(...) if (sDebugFlag & DEBUG_DEBUG) { taosPrintLongString("SYN ", DEBUG_DEBUG, sDebugFlag, __VA_ARGS__); } #define sLTrace(...) if (sDebugFlag & DEBUG_TRACE) { taosPrintLongString("SYN ", DEBUG_TRACE, sDebugFlag, __VA_ARGS__); } -#define sNFatal(pNode, ...) if (sDebugFlag & DEBUG_FATAL) { syncPrintNodeLog("SYN FATAL ", DEBUG_FATAL, 255, pNode, __VA_ARGS__); } -#define sNError(pNode, ...) if (sDebugFlag & DEBUG_ERROR) { syncPrintNodeLog("SYN ERROR ", DEBUG_ERROR, 255, pNode, __VA_ARGS__); } -#define sNWarn(pNode, ...) if (sDebugFlag & DEBUG_WARN) { syncPrintNodeLog("SYN WARN ", DEBUG_WARN, 255, pNode, __VA_ARGS__); } -#define sNInfo(pNode, ...) if (sDebugFlag & DEBUG_INFO) { syncPrintNodeLog("SYN ", DEBUG_INFO, 255, pNode, __VA_ARGS__); } -#define sNDebug(pNode, ...) if (sDebugFlag & DEBUG_DEBUG) { syncPrintNodeLog("SYN ", DEBUG_DEBUG, sDebugFlag, pNode, __VA_ARGS__); } -#define sNTrace(pNode, ...) if (sDebugFlag & DEBUG_TRACE) { syncPrintNodeLog("SYN ", DEBUG_TRACE, sDebugFlag, pNode, __VA_ARGS__); } +#define sNFatal(pNode, ...) if (sDebugFlag & DEBUG_FATAL) { syncPrintNodeLog("SYN FATAL ", DEBUG_FATAL, 255, true, pNode, __VA_ARGS__); } +#define sNError(pNode, ...) if (sDebugFlag & DEBUG_ERROR) { syncPrintNodeLog("SYN ERROR ", DEBUG_ERROR, 255, true, pNode, __VA_ARGS__); } +#define sNWarn(pNode, ...) if (sDebugFlag & DEBUG_WARN) { syncPrintNodeLog("SYN WARN ", DEBUG_WARN, 255, true, pNode, __VA_ARGS__); } +#define sNInfo(pNode, ...) if (sDebugFlag & DEBUG_INFO) { syncPrintNodeLog("SYN ", DEBUG_INFO, 255, true, pNode, __VA_ARGS__); } +#define sNDebug(pNode, ...) if (sDebugFlag & DEBUG_DEBUG) { syncPrintNodeLog("SYN ", DEBUG_DEBUG, sDebugFlag, false, pNode, __VA_ARGS__); } +#define sNTrace(pNode, ...) if (sDebugFlag & DEBUG_TRACE) { syncPrintNodeLog("SYN ", DEBUG_TRACE, sDebugFlag, false, pNode, __VA_ARGS__); } #define sSFatal(pSender, ...) if (sDebugFlag & DEBUG_FATAL) { syncPrintSnapshotSenderLog("SYN FATAL ", DEBUG_FATAL, 255, pSender, __VA_ARGS__); } #define sSError(pSender, ...) if (sDebugFlag & DEBUG_ERROR) { syncPrintSnapshotSenderLog("SYN ERROR ", DEBUG_ERROR, 255, pSender, __VA_ARGS__); } @@ -85,7 +85,8 @@ void syncUtilMsgHtoN(void* msg); void syncUtilGenerateArbToken(int32_t nodeId, int32_t groupId, char* buf); -void syncPrintNodeLog(const char* flags, ELogLevel level, int32_t dflag, SSyncNode* pNode, const char* format, ...); +void syncPrintNodeLog(const char* flags, ELogLevel level, int32_t dflag, bool formatTime, SSyncNode* pNode, + const char* format, ...); void syncPrintSnapshotSenderLog(const char* flags, ELogLevel level, int32_t dflag, SSyncSnapshotSender* pSender, const char* format, ...); void syncPrintSnapshotReceiverLog(const char* flags, ELogLevel level, int32_t dflag, SSyncSnapshotReceiver* pReceiver, diff --git a/source/libs/sync/src/syncIndexMgr.c b/source/libs/sync/src/syncIndexMgr.c index 4946912941..ec7354040f 100644 --- a/source/libs/sync/src/syncIndexMgr.c +++ b/source/libs/sync/src/syncIndexMgr.c @@ -155,6 +155,18 @@ void syncIndexMgrSetRecvTime(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId, i DID(pRaftId), CID(pRaftId)); } +void syncIndexMgrSetSentTime(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId, int64_t sentTime) { + for (int i = 0; i < pIndexMgr->totalReplicaNum; ++i) { + if (syncUtilSameId(&((*(pIndexMgr->replicas))[i]), pRaftId)) { + (pIndexMgr->sentTimeArr)[i] = sentTime; + return; + } + } + + sError("vgId:%d, indexmgr set sent-time:%" PRId64 " for dnode:%d cluster:%d failed", pIndexMgr->pNode->vgId, sentTime, + DID(pRaftId), CID(pRaftId)); +} + int64_t syncIndexMgrGetRecvTime(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId) { for (int i = 0; i < pIndexMgr->totalReplicaNum; ++i) { if (syncUtilSameId(&((*(pIndexMgr->replicas))[i]), pRaftId)) { @@ -168,6 +180,19 @@ int64_t syncIndexMgrGetRecvTime(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId return TSDB_CODE_SYN_INVALID_ID; } +int64_t syncIndexMgrGetSentTime(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId) { + for (int i = 0; i < pIndexMgr->totalReplicaNum; ++i) { + if (syncUtilSameId(&((*(pIndexMgr->replicas))[i]), pRaftId)) { + int64_t recvTime = (pIndexMgr->sentTimeArr)[i]; + return recvTime; + } + } + + sError("vgId:%d, indexmgr get sent-time from dnode:%d cluster:%d failed", pIndexMgr->pNode->vgId, DID(pRaftId), + CID(pRaftId)); + return TSDB_CODE_SYN_INVALID_ID; +} + void syncIndexMgrSetTerm(SSyncIndexMgr *pIndexMgr, const SRaftId *pRaftId, SyncTerm term) { for (int i = 0; i < pIndexMgr->totalReplicaNum; ++i) { if (syncUtilSameId(&((*(pIndexMgr->replicas))[i]), pRaftId)) { diff --git a/source/libs/sync/src/syncReplication.c b/source/libs/sync/src/syncReplication.c index 247b5624c3..b0735236c6 100644 --- a/source/libs/sync/src/syncReplication.c +++ b/source/libs/sync/src/syncReplication.c @@ -92,7 +92,12 @@ int32_t syncNodeSendAppendEntries(SSyncNode* pSyncNode, const SRaftId* destRaftI } int32_t syncNodeSendHeartbeat(SSyncNode* pSyncNode, const SRaftId* destId, SRpcMsg* pMsg) { - return syncNodeSendMsgById(destId, pSyncNode, pMsg); + TAOS_CHECK_RETURN(syncNodeSendMsgById(destId, pSyncNode, pMsg)); + + int64_t tsMs = taosGetTimestampMs(); + syncIndexMgrSetSentTime(pSyncNode->pMatchIndex, destId, tsMs); + + return TSDB_CODE_SUCCESS; } int32_t syncNodeHeartbeatPeers(SSyncNode* pSyncNode) { diff --git a/source/libs/sync/src/syncUtil.c b/source/libs/sync/src/syncUtil.c index 9058b6ecef..13ae4c4169 100644 --- a/source/libs/sync/src/syncUtil.c +++ b/source/libs/sync/src/syncUtil.c @@ -22,6 +22,7 @@ #include "syncRaftStore.h" #include "syncSnapshot.h" #include "tglobal.h" +#include "ttime.h" static void syncCfg2SimpleStr(const SSyncCfg* pCfg, char* buf, int32_t bufLen) { int32_t len = tsnprintf(buf, bufLen, "{num:%d, as:%d, [", pCfg->replicaNum, pCfg->myIndex); @@ -109,12 +110,41 @@ void syncUtilGenerateArbToken(int32_t nodeId, int32_t groupId, char* buf) { } // for leader -static void syncHearbeatReplyTime2Str(SSyncNode* pSyncNode, char* buf, int32_t bufLen) { +static void syncHearbeatReplyTime2Str(SSyncNode* pSyncNode, char* buf, int32_t bufLen, bool formatTime) { int32_t len = 0; len += tsnprintf(buf + len, bufLen - len, "%s", "{"); for (int32_t i = 0; i < pSyncNode->replicaNum; ++i) { int64_t tsMs = syncIndexMgrGetRecvTime(pSyncNode->pMatchIndex, &(pSyncNode->replicasId[i])); - len += tsnprintf(buf + len, bufLen - len, "%d:%" PRId64, i, tsMs); + if (formatTime) { + char pBuf[TD_TIME_STR_LEN] = {0}; + if (tsMs > 0) { + taosFormatUtcTime(pBuf, TD_TIME_STR_LEN, tsMs, TSDB_TIME_PRECISION_MILLI); + } + len += tsnprintf(buf + len, bufLen - len, "%d:%s", i, pBuf); + } else { + len += tsnprintf(buf + len, bufLen - len, "%d:%" PRId64, i, tsMs); + } + if (i < pSyncNode->replicaNum - 1) { + len += tsnprintf(buf + len, bufLen - len, "%s", ","); + } + } + len += tsnprintf(buf + len, bufLen - len, "%s", "}"); +} + +static void syncSentHearbeatTime2Str(SSyncNode* pSyncNode, char* buf, int32_t bufLen, bool formatTime) { + int32_t len = 0; + len += tsnprintf(buf + len, bufLen - len, "%s", "{"); + for (int32_t i = 0; i < pSyncNode->replicaNum; ++i) { + int64_t tsMs = syncIndexMgrGetSentTime(pSyncNode->pMatchIndex, &(pSyncNode->replicasId[i])); + if (formatTime) { + char pBuf[TD_TIME_STR_LEN] = {0}; + if (tsMs > 0) { + taosFormatUtcTime(pBuf, TD_TIME_STR_LEN, tsMs, TSDB_TIME_PRECISION_MILLI); + } + len += tsnprintf(buf + len, bufLen - len, "%d:%s", i, pBuf); + } else { + len += tsnprintf(buf + len, bufLen - len, "%d:%" PRId64, i, tsMs); + } if (i < pSyncNode->replicaNum - 1) { len += tsnprintf(buf + len, bufLen - len, "%s", ","); } @@ -123,12 +153,20 @@ static void syncHearbeatReplyTime2Str(SSyncNode* pSyncNode, char* buf, int32_t b } // for follower -static void syncHearbeatTime2Str(SSyncNode* pSyncNode, char* buf, int32_t bufLen) { +static void syncHearbeatTime2Str(SSyncNode* pSyncNode, char* buf, int32_t bufLen, bool formatTime) { int32_t len = 0; len += tsnprintf(buf + len, bufLen - len, "%s", "{"); for (int32_t i = 0; i < pSyncNode->replicaNum; ++i) { int64_t tsMs = syncIndexMgrGetRecvTime(pSyncNode->pNextIndex, &(pSyncNode->replicasId[i])); - len += tsnprintf(buf + len, bufLen - len, "%d:%" PRId64, i, tsMs); + if (formatTime) { + char pBuf[TD_TIME_STR_LEN] = {0}; + if (tsMs > 0) { + taosFormatUtcTime(pBuf, TD_TIME_STR_LEN, tsMs, TSDB_TIME_PRECISION_MILLI); + } + len += tsnprintf(buf + len, bufLen - len, "%d:%s", i, pBuf); + } else { + len += tsnprintf(buf + len, bufLen - len, "%d:%" PRId64, i, tsMs); + } if (i < pSyncNode->replicaNum - 1) { len += tsnprintf(buf + len, bufLen - len, "%s", ","); } @@ -173,7 +211,8 @@ static void syncPeerState2Str(SSyncNode* pSyncNode, char* buf, int32_t bufLen) { len += tsnprintf(buf + len, bufLen - len, "%s", "}"); } -void syncPrintNodeLog(const char* flags, ELogLevel level, int32_t dflag, SSyncNode* pNode, const char* format, ...) { +void syncPrintNodeLog(const char* flags, ELogLevel level, int32_t dflag, bool formatTime, SSyncNode* pNode, + const char* format, ...) { if (pNode == NULL || pNode->pLogStore == NULL) return; int64_t currentTerm = raftStoreGetTerm(pNode); @@ -205,10 +244,13 @@ void syncPrintNodeLog(const char* flags, ELogLevel level, int32_t dflag, SSyncNo syncLogBufferStates2Str(pNode, bufferStatesStr, sizeof(bufferStatesStr)); char hbrTimeStr[256] = ""; - syncHearbeatReplyTime2Str(pNode, hbrTimeStr, sizeof(hbrTimeStr)); + syncHearbeatReplyTime2Str(pNode, hbrTimeStr, sizeof(hbrTimeStr), formatTime); char hbTimeStr[256] = ""; - syncHearbeatTime2Str(pNode, hbTimeStr, sizeof(hbTimeStr)); + syncHearbeatTime2Str(pNode, hbTimeStr, sizeof(hbTimeStr), formatTime); + + char sentHbTimeStr[512] = ""; + syncSentHearbeatTime2Str(pNode, sentHbTimeStr, sizeof(sentHbTimeStr), formatTime); char eventLog[512]; // {0}; va_list argpointer; @@ -234,14 +276,14 @@ void syncPrintNodeLog(const char* flags, ELogLevel level, int32_t dflag, SSyncNo ", elect-times:%d, as-leader-times:%d, as-assigned-leader-times:%d, cfg-ch-times:%d, hb-slow:%d, hbr-slow:%d, " "aq-items:%d, snaping:%" PRId64 ", replicas:%d, last-cfg:%" PRId64 ", chging:%d, restore:%d, quorum:%d, elect-lc-timer:%" PRId64 ", hb:%" PRId64 - ", buffer:%s, repl-mgrs:%s, members:%s, hb:%s, hb-reply:%s, arb-token:%s", + ", buffer:%s, repl-mgrs:%s, members:%s, send hb:%s:, recv hb:%s, recv hb-reply:%s, arb-token:%s", pNode->vgId, eventLog, syncStr(pNode->state), currentTerm, pNode->commitIndex, pNode->assignedCommitIndex, appliedIndex, logBeginIndex, logLastIndex, pNode->minMatchIndex, snapshot.lastApplyIndex, snapshot.lastApplyTerm, pNode->electNum, pNode->becomeLeaderNum, pNode->becomeAssignedLeaderNum, pNode->configChangeNum, pNode->hbSlowNum, pNode->hbrSlowNum, aqItems, pNode->snapshottingIndex, pNode->replicaNum, pNode->raftCfg.lastConfigIndex, pNode->changing, pNode->restoreFinish, syncNodeDynamicQuorum(pNode), pNode->electTimerLogicClock, pNode->heartbeatTimerLogicClockUser, bufferStatesStr, - replMgrStatesStr, cfgStr, hbTimeStr, hbrTimeStr, pNode->arbToken); + replMgrStatesStr, cfgStr, sentHbTimeStr, hbTimeStr, hbrTimeStr, pNode->arbToken); } } From 31f5055f21bf60cd447a4f95e59291ea7190c497 Mon Sep 17 00:00:00 2001 From: dmchen Date: Mon, 18 Nov 2024 19:06:46 +0800 Subject: [PATCH 40/88] fix/TS-5639-check-mnode-leader --- source/dnode/mnode/impl/src/mndMain.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/dnode/mnode/impl/src/mndMain.c b/source/dnode/mnode/impl/src/mndMain.c index 6c30193ea7..9dd43225b1 100644 --- a/source/dnode/mnode/impl/src/mndMain.c +++ b/source/dnode/mnode/impl/src/mndMain.c @@ -53,7 +53,7 @@ static inline int32_t mndAcquireRpc(SMnode *pMnode) { if (pMnode->stopped) { code = TSDB_CODE_APP_IS_STOPPING; } else if (!mndIsLeader(pMnode)) { - code = -1; + code = 1; } else { #if 1 (void)atomic_add_fetch_32(&pMnode->rpcRef, 1); @@ -1002,8 +1002,12 @@ int64_t mndGenerateUid(const char *name, int32_t len) { int32_t mndGetMonitorInfo(SMnode *pMnode, SMonClusterInfo *pClusterInfo, SMonVgroupInfo *pVgroupInfo, SMonStbInfo *pStbInfo, SMonGrantInfo *pGrantInfo) { - int32_t code = 0; - TAOS_CHECK_RETURN(mndAcquireRpc(pMnode)); + int32_t code = mndAcquireRpc(pMnode); + if (code < 0) { + TAOS_RETURN(code); + } else if (code == 1) { + TAOS_RETURN(TSDB_CODE_SUCCESS); + } SSdb *pSdb = pMnode->pSdb; int64_t ms = taosGetTimestampMs(); From fde7a7aeaf56319489da23f8991edbfeb58743f3 Mon Sep 17 00:00:00 2001 From: dmchen Date: Tue, 19 Nov 2024 08:23:43 +0800 Subject: [PATCH 41/88] ehn/add-sync-heartbeat-sent-time-to-log-fix-check --- source/libs/sync/src/syncUtil.c | 44 +++++++++++++-------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/source/libs/sync/src/syncUtil.c b/source/libs/sync/src/syncUtil.c index 13ae4c4169..1e228fbdf2 100644 --- a/source/libs/sync/src/syncUtil.c +++ b/source/libs/sync/src/syncUtil.c @@ -109,21 +109,27 @@ void syncUtilGenerateArbToken(int32_t nodeId, int32_t groupId, char* buf) { (void)snprintf(buf, TSDB_ARB_TOKEN_SIZE, "d%d#g%d#%" PRId64 "#%d", nodeId, groupId, currentMs, randVal); } +static void syncPrintTime(bool formatTime, int32_t* len, int64_t tsMs, int32_t i, char* buf, int32_t bufLen) { + if (formatTime) { + char pBuf[TD_TIME_STR_LEN] = {0}; + if (tsMs > 0) { + if (taosFormatUtcTime(pBuf, TD_TIME_STR_LEN, tsMs, TSDB_TIME_PRECISION_MILLI) != 0) { + pBuf[0] = '\0'; + } + } + (*len) += tsnprintf(buf + (*len), bufLen - (*len), "%d:%s", i, pBuf); + } else { + (*len) += tsnprintf(buf + (*len), bufLen - (*len), "%d:%" PRId64, i, tsMs); + } +} + // for leader static void syncHearbeatReplyTime2Str(SSyncNode* pSyncNode, char* buf, int32_t bufLen, bool formatTime) { int32_t len = 0; len += tsnprintf(buf + len, bufLen - len, "%s", "{"); for (int32_t i = 0; i < pSyncNode->replicaNum; ++i) { int64_t tsMs = syncIndexMgrGetRecvTime(pSyncNode->pMatchIndex, &(pSyncNode->replicasId[i])); - if (formatTime) { - char pBuf[TD_TIME_STR_LEN] = {0}; - if (tsMs > 0) { - taosFormatUtcTime(pBuf, TD_TIME_STR_LEN, tsMs, TSDB_TIME_PRECISION_MILLI); - } - len += tsnprintf(buf + len, bufLen - len, "%d:%s", i, pBuf); - } else { - len += tsnprintf(buf + len, bufLen - len, "%d:%" PRId64, i, tsMs); - } + syncPrintTime(formatTime, &len, tsMs, i, buf, bufLen); if (i < pSyncNode->replicaNum - 1) { len += tsnprintf(buf + len, bufLen - len, "%s", ","); } @@ -136,15 +142,7 @@ static void syncSentHearbeatTime2Str(SSyncNode* pSyncNode, char* buf, int32_t bu len += tsnprintf(buf + len, bufLen - len, "%s", "{"); for (int32_t i = 0; i < pSyncNode->replicaNum; ++i) { int64_t tsMs = syncIndexMgrGetSentTime(pSyncNode->pMatchIndex, &(pSyncNode->replicasId[i])); - if (formatTime) { - char pBuf[TD_TIME_STR_LEN] = {0}; - if (tsMs > 0) { - taosFormatUtcTime(pBuf, TD_TIME_STR_LEN, tsMs, TSDB_TIME_PRECISION_MILLI); - } - len += tsnprintf(buf + len, bufLen - len, "%d:%s", i, pBuf); - } else { - len += tsnprintf(buf + len, bufLen - len, "%d:%" PRId64, i, tsMs); - } + syncPrintTime(formatTime, &len, tsMs, i, buf, bufLen); if (i < pSyncNode->replicaNum - 1) { len += tsnprintf(buf + len, bufLen - len, "%s", ","); } @@ -158,15 +156,7 @@ static void syncHearbeatTime2Str(SSyncNode* pSyncNode, char* buf, int32_t bufLen len += tsnprintf(buf + len, bufLen - len, "%s", "{"); for (int32_t i = 0; i < pSyncNode->replicaNum; ++i) { int64_t tsMs = syncIndexMgrGetRecvTime(pSyncNode->pNextIndex, &(pSyncNode->replicasId[i])); - if (formatTime) { - char pBuf[TD_TIME_STR_LEN] = {0}; - if (tsMs > 0) { - taosFormatUtcTime(pBuf, TD_TIME_STR_LEN, tsMs, TSDB_TIME_PRECISION_MILLI); - } - len += tsnprintf(buf + len, bufLen - len, "%d:%s", i, pBuf); - } else { - len += tsnprintf(buf + len, bufLen - len, "%d:%" PRId64, i, tsMs); - } + syncPrintTime(formatTime, &len, tsMs, i, buf, bufLen); if (i < pSyncNode->replicaNum - 1) { len += tsnprintf(buf + len, bufLen - len, "%s", ","); } From 7f92585b93bf917ed4313ce335751fa55e2e4162 Mon Sep 17 00:00:00 2001 From: menshibin Date: Mon, 18 Nov 2024 20:09:37 +0800 Subject: [PATCH 42/88] add test tzlocal dependent --- docs/examples/node/package.json | 10 ++++++++-- tests/requirements.txt | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/examples/node/package.json b/docs/examples/node/package.json index 14303c8f37..d77c96fbb3 100644 --- a/docs/examples/node/package.json +++ b/docs/examples/node/package.json @@ -4,6 +4,12 @@ "main": "index.js", "license": "MIT", "dependencies": { - "@tdengine/websocket": "^3.1.1" - } + "@tdengine/websocket": "^3.1.2" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "description": "" } diff --git a/tests/requirements.txt b/tests/requirements.txt index c6dd044c86..a036c2b3d0 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -9,4 +9,5 @@ requests pexpect faker pyopenssl -hyperloglog \ No newline at end of file +hyperloglog +tzlocal \ No newline at end of file From b3eb84c0047dc5d960092f11f97482ff9443d3e6 Mon Sep 17 00:00:00 2001 From: factosea <285808407@qq.com> Date: Tue, 19 Nov 2024 18:30:27 +0800 Subject: [PATCH 43/88] fix: fetch_row callback error code --- source/client/src/clientImpl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/client/src/clientImpl.c b/source/client/src/clientImpl.c index 8a0b1ddaab..94d06166f2 100644 --- a/source/client/src/clientImpl.c +++ b/source/client/src/clientImpl.c @@ -3032,13 +3032,13 @@ static void fetchCallback(void* pResult, void* param, int32_t code) { if (code != TSDB_CODE_SUCCESS) { pRequest->code = code; taosMemoryFreeClear(pResultInfo->pData); - pRequest->body.fetchFp(((SSyncQueryParam*)pRequest->body.interParam)->userParam, pRequest, 0); + pRequest->body.fetchFp(((SSyncQueryParam*)pRequest->body.interParam)->userParam, pRequest, code); return; } if (pRequest->code != TSDB_CODE_SUCCESS) { taosMemoryFreeClear(pResultInfo->pData); - pRequest->body.fetchFp(((SSyncQueryParam*)pRequest->body.interParam)->userParam, pRequest, 0); + pRequest->body.fetchFp(((SSyncQueryParam*)pRequest->body.interParam)->userParam, pRequest, pRequest->code); return; } From 828f01f5ae8cd9826f89f1846fd4648954388810 Mon Sep 17 00:00:00 2001 From: dmchen Date: Wed, 20 Nov 2024 08:57:27 +0800 Subject: [PATCH 44/88] ehn/add-sync-heartbeat-sent-time-to-log --- source/libs/sync/src/syncReplication.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/libs/sync/src/syncReplication.c b/source/libs/sync/src/syncReplication.c index b0735236c6..49d438796a 100644 --- a/source/libs/sync/src/syncReplication.c +++ b/source/libs/sync/src/syncReplication.c @@ -92,10 +92,11 @@ int32_t syncNodeSendAppendEntries(SSyncNode* pSyncNode, const SRaftId* destRaftI } int32_t syncNodeSendHeartbeat(SSyncNode* pSyncNode, const SRaftId* destId, SRpcMsg* pMsg) { + SRaftId destIdTmp = *destId; TAOS_CHECK_RETURN(syncNodeSendMsgById(destId, pSyncNode, pMsg)); int64_t tsMs = taosGetTimestampMs(); - syncIndexMgrSetSentTime(pSyncNode->pMatchIndex, destId, tsMs); + syncIndexMgrSetSentTime(pSyncNode->pMatchIndex, &destIdTmp, tsMs); return TSDB_CODE_SUCCESS; } From 15bccef3920d7c375dc9a612dbb808e411617b38 Mon Sep 17 00:00:00 2001 From: Shungang Li Date: Wed, 20 Nov 2024 11:02:17 +0800 Subject: [PATCH 45/88] docs: describe KEEP_TIME_OFFSET --- docs/en/14-reference/12-config/index.md | 4 ++-- docs/zh/14-reference/03-taos-sql/02-database.md | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/en/14-reference/12-config/index.md b/docs/en/14-reference/12-config/index.md index 77d183a5ef..63aa6ed447 100755 --- a/docs/en/14-reference/12-config/index.md +++ b/docs/en/14-reference/12-config/index.md @@ -773,7 +773,7 @@ lossyColumns float|double 02/22 10:49:27.607990 00002933 UTL lossyColumns float|double ``` -### ifAdtFse +### ifAdtFse | Attribute | Description | | -------- | -------------------------------- | @@ -898,4 +898,4 @@ lossyColumns float|double | 53 | udf | Yes | Yes | | | 54 | enableCoreFile | Yes | Yes | | | 55 | ttlChangeOnWrite | No | Yes | | -| 56 | keepTimeOffset | Yes | Yes(discarded since 3.2.0.0) | | +| 56 | keepTimeOffset | Yes | Yes(discarded since 3.2.0.0) | see "KEEP_TIME_OFFSET" | diff --git a/docs/zh/14-reference/03-taos-sql/02-database.md b/docs/zh/14-reference/03-taos-sql/02-database.md index 0d9ab019c0..25d7e54823 100644 --- a/docs/zh/14-reference/03-taos-sql/02-database.md +++ b/docs/zh/14-reference/03-taos-sql/02-database.md @@ -8,10 +8,10 @@ description: "创建、删除数据库,查看、修改数据库参数" ```sql CREATE DATABASE [IF NOT EXISTS] db_name [database_options] - + database_options: database_option ... - + database_option: { VGROUPS value | PRECISION {'ms' | 'us' | 'ns'} @@ -26,6 +26,7 @@ database_option: { | MAXROWS value | MINROWS value | KEEP value + | KEEP_TIME_OFFSET value | STT_TRIGGER value | SINGLE_STABLE {0 | 1} | TABLE_PREFIX value @@ -43,7 +44,7 @@ database_option: { - VGROUPS:数据库中初始 vgroup 的数目。 - PRECISION:数据库的时间戳精度。ms 表示毫秒,us 表示微秒,ns 表示纳秒,默认 ms 毫秒。 -- REPLICA:表示数据库副本数,取值为 1、2 或 3,默认为 1; 2 仅在企业版 3.3.0.0 及以后版本中可用。在集群中使用,副本数必须小于或等于 DNODE 的数目。且使用时存在以下限制: +- REPLICA:表示数据库副本数,取值为 1、2 或 3,默认为 1; 2 仅在企业版 3.3.0.0 及以后版本中可用。在集群中使用,副本数必须小于或等于 DNODE 的数目。且使用时存在以下限制: - 暂不支持对双副本数据库相关 Vgroup 进行 SPLITE VGROUP 或 REDISTRIBUTE VGROUP 操作 - 单副本数据库可变更为双副本数据库,但不支持从双副本变更为其它副本数,也不支持从三副本变更为双副本 - BUFFER: 一个 VNODE 写入内存池大小,单位为 MB,默认为 256,最小为 3,最大为 16384。 @@ -64,6 +65,7 @@ database_option: { - MAXROWS:文件块中记录的最大条数,默认为 4096 条。 - MINROWS:文件块中记录的最小条数,默认为 100 条。 - KEEP:表示数据文件保存的天数,缺省值为 3650,取值范围 [1, 365000],且必须大于或等于3倍的 DURATION 参数值。数据库会自动删除保存时间超过 KEEP 值的数据从而释放存储空间。KEEP 可以使用加单位的表示形式,如 KEEP 100h、KEEP 10d 等,支持 m(分钟)、h(小时)和 d(天)三个单位。也可以不写单位,如 KEEP 50,此时默认单位为天。企业版支持[多级存储](https://docs.taosdata.com/tdinternal/arch/#%E5%A4%9A%E7%BA%A7%E5%AD%98%E5%82%A8)功能, 因此, 可以设置多个保存时间(多个以英文逗号分隔,最多 3 个,满足 keep 0 \<= keep 1 \<= keep 2,如 KEEP 100h,100d,3650d); 社区版不支持多级存储功能(即使配置了多个保存时间, 也不会生效, KEEP 会取最大的保存时间)。 +- KEEP_TIME_OFFSET:自 3.2.0.0 版本生效。删除或迁移保存时间超过 KEEP 值的数据的延迟执行时间,默认值为 0 (小时)。在数据文件保存时间超过 KEEP 后,删除或迁移操作不会立即执行,而会额外等待本参数指定的时间间隔,以实现与业务高峰期错开的目的。 - STT_TRIGGER:表示落盘文件触发文件合并的个数。开源版本固定为 1,企业版本可设置范围为 1 到 16。对于少表高频写入场景,此参数建议使用默认配置;而对于多表低频写入场景,此参数建议配置较大的值。 - SINGLE_STABLE:表示此数据库中是否只可以创建一个超级表,用于超级表列非常多的情况。 - 0:表示可以创建多张超级表。 From 9314dfb80cf26cfbecb4668994fff9a008fc519d Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Wed, 20 Nov 2024 14:29:27 +0800 Subject: [PATCH 46/88] fix not setting step in tsdbread --- source/dnode/vnode/src/tsdb/tsdbRead2.c | 4 ++-- tests/system-test/2-query/partition_by_col.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/source/dnode/vnode/src/tsdb/tsdbRead2.c b/source/dnode/vnode/src/tsdb/tsdbRead2.c index 441d073666..527486270c 100644 --- a/source/dnode/vnode/src/tsdb/tsdbRead2.c +++ b/source/dnode/vnode/src/tsdb/tsdbRead2.c @@ -4812,7 +4812,7 @@ static int32_t checkForNeighborFileBlock(STsdbReader* pReader, STableBlockScanIn pBlockData = &pReader->status.fileBlockData; asc = ASCENDING_TRAVERSE(pReader->info.order); pVerRange = &pReader->info.verRange; - ASCENDING_TRAVERSE(pReader->info.order) ? 1 : -1; + step = ASCENDING_TRAVERSE(pReader->info.order) ? 1 : -1; *state = CHECK_FILEBLOCK_QUIT; code = loadNeighborIfOverlap(pFBlock, pScanInfo, pReader, &loadNeighbor); @@ -6169,7 +6169,7 @@ int32_t tsdbNextDataBlock2(STsdbReader* pReader, bool* hasNext) { TSDB_CHECK_CODE(code, lino, _end); } - goto _end; + return code; } } diff --git a/tests/system-test/2-query/partition_by_col.py b/tests/system-test/2-query/partition_by_col.py index ef88e88cbd..da7fe78124 100644 --- a/tests/system-test/2-query/partition_by_col.py +++ b/tests/system-test/2-query/partition_by_col.py @@ -313,7 +313,21 @@ class TDTestCase: order_by_list = 'ts,c1,c2,c3,c4,c5,c6,c7,c8,c9,t1,t2,t3,t4,t5,t6' self.prepare_and_query_and_compare(sqls, order_by_list, compare_what=COMPARE_LEN) + + def test_tsdb_read(self): + tdSql.execute('delete from t0') + tdSql.execute('flush database test') + for i in range(0, 4096): + tdSql.execute(f"insert into test.t0 values({1537146000000 + i}, 1,1,1,1,1,1,1,'a','1')") + tdSql.execute("flush database test") + tdSql.execute(f"insert into t0 values({1537146000000 + 4095}, 1,1,1,1,1,1,1,'a','1')") + for i in range(4095, 4096*2 + 100): + tdSql.execute(f"insert into test.t0 values({1537146000000 + i}, 1,1,1,1,1,1,1,'a','1')") + tdSql.execute("flush database test") + time.sleep(5) + tdSql.query('select first(ts), last(ts) from t0', queryTimes=1) + tdSql.checkRows(1) def run(self): self.prepareTestEnv() @@ -323,6 +337,8 @@ class TDTestCase: self.test_sort_for_partition_res() self.test_sort_for_partition_interval() self.test_sort_for_partition_no_agg_limit() + self.test_tsdb_read() + def stop(self): tdSql.close() From e42ca483b0190005264c17f7a14f9a71d0ef8447 Mon Sep 17 00:00:00 2001 From: Jing Sima Date: Wed, 20 Nov 2024 15:48:10 +0800 Subject: [PATCH 47/88] docs:[TD-32946] Modify the description of time and date functions. --- .../en/14-reference/03-taos-sql/10-function.md | 4 ++-- .../zh/14-reference/03-taos-sql/10-function.md | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/en/14-reference/03-taos-sql/10-function.md b/docs/en/14-reference/03-taos-sql/10-function.md index f6c1ef24d0..3852783c10 100644 --- a/docs/en/14-reference/03-taos-sql/10-function.md +++ b/docs/en/14-reference/03-taos-sql/10-function.md @@ -422,7 +422,7 @@ CAST(expr AS type_name) TO_ISO8601(expr [, timezone]) ``` -**Description**: The ISO8601 date/time format converted from a UNIX timestamp, plus the timezone. You can specify any time zone with the timezone parameter. If you do not enter this parameter, the time zone on the client is used. +**Description**: The ISO8601 date/time format converted from a timestamp, plus the timezone. You can specify any time zone with the timezone parameter. If you do not enter this parameter, the time zone on the client is used. **Return value type**: VARCHAR @@ -466,7 +466,7 @@ return_timestamp: { } ``` -**Description**: UNIX timestamp converted from a string of date/time format +**Description**: timestamp converted from a string of date/time format **Return value type**: BIGINT, TIMESTAMP diff --git a/docs/zh/14-reference/03-taos-sql/10-function.md b/docs/zh/14-reference/03-taos-sql/10-function.md index ae256a4ac0..2f4b739447 100644 --- a/docs/zh/14-reference/03-taos-sql/10-function.md +++ b/docs/zh/14-reference/03-taos-sql/10-function.md @@ -1065,7 +1065,7 @@ CAST(expr AS type_name) TO_ISO8601(expr [, timezone]) ``` -**功能说明**:将 UNIX 时间戳转换成为 ISO8601 标准的日期时间格式,并附加时区信息。timezone 参数允许用户为输出结果指定附带任意时区信息。如果 timezone 参数省略,输出结果则附带当前客户端的系统时区信息。 +**功能说明**:将时间戳转换成为 ISO8601 标准的日期时间格式,并附加时区信息。timezone 参数允许用户为输出结果指定附带任意时区信息。如果 timezone 参数省略,输出结果则附带当前客户端的系统时区信息。 **返回结果数据类型**:VARCHAR 类型。 @@ -1109,7 +1109,7 @@ return_timestamp: { } ``` -**功能说明**:将日期时间格式的字符串转换成为 UNIX 时间戳。 +**功能说明**:将日期时间格式的字符串转换成为时间戳。 **返回结果数据类型**:BIGINT, TIMESTAMP。 @@ -1257,8 +1257,8 @@ TIMEDIFF(expr1, expr2 [, time_unit]) **返回结果类型**:BIGINT。 **适用数据类型**: -- `expr1`:表示 UNIX 时间戳的 BIGINT, TIMESTAMP 类型,或符合日期时间格式的 VARCHAR, NCHAR 类型。 -- `expr2`:表示 UNIX 时间戳的 BIGINT, TIMESTAMP 类型,或符合日期时间格式的 VARCHAR, NCHAR 类型。 +- `expr1`:表示时间戳的 BIGINT, TIMESTAMP 类型,或符合 ISO8601/RFC3339 标准的日期时间格式的 VARCHAR, NCHAR 类型。 +- `expr2`:表示时间戳的 BIGINT, TIMESTAMP 类型,或符合 ISO8601/RFC3339 标准的日期时间格式的 VARCHAR, NCHAR 类型。 - `time_unit`:见使用说明。 **嵌套子查询支持**:适用于内层查询和外层查询。 @@ -1301,7 +1301,7 @@ use_current_timezone: { **返回结果数据类型**:TIMESTAMP。 -**应用字段**:表示 UNIX 时间戳的 BIGINT, TIMESTAMP 类型,或符合日期时间格式的 VARCHAR, NCHAR 类型。 +**应用字段**:表示时间戳的 BIGINT, TIMESTAMP 类型,或符合 ISO8601/RFC3339 标准的日期时间格式的 VARCHAR, NCHAR 类型。 **适用于**:表和超级表。 @@ -1364,7 +1364,7 @@ WEEK(expr [, mode]) **返回结果类型**:BIGINT。 **适用数据类型**: -- `expr`:表示 UNIX 时间戳的 BIGINT, TIMESTAMP 类型,或符合日期时间格式的 VARCHAR, NCHAR 类型。 +- `expr`:表示时间戳的 BIGINT, TIMESTAMP 类型,或符合 ISO8601/RFC3339 标准的日期时间格式的 VARCHAR, NCHAR 类型。 - `mode`:0 - 7 之间的整数。 **嵌套子查询支持**:适用于内层查询和外层查询。 @@ -1424,7 +1424,7 @@ WEEKOFYEAR(expr) **返回结果类型**:BIGINT。 -**适用数据类型**:表示 UNIX 时间戳的 BIGINT, TIMESTAMP 类型,或符合日期时间格式的 VARCHAR, NCHAR 类型。 +**适用数据类型**:表示时间戳的 BIGINT, TIMESTAMP 类型,或符合 ISO8601/RFC3339 标准的日期时间格式的 VARCHAR, NCHAR 类型。 **嵌套子查询支持**:适用于内层查询和外层查询。 @@ -1451,7 +1451,7 @@ WEEKDAY(expr) **返回结果类型**:BIGINT。 -**适用数据类型**:表示 UNIX 时间戳的 BIGINT, TIMESTAMP 类型,或符合日期时间格式的 VARCHAR, NCHAR 类型。 +**适用数据类型**:表示 表示时间戳的 BIGINT, TIMESTAMP 类型,或符合 ISO8601/RFC3339 标准的日期时间格式的 VARCHAR, NCHAR 类型。 **嵌套子查询支持**:适用于内层查询和外层查询。 @@ -1478,7 +1478,7 @@ DAYOFWEEK(expr) **返回结果类型**:BIGINT。 -**适用数据类型**:表示 UNIX 时间戳的 BIGINT, TIMESTAMP 类型,或符合日期时间格式的 VARCHAR, NCHAR 类型。 +**适用数据类型**:表示时间戳的 BIGINT, TIMESTAMP 类型,或符合 ISO8601/RFC3339 标准的日期时间格式的 VARCHAR, NCHAR 类型。 **嵌套子查询支持**:适用于内层查询和外层查询。 From 3a4ed77b170702c4f6fdfdd4b45fa596656e6017 Mon Sep 17 00:00:00 2001 From: menshibin Date: Wed, 20 Nov 2024 16:35:27 +0800 Subject: [PATCH 48/88] upgrade python version --- Jenkinsfile2 | 2 +- docs/en/14-reference/05-connectors/30-python.mdx | 6 ++++++ docs/en/14-reference/05-connectors/35-node.mdx | 2 ++ docs/zh/14-reference/05-connector/30-python.mdx | 2 ++ docs/zh/14-reference/05-connector/35-node.mdx | 1 + tests/ci/Dockerfile | 2 +- tests/docs-examples-test/python.sh | 2 +- tests/parallel_test/run_case.sh | 4 ++-- 8 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Jenkinsfile2 b/Jenkinsfile2 index 7ba9b4a933..910388fa8c 100644 --- a/Jenkinsfile2 +++ b/Jenkinsfile2 @@ -355,7 +355,7 @@ def pre_test_build_win() { bat ''' cd %WIN_COMMUNITY_ROOT%/tests/ci pip3 install taospy==2.7.16 - pip3 install taos-ws-py==0.3.3 + pip3 install taos-ws-py==0.3.5 xcopy /e/y/i/f %WIN_INTERNAL_ROOT%\\debug\\build\\lib\\taos.dll C:\\Windows\\System32 ''' return 1 diff --git a/docs/en/14-reference/05-connectors/30-python.mdx b/docs/en/14-reference/05-connectors/30-python.mdx index 7263a3caa6..4f17261b33 100644 --- a/docs/en/14-reference/05-connectors/30-python.mdx +++ b/docs/en/14-reference/05-connectors/30-python.mdx @@ -41,12 +41,18 @@ We recommend using the latest version of `taospy`, regardless of the version of |Python Client Library Version|major changes| |:-------------------:|:----:| +|2.7.16|add subscription configuration (session.timeout.ms, max.poll.interval.ms)| +|2.7.15|added support for VARBINARY and GEOMETRY types| +|2.7.14|fix known issues| +|2.7.13|add TMQ synchronous submission offset interface| |2.7.12|1. added support for `varbinary` type (STMT does not yet support)
2. improved query performance (thanks to contributor [hadrianl](https://github.com/taosdata/taos-connector-python/pull/209))| |2.7.9|support for getting assignment and seek function on subscription| |2.7.8|add `execute_many` method| |Python Websocket Connection Version|major changes| |:----------------------------:|:-----:| +|0.3.5|1. added support for VARBINARY and GEOMETRY types
2. Fix known issues| +|0.3.2|1. optimize WebSocket SQL query and insertion performance
2. Fix known issues
3. Modify the readme and document| |0.2.9|bugs fixes| |0.2.5|1. support for getting assignment and seek function on subscription
2. support schemaless
3. support STMT| |0.2.4|support `unsubscribe` on subscription| diff --git a/docs/en/14-reference/05-connectors/35-node.mdx b/docs/en/14-reference/05-connectors/35-node.mdx index 476f9bab71..2aeef7af1e 100644 --- a/docs/en/14-reference/05-connectors/35-node.mdx +++ b/docs/en/14-reference/05-connectors/35-node.mdx @@ -27,6 +27,8 @@ Node.js client library needs to be run with Node.js 14 or higher version. | Node.js connector version | major changes | TDengine 版本 | | :-----------------------: | :------------------: | :----------------:| +| 3.1.2 | Optimized the data protocol and parsing, resulting in a significant improvement in performance | 3.2.0.0 or later | +| 3.1.1 | Optimized data transmission performance | 3.2.0.0 or later | | 3.1.0 | new version, supports websocket | 3.2.0.0 or later | ## Supported features diff --git a/docs/zh/14-reference/05-connector/30-python.mdx b/docs/zh/14-reference/05-connector/30-python.mdx index 8436c30249..3991477635 100644 --- a/docs/zh/14-reference/05-connector/30-python.mdx +++ b/docs/zh/14-reference/05-connector/30-python.mdx @@ -41,6 +41,7 @@ Python 连接器的源码托管在 [GitHub](https://github.com/taosdata/taos-con |Python Connector 版本|主要变化| |:-------------------:|:----:| +|2.7.16|新增订阅配置 (session.timeout.ms, max.poll.interval.ms)| |2.7.15|新增 VARBINARY 和 GEOMETRY 类型支持| |2.7.14|修复已知问题| |2.7.13|新增 tmq 同步提交 offset 接口| @@ -50,6 +51,7 @@ Python 连接器的源码托管在 [GitHub](https://github.com/taosdata/taos-con |Python WebSocket Connector 版本|主要变化| |:----------------------------:|:-----:| +|0.3.5|新增 VARBINARY 和 GEOMETRY 类型支持,修复已知问题| |0.3.2|优化 WebSocket sql 查询和插入性能,修改 readme 和 文档,修复已知问题| |0.2.9|已知问题修复| |0.2.5|1. 数据订阅支持获取消费进度和重置消费进度
2. 支持 schemaless
3. 支持 STMT| diff --git a/docs/zh/14-reference/05-connector/35-node.mdx b/docs/zh/14-reference/05-connector/35-node.mdx index d9512eae78..df2abfab3d 100644 --- a/docs/zh/14-reference/05-connector/35-node.mdx +++ b/docs/zh/14-reference/05-connector/35-node.mdx @@ -26,6 +26,7 @@ Node.js 连接器目前仅支持 WebSocket 连接器, 其通过 taosAdapter | Node.js 连接器 版本 | 主要变化 | TDengine 版本 | | :------------------: | :----------------------: | :----------------: | +| 3.1.2 | 对数据协议和解析进行了优化,性能得到大幅提升| 3.3.2.0 及更高版本 | | 3.1.1 | 优化了数据传输性能 | 3.3.2.0 及更高版本 | | 3.1.0 | 新版本发布,支持 WebSocket 连接 | 3.2.0.0 及更高版本 | diff --git a/tests/ci/Dockerfile b/tests/ci/Dockerfile index d3d574b484..1caa6fea9e 100644 --- a/tests/ci/Dockerfile +++ b/tests/ci/Dockerfile @@ -7,7 +7,7 @@ RUN apt-get install -y locales psmisc sudo tree libgeos-dev libgflags2.2 libgfl RUN sed -i 's/# en_US.UTF-8/en_US.UTF-8/' /etc/locale.gen && locale-gen RUN pip3 config set global.index-url http://admin:123456@192.168.0.212:3141/admin/dev/+simple/ RUN pip3 config set global.trusted-host 192.168.0.212 -RUN pip3 install taospy==2.7.16 taos-ws-py==0.3.3 pandas psutil fabric2 requests faker simplejson toml pexpect tzlocal distro decorator loguru hyperloglog +RUN pip3 install taospy==2.7.16 taos-ws-py==0.3.5 pandas psutil fabric2 requests faker simplejson toml pexpect tzlocal distro decorator loguru hyperloglog ENV LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LC_ALL=en_US.UTF-8 RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9 RUN add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/' diff --git a/tests/docs-examples-test/python.sh b/tests/docs-examples-test/python.sh index 6a25683b58..3a9812637c 100644 --- a/tests/docs-examples-test/python.sh +++ b/tests/docs-examples-test/python.sh @@ -130,7 +130,7 @@ pip3 install kafka-python python3 kafka_example_consumer.py # 21 -pip3 install taos-ws-py==0.3.3 +pip3 install taos-ws-py==0.3.5 python3 conn_websocket_pandas.py # 22 diff --git a/tests/parallel_test/run_case.sh b/tests/parallel_test/run_case.sh index 5b0d34fc0a..a78d0aa4a4 100755 --- a/tests/parallel_test/run_case.sh +++ b/tests/parallel_test/run_case.sh @@ -76,9 +76,9 @@ ulimit -c unlimited md5sum /usr/lib/libtaos.so.1 md5sum /home/TDinternal/debug/build/lib/libtaos.so -#get python connector and update: taospy 2.7.16 taos-ws-py 0.3.3 +#get python connector and update: taospy 2.7.16 taos-ws-py 0.3.5 pip3 install taospy==2.7.16 -pip3 install taos-ws-py==0.3.3 +pip3 install taos-ws-py==0.3.5 $TIMEOUT_CMD $cmd RET=$? echo "cmd exit code: $RET" From 6461bb0474b8a534e9729ce12e8ada4dcd6697d6 Mon Sep 17 00:00:00 2001 From: yihaoDeng Date: Wed, 20 Nov 2024 17:03:13 +0800 Subject: [PATCH 49/88] unify the format of qId in the logging macros, --- source/libs/catalog/inc/catalogInt.h | 12 +++---- source/libs/qworker/inc/qwInt.h | 20 ++++++------ source/libs/scheduler/inc/schInt.h | 47 ++++++++++++++-------------- 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/source/libs/catalog/inc/catalogInt.h b/source/libs/catalog/inc/catalogInt.h index e757163ba8..919d967af8 100644 --- a/source/libs/catalog/inc/catalogInt.h +++ b/source/libs/catalog/inc/catalogInt.h @@ -831,12 +831,12 @@ typedef struct SCtgCacheItemInfo { #define ctgDebug(param, ...) qDebug("CTG:%p " param, pCtg, __VA_ARGS__) #define ctgTrace(param, ...) qTrace("CTG:%p " param, pCtg, __VA_ARGS__) -#define ctgTaskFatal(param, ...) qFatal("qid:%" PRIx64 " CTG:%p " param, pTask->pJob->queryId, pCtg, __VA_ARGS__) -#define ctgTaskError(param, ...) qError("qid:%" PRIx64 " CTG:%p " param, pTask->pJob->queryId, pCtg, __VA_ARGS__) -#define ctgTaskWarn(param, ...) qWarn("qid:%" PRIx64 " CTG:%p " param, pTask->pJob->queryId, pCtg, __VA_ARGS__) -#define ctgTaskInfo(param, ...) qInfo("qid:%" PRIx64 " CTG:%p " param, pTask->pJob->queryId, pCtg, __VA_ARGS__) -#define ctgTaskDebug(param, ...) qDebug("qid:%" PRIx64 " CTG:%p " param, pTask->pJob->queryId, pCtg, __VA_ARGS__) -#define ctgTaskTrace(param, ...) qTrace("qid:%" PRIx64 " CTG:%p " param, pTask->pJob->queryId, pCtg, __VA_ARGS__) +#define ctgTaskFatal(param, ...) qFatal("QID:%" PRIx64 " CTG:%p " param, pTask->pJob->queryId, pCtg, __VA_ARGS__) +#define ctgTaskError(param, ...) qError("QID:%" PRIx64 " CTG:%p " param, pTask->pJob->queryId, pCtg, __VA_ARGS__) +#define ctgTaskWarn(param, ...) qWarn("QID:%" PRIx64 " CTG:%p " param, pTask->pJob->queryId, pCtg, __VA_ARGS__) +#define ctgTaskInfo(param, ...) qInfo("QID:%" PRIx64 " CTG:%p " param, pTask->pJob->queryId, pCtg, __VA_ARGS__) +#define ctgTaskDebug(param, ...) qDebug("QID:%" PRIx64 " CTG:%p " param, pTask->pJob->queryId, pCtg, __VA_ARGS__) +#define ctgTaskTrace(param, ...) qTrace("QID:%" PRIx64 " CTG:%p " param, pTask->pJob->queryId, pCtg, __VA_ARGS__) #define CTG_LOCK_DEBUG(...) \ do { \ diff --git a/source/libs/qworker/inc/qwInt.h b/source/libs/qworker/inc/qwInt.h index 708c285aea..6d81baf91a 100644 --- a/source/libs/qworker/inc/qwInt.h +++ b/source/libs/qworker/inc/qwInt.h @@ -313,29 +313,29 @@ typedef struct SQWorkerMgmt { #define QW_SCH_DLOG(param, ...) qDebug("QW:%p SID:%" PRIx64 " " param, mgmt, sId, __VA_ARGS__) #define QW_TASK_ELOG(param, ...) \ - qError("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId, __VA_ARGS__) + qError("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId, __VA_ARGS__) #define QW_TASK_WLOG(param, ...) \ - qWarn("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId, __VA_ARGS__) + qWarn("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId, __VA_ARGS__) #define QW_TASK_DLOG(param, ...) \ - qDebug("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId, __VA_ARGS__) + qDebug("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId, __VA_ARGS__) #define QW_TASK_DLOGL(param, ...) \ - qDebugL("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId, __VA_ARGS__) + qDebugL("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId, __VA_ARGS__) #define QW_TASK_ELOG_E(param) \ - qError("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId) + qError("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId) #define QW_TASK_WLOG_E(param) \ - qWarn("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId) + qWarn("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId) #define QW_TASK_DLOG_E(param) \ - qDebug("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId) + qDebug("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId) #define QW_SCH_TASK_ELOG(param, ...) \ - qError("QW:%p SID:0x%" PRIx64 ",qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, mgmt, sId, \ + qError("QW:%p SID:0x%" PRIx64 ",QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, mgmt, sId, \ qId, cId, tId, eId, __VA_ARGS__) #define QW_SCH_TASK_WLOG(param, ...) \ - qWarn("QW:%p SID:0x%" PRIx64 ",qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, mgmt, sId, qId, \ + qWarn("QW:%p SID:0x%" PRIx64 ",QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, mgmt, sId, qId, \ cId, tId, eId, __VA_ARGS__) #define QW_SCH_TASK_DLOG(param, ...) \ - qDebug("QW:%p SID:0x%" PRIx64 ",qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, mgmt, sId, \ + qDebug("QW:%p SID:0x%" PRIx64 ",QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, mgmt, sId, \ qId, cId, tId, eId, __VA_ARGS__) #define QW_LOCK_DEBUG(...) \ diff --git a/source/libs/scheduler/inc/schInt.h b/source/libs/scheduler/inc/schInt.h index 6a910453f0..ef643852ea 100644 --- a/source/libs/scheduler/inc/schInt.h +++ b/source/libs/scheduler/inc/schInt.h @@ -62,7 +62,7 @@ typedef enum { #define SCH_DEFAULT_MAX_RETRY_NUM 6 #define SCH_MIN_AYSNC_EXEC_NUM 3 #define SCH_DEFAULT_RETRY_TOTAL_ROUND 3 -#define SCH_DEFAULT_TASK_CAPACITY_NUM 1000 +#define SCH_DEFAULT_TASK_CAPACITY_NUM 1000 typedef struct SSchDebug { bool lockEnable; @@ -333,12 +333,13 @@ extern SSchedulerMgmt schMgmt; #define SCH_UNLOCK_TASK(_task) SCH_UNLOCK(SCH_WRITE, &(_task)->lock) #define SCH_CLIENT_ID(_task) ((_task) ? (_task)->clientId : -1) -#define SCH_TASK_ID(_task) ((_task) ? (_task)->taskId : -1) -#define SCH_TASK_EID(_task) ((_task) ? (_task)->execId : -1) +#define SCH_TASK_ID(_task) ((_task) ? (_task)->taskId : -1) +#define SCH_TASK_EID(_task) ((_task) ? (_task)->execId : -1) #define SCH_IS_DATA_BIND_QRY_TASK(task) ((task)->plan->subplanType == SUBPLAN_TYPE_SCAN) -#define SCH_IS_DATA_BIND_PLAN(_plan) (((_plan)->subplanType == SUBPLAN_TYPE_SCAN) || ((_plan)->subplanType == SUBPLAN_TYPE_MODIFY)) -#define SCH_IS_DATA_BIND_TASK(task) SCH_IS_DATA_BIND_PLAN((task)->plan) +#define SCH_IS_DATA_BIND_PLAN(_plan) \ + (((_plan)->subplanType == SUBPLAN_TYPE_SCAN) || ((_plan)->subplanType == SUBPLAN_TYPE_MODIFY)) +#define SCH_IS_DATA_BIND_TASK(task) SCH_IS_DATA_BIND_PLAN((task)->plan) #define SCH_IS_LEAF_TASK(_job, _task) (((_task)->level->level + 1) == (_job)->levelNum) #define SCH_IS_DATA_MERGE_TASK(task) (!SCH_IS_DATA_BIND_TASK(task)) #define SCH_IS_LOCAL_EXEC_TASK(_job, _task) \ @@ -419,15 +420,15 @@ extern SSchedulerMgmt schMgmt; #define SCH_SWITCH_EPSET(_addr) ((_addr)->epSet.inUse = ((_addr)->epSet.inUse + 1) % (_addr)->epSet.numOfEps) #define SCH_TASK_NUM_OF_EPS(_addr) ((_addr)->epSet.numOfEps) -#define SCH_LOG_TASK_START_TS(_task) \ - do { \ - int64_t us = taosGetTimestampUs(); \ - if (NULL == taosArrayPush((_task)->profile.execTime, &us)) { \ - qError("taosArrayPush task execTime failed, error:%s", tstrerror(terrno)); \ - } \ - if (0 == (_task)->execId) { \ - (_task)->profile.startTs = us; \ - } \ +#define SCH_LOG_TASK_START_TS(_task) \ + do { \ + int64_t us = taosGetTimestampUs(); \ + if (NULL == taosArrayPush((_task)->profile.execTime, &us)) { \ + qError("taosArrayPush task execTime failed, error:%s", tstrerror(terrno)); \ + } \ + if (0 == (_task)->execId) { \ + (_task)->profile.startTs = us; \ + } \ } while (0) #define SCH_LOG_TASK_WAIT_TS(_task) \ @@ -450,23 +451,23 @@ extern SSchedulerMgmt schMgmt; (_task)->profile.endTs = us; \ } while (0) -#define SCH_JOB_ELOG(param, ...) qError("qid:0x%" PRIx64 " " param, pJob->queryId, __VA_ARGS__) -#define SCH_JOB_DLOG(param, ...) qDebug("qid:0x%" PRIx64 " " param, pJob->queryId, __VA_ARGS__) +#define SCH_JOB_ELOG(param, ...) qError("QID:0x%" PRIx64 " " param, pJob->queryId, __VA_ARGS__) +#define SCH_JOB_DLOG(param, ...) qDebug("QID:0x%" PRIx64 " " param, pJob->queryId, __VA_ARGS__) #define SCH_TASK_ELOG(param, ...) \ - qError("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ + qError("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), __VA_ARGS__) #define SCH_TASK_DLOG(param, ...) \ - qDebug("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ + qDebug("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), __VA_ARGS__) #define SCH_TASK_TLOG(param, ...) \ - qTrace("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ + qTrace("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), __VA_ARGS__) #define SCH_TASK_DLOGL(param, ...) \ - qDebugL("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ + qDebugL("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), __VA_ARGS__) #define SCH_TASK_WLOG(param, ...) \ - qWarn("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ + qWarn("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), __VA_ARGS__) #define SCH_SET_ERRNO(_err) \ @@ -580,7 +581,7 @@ int32_t schDelayLaunchTask(SSchJob *pJob, SSchTask *pTask); int32_t schBuildAndSendMsg(SSchJob *job, SSchTask *task, SQueryNodeAddr *addr, int32_t msgType, void *param); int32_t schAcquireJob(int64_t refId, SSchJob **ppJob); int32_t schReleaseJob(int64_t refId); -int32_t schReleaseJobEx(int64_t refId, int32_t* released); +int32_t schReleaseJobEx(int64_t refId, int32_t *released); void schFreeFlowCtrl(SSchJob *pJob); int32_t schChkJobNeedFlowCtrl(SSchJob *pJob, SSchLevel *pLevel); int32_t schDecTaskFlowQuota(SSchJob *pJob, SSchTask *pTask); @@ -648,7 +649,7 @@ void schDropTaskInHashList(SSchJob *pJob, SHashObj *list); int32_t schNotifyTaskInHashList(SSchJob *pJob, SHashObj *list, ETaskNotifyType type, SSchTask *pTask); int32_t schLaunchLevelTasks(SSchJob *pJob, SSchLevel *level); void schGetTaskFromList(SHashObj *pTaskList, uint64_t taskId, SSchTask **pTask); -int32_t schValidateSubplan(SSchJob *pJob, SSubplan* pSubplan, int32_t level, int32_t idx, int32_t taskNum); +int32_t schValidateSubplan(SSchJob *pJob, SSubplan *pSubplan, int32_t level, int32_t idx, int32_t taskNum); int32_t schInitTask(SSchJob *pJob, SSchTask *pTask, SSubplan *pPlan, SSchLevel *pLevel); int32_t schSwitchTaskCandidateAddr(SSchJob *pJob, SSchTask *pTask); void schDirectPostJobRes(SSchedulerReq *pReq, int32_t errCode); From da51741e66dc7992ac3d3edfe03500a4a5581c05 Mon Sep 17 00:00:00 2001 From: huohong <346479823@qq.com> Date: Wed, 20 Nov 2024 17:39:25 +0800 Subject: [PATCH 50/88] test:update 3.3.4.3 release notes in office web --- docs/en/28-releases/01-tdengine.md | 4 + docs/zh/28-releases/01-tdengine.md | 4 + docs/zh/28-releases/03-notes/3.3.4.3.md | 123 +++++++++++------------- docs/zh/28-releases/03-notes/index.md | 1 + 4 files changed, 66 insertions(+), 66 deletions(-) diff --git a/docs/en/28-releases/01-tdengine.md b/docs/en/28-releases/01-tdengine.md index b24931b166..161efe0d61 100644 --- a/docs/en/28-releases/01-tdengine.md +++ b/docs/en/28-releases/01-tdengine.md @@ -24,6 +24,10 @@ import Release from "/components/ReleaseV3"; +## 3.3.4.3 + + + ## 3.3.3.0 diff --git a/docs/zh/28-releases/01-tdengine.md b/docs/zh/28-releases/01-tdengine.md index cf9d7b6878..d285cb36ae 100644 --- a/docs/zh/28-releases/01-tdengine.md +++ b/docs/zh/28-releases/01-tdengine.md @@ -28,6 +28,10 @@ import Release from "/components/ReleaseV3"; +## 3.3.4.3 + + + ## 3.3.3.0 diff --git a/docs/zh/28-releases/03-notes/3.3.4.3.md b/docs/zh/28-releases/03-notes/3.3.4.3.md index 8ffd5802ed..364732b478 100644 --- a/docs/zh/28-releases/03-notes/3.3.4.3.md +++ b/docs/zh/28-releases/03-notes/3.3.4.3.md @@ -1,69 +1,60 @@ ---- -title: 3.3.4.3 版本说明 -sidebar_label: 3.3.4.3 -description: 3.3.4.3 版本说明 ---- +## 新特性 +* 新功能:流计算的 TWA 函数支持时间驱动的结果推送模式 +* 新功能:流计算的 Interp 函数支持时间驱动的结果推送模式 +* 优化:顺序执行 compact 和 split vgroup操作时的日志错误提示 +* 新功能:支持微软对象存储 -### 行为变更及兼容性 -1. 多副本流计算中必须使用 snode -1. 增加了流计算的兼容性保证机制,避免后续函数变更产生新的兼容性问题,但之前版本的流计算必须重建,具体参见 https://docs.taosdata.com/advanced/stream/#流计算升级故障恢复 -1. 调整 case when 语句结果类型的判断方法 - -### 新特性 -1. 新功能:流计算的 TWA 函数支持时间驱动的结果推送模式 -1. 新功能:流计算的 Interp 函数支持时间驱动的结果推送模式 -1. 新功能:支持微软对象存储 +## 优化 +* 优化:提升并发大查询时节点之间互相拉数据的效率 +* 优化:支持使用 AVX2 和 AVX512 对 double 、timestamp 和 bigint 类型进行解码优化 +* 优化:调整 case when 语句的结果类型判断方法 +* 优化:提升查询 “select ... from ... where ts in (...)” 的数据扫描速度 +* 优化:增加了流计算的兼容性保证机制,避免后续函数变更产生新的兼容性问题,之前版本的流计算必须重建 +* 优化:提升 taosX 在交叉写入场景下的数据同步性能 +* 优化:支持关闭整数/浮点数类型的编码 +* 优化:多副本流计算中必须使用 snode +* 优化:客户端生成唯一 ID 标识每一个查询任务,避免重复 ID 导致的内存损坏 +* 优化:加快数据库的创建时间 +* 优化:修改 s3MigrateEnabled 默认值为0 +* 优化:支持在审计数据库中记录删除操作 +* 优化:支持在指定的 dnode 中创建数据库 [企业版] +* 优化:调整删除超级表数据列时的报错信息 -### 优化 -1. 优化:提升并发大查询时节点之间互相拉数据的效率 -1. 优化:支持使用 AVX2 和 AVX512 对 double 、timestamp 和 bigint 类型进行解码优化 -1. 优化:调整 case when 语句的结果类型判断方法 -1. 优化:顺序执行 compact 和 split vgroup操作时的日志错误提示 -1. 优化:提升查询 “select ... from ... where ts in (...)” 的数据扫描速度 -1. 优化:增加了流计算的兼容性保证机制,避免后续函数变更产生新的兼容性问题,之前版本的流计算必须重建 -1. 优化:提升 taosX 在交叉写入场景下的数据同步性能 -1. 优化:支持关闭整数/浮点数类型的编码 -1. 优化:多副本流计算中必须使用 snode -1. 优化:客户端生成唯一 ID 标识每一个查询任务,避免重复 ID 导致的内存损坏 -1. 优化:加快数据库的创建时间 -1. 优化:修改 s3MigrateEnabled 默认值为0 -1. 优化:支持在审计数据库中记录删除操作 -1. 优化:支持在指定的 dnode 中创建数据库 [企业版] -1. 优化:调整删除超级表数据列时的报错信息 +## 修复 +* 修复:last_row 查询性能在 3.3.3.0 中大幅下降的问题 +* 修复:WAL 条目不完整时 taosd 无法启动的问题 +* 修复: partition by 常量时查询结果错误的问题 +* 修复:标量函数包含 _wstart 且填充方式为 prev 时计算结果错误 +* 修复:Windows 平台下的时区设置问题 +* 修复:空数据库进行 compact 操作时,事务无法结束【企业版】 +* 修复:事务冲突的逻辑错误 +* 修复:管理节点某些错误会导致事务无法停止 +* 修复:管理节点某些错误会导致事务无法停止 +* 修复:dnode 数据清空后 taosc 重试错误的问题 +* 修复:Data Compact 被异常终止后,中间文件未被清理 +* 修复:新增列后,Kafka 连接器的 earliest 模式消费不到新列数据 +* 修复:interp 函数在 fill(prev) 时行为不正确 +* 修复:TSMA 在高频元数据操作时异常停止的问题 +* 修复:show create stable 语句执行结果的标签显示错误 +* 修复:Percentile 函数在大数据量查询时会崩溃。 +* 修复:partition by 和 having 联合使用时的语法错误问题 +* 修复:interp 在 partition by tbname,c1 时 tbname 为空的问题 +* 修复:通过 stmt 写入非法布尔数值时 taosd 可能 crash +* 修复:Explorer OPC-UA 表名模板说明 +* 修复:库符号 version 与使用相同符号的库冲突的问题 https://github.com/taosdata/TDengine/issues/25920 +* 修复:在 windows 平台下 JDBC 驱动的句柄数持续升高问题 +* 修复:3.3.3.1 升级至 3.3.4.0 偶现的启动失败问题 +* 修复:Windows 平台重复增删表的内存泄漏 +* 修复:无法限制并发拉起 checkpoint 数量导致流计算消耗资源过多 +* 修复:并发查询时的 too many session 问题 +* 修复:Windows 平台下 taos shell 在慢查询场景中崩溃的问题 +* 修复:当打开 dnode日志时,加密数据库无法恢复的问题 +* 修复:由于 mnode 同步超时,进而导致 taosd 无法启动的问题 +* 修复:由于在快照同步过程中整理文件组数据的速度过慢,从而导致 Vnode(虚拟节点)无法恢复的问题 +* 修复:通过行协议向字符串类型的字段中写入带转义符的数据时,taosd 会崩溃 +* 修复:Error Code 逻辑处理错误导致的元数据文件损坏 +* 修复:查询语句中包含多个 “not” 条件语句嵌套时,未设置标量模式导致查询错误 +* 修复:vnode 统计信息上报超时导致的 dnode offline 问题 +* 修复:在不支持 avx 指令集的服务器上,taosd 启动失败问题 +* 修复:taosX 数据迁移容错处理 0x09xx 错误码 -### 修复 -1. 修复:last_row 查询性能在 3.3.3.0 中大幅下降的问题 -1. 修复:WAL 条目不完整时 taosd 无法启动的问题 -1. 修复: partition by 常量时查询结果错误的问题 -1. 修复:标量函数包含 _wstart 且填充方式为 prev 时计算结果错误 -1. 修复:Windows 平台下的时区设置问题 -1. 修复:空数据库进行 compact 操作时,事务无法结束【企业版】 -1. 修复:事务冲突的逻辑错误 -1. 修复:管理节点某些错误会导致事务无法停止 -1. 修复:管理节点某些错误会导致事务无法停止 -1. 修复:dnode 数据清空后 taosc 重试错误的问题 -1. 修复:Data Compact 被异常终止后,中间文件未被清理 -1. 修复:新增列后,Kafka 连接器的 earliest 模式消费不到新列数据 -1. 修复:interp 函数在 fill(prev) 时行为不正确 -1. 修复:TSMA 在高频元数据操作时异常停止的问题 -1. 修复:show create stable 语句执行结果的标签显示错误 -1. 修复:Percentile 函数在大数据量查询时会崩溃。 -1. 修复:partition by 和 having 联合使用时的语法错误问题 -1. 修复:interp 在 partition by tbname,c1 时 tbname 为空的问题 -1. 修复:通过 stmt 写入非法布尔数值时 taosd 可能 crash -1. 修复:库符号 version 与使用相同符号的库冲突的问题 -1. 修复:在 windows 平台下 JDBC 驱动的句柄数持续升高问题 -1. 修复:3.3.3.1 升级至 3.3.4.0 偶现的启动失败问题 -1. 修复:Windows 平台重复增删表的内存泄漏 -1. 修复:无法限制并发拉起 checkpoint 数量导致流计算消耗资源过多 -1. 修复:并发查询时的 too many session 问题 -1. 修复:Windows 平台下 taos shell 在慢查询场景中崩溃的问题 -1. 修复:当打开 dnode日志时,加密数据库无法恢复的问题 -1. 修复:由于 mnode 同步超时,进而导致 taosd 无法启动的问题 -1. 修复:由于在快照同步过程中整理文件组数据的速度过慢,从而导致 Vnode(虚拟节点)无法恢复的问题 -1. 修复:通过行协议向字符串类型的字段中写入带转义符的数据时,taosd 会崩溃 -1. 修复:Error Code 逻辑处理错误导致的元数据文件损坏 -1. 修复:查询语句中包含多个 “not” 条件语句嵌套时,未设置标量模式导致查询错误 -1. 修复:vnode 统计信息上报超时导致的 dnode offline 问题 -1. 修复:在不支持 avx 指令集的服务器上,taosd 启动失败问题 -1. 修复:taosX 数据迁移容错处理 0x09xx 错误码 diff --git a/docs/zh/28-releases/03-notes/index.md b/docs/zh/28-releases/03-notes/index.md index d1a48ab9a8..1cdd3239e7 100644 --- a/docs/zh/28-releases/03-notes/index.md +++ b/docs/zh/28-releases/03-notes/index.md @@ -4,6 +4,7 @@ sidebar_label: 版本说明 description: 各版本版本说明 --- +[3.3.4.3](./3.3.4.3) [3.3.4.3](./3.3.4.3) [3.3.3.0](./3.3.3.0) [3.3.2.0](./3.3.2.0) From 53ae0143544ecc506ee330e494417e47f55d05c7 Mon Sep 17 00:00:00 2001 From: 54liuyao <54liuyao@163.com> Date: Wed, 20 Nov 2024 19:13:05 +0800 Subject: [PATCH 51/88] fix issue for streamscan --- source/libs/executor/src/scanoperator.c | 31 ++++----- source/libs/stream/src/streamUpdate.c | 5 ++ .../stream/streamFwcIntervalCheckpoint.sim | 67 +++++++++++++++++++ 3 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 tests/script/tsim/stream/streamFwcIntervalCheckpoint.sim diff --git a/source/libs/executor/src/scanoperator.c b/source/libs/executor/src/scanoperator.c index eac4f87b14..7822aa9711 100644 --- a/source/libs/executor/src/scanoperator.c +++ b/source/libs/executor/src/scanoperator.c @@ -3410,6 +3410,8 @@ int32_t streamScanOperatorEncode(SStreamScanInfo* pInfo, void** pBuff, int32_t* QUERY_CHECK_CODE(code, lino, _end); } + qDebug("%s last scan range %d. %" PRId64 ",%" PRId64, __func__, __LINE__, pInfo->lastScanRange.skey, pInfo->lastScanRange.ekey); + *pLen = len; _end: @@ -3475,21 +3477,19 @@ void streamScanOperatorDecode(void* pBuff, int32_t len, SStreamScanInfo* pInfo) goto _end; } - if (pInfo->pUpdateInfo != NULL) { - void* pUpInfo = taosMemoryCalloc(1, sizeof(SUpdateInfo)); - if (!pUpInfo) { - lino = __LINE__; - goto _end; - } - code = pInfo->stateStore.updateInfoDeserialize(pDeCoder, pUpInfo); - if (code == TSDB_CODE_SUCCESS) { - pInfo->stateStore.updateInfoDestroy(pInfo->pUpdateInfo); - pInfo->pUpdateInfo = pUpInfo; - } else { - taosMemoryFree(pUpInfo); - lino = __LINE__; - goto _end; - } + void* pUpInfo = taosMemoryCalloc(1, sizeof(SUpdateInfo)); + if (!pUpInfo) { + lino = __LINE__; + goto _end; + } + code = pInfo->stateStore.updateInfoDeserialize(pDeCoder, pUpInfo); + if (code == TSDB_CODE_SUCCESS) { + pInfo->stateStore.updateInfoDestroy(pInfo->pUpdateInfo); + pInfo->pUpdateInfo = pUpInfo; + } else { + taosMemoryFree(pUpInfo); + lino = __LINE__; + goto _end; } if (tDecodeIsEnd(pDeCoder)) { @@ -3509,6 +3509,7 @@ void streamScanOperatorDecode(void* pBuff, int32_t len, SStreamScanInfo* pInfo) lino = __LINE__; goto _end; } + qDebug("%s last scan range %d. %" PRId64 ",%" PRId64, __func__, __LINE__, pInfo->lastScanRange.skey, pInfo->lastScanRange.ekey); _end: if (pDeCoder != NULL) { diff --git a/source/libs/stream/src/streamUpdate.c b/source/libs/stream/src/streamUpdate.c index a3cfa00127..1f1e4c8fbc 100644 --- a/source/libs/stream/src/streamUpdate.c +++ b/source/libs/stream/src/streamUpdate.c @@ -445,6 +445,7 @@ int32_t updateInfoSerialize(SEncoder* pEncoder, const SUpdateInfo* pInfo) { int32_t code = TSDB_CODE_SUCCESS; int32_t lino = 0; if (!pInfo) { + tEncodeI32(pEncoder, -1); return TSDB_CODE_SUCCESS; } @@ -550,6 +551,10 @@ int32_t updateInfoDeserialize(SDecoder* pDeCoder, SUpdateInfo* pInfo) { int32_t size = 0; if (tDecodeI32(pDeCoder, &size) < 0) return -1; + + if (size < 0) { + return -1; + } pInfo->pTsBuckets = taosArrayInit(size, sizeof(TSKEY)); QUERY_CHECK_NULL(pInfo->pTsBuckets, code, lino, _error, terrno); diff --git a/tests/script/tsim/stream/streamFwcIntervalCheckpoint.sim b/tests/script/tsim/stream/streamFwcIntervalCheckpoint.sim new file mode 100644 index 0000000000..ed72d87e9a --- /dev/null +++ b/tests/script/tsim/stream/streamFwcIntervalCheckpoint.sim @@ -0,0 +1,67 @@ +system sh/stop_dnodes.sh +system sh/deploy.sh -n dnode1 -i 1 + +system sh/cfg.sh -n dnode1 -c checkpointInterval -v 60 + +system sh/exec.sh -n dnode1 -s start +sleep 50 +sql connect + +print step1 +print =============== create database +sql create database test vgroups 4; +sql use test; + +sql create stable st(ts timestamp, a int, b int , c int)tags(ta int,tb int,tc int); +sql create table t1 using st tags(1,1,1); +sql create table t2 using st tags(2,2,2); + +sql create stream streams1 trigger force_window_close IGNORE EXPIRED 1 IGNORE UPDATE 1 into streamt1 as select _wstart, count(a) from st partition by tbname interval(2s); +sql create stream streams2 trigger at_once IGNORE EXPIRED 0 IGNORE UPDATE 0 into streamt2 as select _wstart, count(a) from st interval(2s); + +run tsim/stream/checkTaskStatus.sim + +sleep 70000 + + +print restart taosd 01 ...... + +system sh/stop_dnodes.sh + +system sh/exec.sh -n dnode1 -s start + +run tsim/stream/checkTaskStatus.sim + +sql insert into t1 values(now + 3000a,1,1,1); + +$loop_count = 0 +loop0: + +sleep 2000 + +$loop_count = $loop_count + 1 +if $loop_count == 20 then + return -1 +endi + +print select * from streamt1; +sql select * from streamt1; + +print $data00 $data01 $data02 + +if $rows == 0 then + goto loop0 +endi + +print select * from streamt2; +sql select * from streamt2; + +print $data00 $data01 $data02 + +if $rows == 0 then + goto loop0 +endi + +print end + +system sh/exec.sh -n dnode1 -s stop -x SIGINT From 5ace9b1d90e8023bcc14aac69391f558eb2979a1 Mon Sep 17 00:00:00 2001 From: 54liuyao <54liuyao@163.com> Date: Wed, 20 Nov 2024 19:33:46 +0800 Subject: [PATCH 52/88] fix issue --- source/libs/stream/src/streamUpdate.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/libs/stream/src/streamUpdate.c b/source/libs/stream/src/streamUpdate.c index 1f1e4c8fbc..ff11850d7c 100644 --- a/source/libs/stream/src/streamUpdate.c +++ b/source/libs/stream/src/streamUpdate.c @@ -445,7 +445,10 @@ int32_t updateInfoSerialize(SEncoder* pEncoder, const SUpdateInfo* pInfo) { int32_t code = TSDB_CODE_SUCCESS; int32_t lino = 0; if (!pInfo) { - tEncodeI32(pEncoder, -1); + if (tEncodeI32(pEncoder, -1) < 0) { + code = TSDB_CODE_FAILED; + QUERY_CHECK_CODE(code, lino, _end); + } return TSDB_CODE_SUCCESS; } From 6f0279d6038e5b72c4aa3363b71738fe37603f53 Mon Sep 17 00:00:00 2001 From: xiao-77 Date: Wed, 20 Nov 2024 19:46:08 +0800 Subject: [PATCH 53/88] Enh modify wal print fileInfo from info to trace. --- source/libs/wal/src/walMeta.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/libs/wal/src/walMeta.c b/source/libs/wal/src/walMeta.c index da26ddae3a..ce2b9218b5 100644 --- a/source/libs/wal/src/walMeta.c +++ b/source/libs/wal/src/walMeta.c @@ -415,10 +415,10 @@ static void printFileSet(int32_t vgId, SArray* fileSet, const char* str) { int32_t sz = taosArrayGetSize(fileSet); for (int32_t i = 0; i < sz; i++) { SWalFileInfo* pFileInfo = taosArrayGet(fileSet, i); - wInfo("vgId:%d, %s-%d, firstVer:%" PRId64 ", lastVer:%" PRId64 ", fileSize:%" PRId64 ", syncedOffset:%" PRId64 - ", createTs:%" PRId64 ", closeTs:%" PRId64, - vgId, str, i, pFileInfo->firstVer, pFileInfo->lastVer, pFileInfo->fileSize, pFileInfo->syncedOffset, - pFileInfo->createTs, pFileInfo->closeTs); + wTrace("vgId:%d, %s-%d, firstVer:%" PRId64 ", lastVer:%" PRId64 ", fileSize:%" PRId64 ", syncedOffset:%" PRId64 + ", createTs:%" PRId64 ", closeTs:%" PRId64, + vgId, str, i, pFileInfo->firstVer, pFileInfo->lastVer, pFileInfo->fileSize, pFileInfo->syncedOffset, + pFileInfo->createTs, pFileInfo->closeTs); } } From 78d956bf9e7d643534e4cc0ae2d5b247ba692658 Mon Sep 17 00:00:00 2001 From: haoranchen Date: Wed, 20 Nov 2024 20:38:27 +0800 Subject: [PATCH 54/88] Update Jenkinsfile2 --- Jenkinsfile2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile2 b/Jenkinsfile2 index 7ba9b4a933..18ce2fde23 100644 --- a/Jenkinsfile2 +++ b/Jenkinsfile2 @@ -5,7 +5,7 @@ node { } file_zh_changed = '' file_en_changed = '' -file_no_doc_changed = '' +file_no_doc_changed = '1' def abortPreviousBuilds() { def currentJobName = env.JOB_NAME def currentBuildNumber = env.BUILD_NUMBER.toInteger() @@ -655,4 +655,4 @@ pipeline { ) } } -} \ No newline at end of file +} From 6464077cec68505240e758c07e9783f96a35b39a Mon Sep 17 00:00:00 2001 From: 54liuyao <54liuyao@163.com> Date: Thu, 21 Nov 2024 09:50:45 +0800 Subject: [PATCH 55/88] add log --- source/libs/executor/src/scanoperator.c | 5 +++-- source/libs/stream/src/streamUpdate.c | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/libs/executor/src/scanoperator.c b/source/libs/executor/src/scanoperator.c index 7822aa9711..095d7e1a2b 100644 --- a/source/libs/executor/src/scanoperator.c +++ b/source/libs/executor/src/scanoperator.c @@ -3486,10 +3486,11 @@ void streamScanOperatorDecode(void* pBuff, int32_t len, SStreamScanInfo* pInfo) if (code == TSDB_CODE_SUCCESS) { pInfo->stateStore.updateInfoDestroy(pInfo->pUpdateInfo); pInfo->pUpdateInfo = pUpInfo; + qDebug("%s line:%d. stream scan updateinfo deserialize success", __func__, __LINE__); } else { taosMemoryFree(pUpInfo); - lino = __LINE__; - goto _end; + code = TSDB_CODE_SUCCESS; + qDebug("%s line:%d. stream scan did not have updateinfo", __func__, __LINE__); } if (tDecodeIsEnd(pDeCoder)) { diff --git a/source/libs/stream/src/streamUpdate.c b/source/libs/stream/src/streamUpdate.c index ff11850d7c..49d5041369 100644 --- a/source/libs/stream/src/streamUpdate.c +++ b/source/libs/stream/src/streamUpdate.c @@ -449,6 +449,7 @@ int32_t updateInfoSerialize(SEncoder* pEncoder, const SUpdateInfo* pInfo) { code = TSDB_CODE_FAILED; QUERY_CHECK_CODE(code, lino, _end); } + uDebug("%s line:%d. it did not have updateinfo", __func__, __LINE__); return TSDB_CODE_SUCCESS; } From 5ebb44903e944ec799b7e7c4886d78bed80cf462 Mon Sep 17 00:00:00 2001 From: Shengliang Guan Date: Thu, 21 Nov 2024 10:30:18 +0800 Subject: [PATCH 56/88] Revert "Update release note for 3.3.4.3 in office web" --- docs/en/28-releases/01-tdengine.md | 4 - docs/zh/28-releases/01-tdengine.md | 4 - docs/zh/28-releases/03-notes/3.3.4.3.md | 123 +++++++++++++----------- docs/zh/28-releases/03-notes/index.md | 1 - 4 files changed, 66 insertions(+), 66 deletions(-) diff --git a/docs/en/28-releases/01-tdengine.md b/docs/en/28-releases/01-tdengine.md index 161efe0d61..b24931b166 100644 --- a/docs/en/28-releases/01-tdengine.md +++ b/docs/en/28-releases/01-tdengine.md @@ -24,10 +24,6 @@ import Release from "/components/ReleaseV3"; -## 3.3.4.3 - - - ## 3.3.3.0 diff --git a/docs/zh/28-releases/01-tdengine.md b/docs/zh/28-releases/01-tdengine.md index d285cb36ae..cf9d7b6878 100644 --- a/docs/zh/28-releases/01-tdengine.md +++ b/docs/zh/28-releases/01-tdengine.md @@ -28,10 +28,6 @@ import Release from "/components/ReleaseV3"; -## 3.3.4.3 - - - ## 3.3.3.0 diff --git a/docs/zh/28-releases/03-notes/3.3.4.3.md b/docs/zh/28-releases/03-notes/3.3.4.3.md index 364732b478..8ffd5802ed 100644 --- a/docs/zh/28-releases/03-notes/3.3.4.3.md +++ b/docs/zh/28-releases/03-notes/3.3.4.3.md @@ -1,60 +1,69 @@ -## 新特性 -* 新功能:流计算的 TWA 函数支持时间驱动的结果推送模式 -* 新功能:流计算的 Interp 函数支持时间驱动的结果推送模式 -* 优化:顺序执行 compact 和 split vgroup操作时的日志错误提示 -* 新功能:支持微软对象存储 +--- +title: 3.3.4.3 版本说明 +sidebar_label: 3.3.4.3 +description: 3.3.4.3 版本说明 +--- -## 优化 -* 优化:提升并发大查询时节点之间互相拉数据的效率 -* 优化:支持使用 AVX2 和 AVX512 对 double 、timestamp 和 bigint 类型进行解码优化 -* 优化:调整 case when 语句的结果类型判断方法 -* 优化:提升查询 “select ... from ... where ts in (...)” 的数据扫描速度 -* 优化:增加了流计算的兼容性保证机制,避免后续函数变更产生新的兼容性问题,之前版本的流计算必须重建 -* 优化:提升 taosX 在交叉写入场景下的数据同步性能 -* 优化:支持关闭整数/浮点数类型的编码 -* 优化:多副本流计算中必须使用 snode -* 优化:客户端生成唯一 ID 标识每一个查询任务,避免重复 ID 导致的内存损坏 -* 优化:加快数据库的创建时间 -* 优化:修改 s3MigrateEnabled 默认值为0 -* 优化:支持在审计数据库中记录删除操作 -* 优化:支持在指定的 dnode 中创建数据库 [企业版] -* 优化:调整删除超级表数据列时的报错信息 +### 行为变更及兼容性 +1. 多副本流计算中必须使用 snode +1. 增加了流计算的兼容性保证机制,避免后续函数变更产生新的兼容性问题,但之前版本的流计算必须重建,具体参见 https://docs.taosdata.com/advanced/stream/#流计算升级故障恢复 +1. 调整 case when 语句结果类型的判断方法 + +### 新特性 +1. 新功能:流计算的 TWA 函数支持时间驱动的结果推送模式 +1. 新功能:流计算的 Interp 函数支持时间驱动的结果推送模式 +1. 新功能:支持微软对象存储 -## 修复 -* 修复:last_row 查询性能在 3.3.3.0 中大幅下降的问题 -* 修复:WAL 条目不完整时 taosd 无法启动的问题 -* 修复: partition by 常量时查询结果错误的问题 -* 修复:标量函数包含 _wstart 且填充方式为 prev 时计算结果错误 -* 修复:Windows 平台下的时区设置问题 -* 修复:空数据库进行 compact 操作时,事务无法结束【企业版】 -* 修复:事务冲突的逻辑错误 -* 修复:管理节点某些错误会导致事务无法停止 -* 修复:管理节点某些错误会导致事务无法停止 -* 修复:dnode 数据清空后 taosc 重试错误的问题 -* 修复:Data Compact 被异常终止后,中间文件未被清理 -* 修复:新增列后,Kafka 连接器的 earliest 模式消费不到新列数据 -* 修复:interp 函数在 fill(prev) 时行为不正确 -* 修复:TSMA 在高频元数据操作时异常停止的问题 -* 修复:show create stable 语句执行结果的标签显示错误 -* 修复:Percentile 函数在大数据量查询时会崩溃。 -* 修复:partition by 和 having 联合使用时的语法错误问题 -* 修复:interp 在 partition by tbname,c1 时 tbname 为空的问题 -* 修复:通过 stmt 写入非法布尔数值时 taosd 可能 crash -* 修复:Explorer OPC-UA 表名模板说明 -* 修复:库符号 version 与使用相同符号的库冲突的问题 https://github.com/taosdata/TDengine/issues/25920 -* 修复:在 windows 平台下 JDBC 驱动的句柄数持续升高问题 -* 修复:3.3.3.1 升级至 3.3.4.0 偶现的启动失败问题 -* 修复:Windows 平台重复增删表的内存泄漏 -* 修复:无法限制并发拉起 checkpoint 数量导致流计算消耗资源过多 -* 修复:并发查询时的 too many session 问题 -* 修复:Windows 平台下 taos shell 在慢查询场景中崩溃的问题 -* 修复:当打开 dnode日志时,加密数据库无法恢复的问题 -* 修复:由于 mnode 同步超时,进而导致 taosd 无法启动的问题 -* 修复:由于在快照同步过程中整理文件组数据的速度过慢,从而导致 Vnode(虚拟节点)无法恢复的问题 -* 修复:通过行协议向字符串类型的字段中写入带转义符的数据时,taosd 会崩溃 -* 修复:Error Code 逻辑处理错误导致的元数据文件损坏 -* 修复:查询语句中包含多个 “not” 条件语句嵌套时,未设置标量模式导致查询错误 -* 修复:vnode 统计信息上报超时导致的 dnode offline 问题 -* 修复:在不支持 avx 指令集的服务器上,taosd 启动失败问题 -* 修复:taosX 数据迁移容错处理 0x09xx 错误码 +### 优化 +1. 优化:提升并发大查询时节点之间互相拉数据的效率 +1. 优化:支持使用 AVX2 和 AVX512 对 double 、timestamp 和 bigint 类型进行解码优化 +1. 优化:调整 case when 语句的结果类型判断方法 +1. 优化:顺序执行 compact 和 split vgroup操作时的日志错误提示 +1. 优化:提升查询 “select ... from ... where ts in (...)” 的数据扫描速度 +1. 优化:增加了流计算的兼容性保证机制,避免后续函数变更产生新的兼容性问题,之前版本的流计算必须重建 +1. 优化:提升 taosX 在交叉写入场景下的数据同步性能 +1. 优化:支持关闭整数/浮点数类型的编码 +1. 优化:多副本流计算中必须使用 snode +1. 优化:客户端生成唯一 ID 标识每一个查询任务,避免重复 ID 导致的内存损坏 +1. 优化:加快数据库的创建时间 +1. 优化:修改 s3MigrateEnabled 默认值为0 +1. 优化:支持在审计数据库中记录删除操作 +1. 优化:支持在指定的 dnode 中创建数据库 [企业版] +1. 优化:调整删除超级表数据列时的报错信息 +### 修复 +1. 修复:last_row 查询性能在 3.3.3.0 中大幅下降的问题 +1. 修复:WAL 条目不完整时 taosd 无法启动的问题 +1. 修复: partition by 常量时查询结果错误的问题 +1. 修复:标量函数包含 _wstart 且填充方式为 prev 时计算结果错误 +1. 修复:Windows 平台下的时区设置问题 +1. 修复:空数据库进行 compact 操作时,事务无法结束【企业版】 +1. 修复:事务冲突的逻辑错误 +1. 修复:管理节点某些错误会导致事务无法停止 +1. 修复:管理节点某些错误会导致事务无法停止 +1. 修复:dnode 数据清空后 taosc 重试错误的问题 +1. 修复:Data Compact 被异常终止后,中间文件未被清理 +1. 修复:新增列后,Kafka 连接器的 earliest 模式消费不到新列数据 +1. 修复:interp 函数在 fill(prev) 时行为不正确 +1. 修复:TSMA 在高频元数据操作时异常停止的问题 +1. 修复:show create stable 语句执行结果的标签显示错误 +1. 修复:Percentile 函数在大数据量查询时会崩溃。 +1. 修复:partition by 和 having 联合使用时的语法错误问题 +1. 修复:interp 在 partition by tbname,c1 时 tbname 为空的问题 +1. 修复:通过 stmt 写入非法布尔数值时 taosd 可能 crash +1. 修复:库符号 version 与使用相同符号的库冲突的问题 +1. 修复:在 windows 平台下 JDBC 驱动的句柄数持续升高问题 +1. 修复:3.3.3.1 升级至 3.3.4.0 偶现的启动失败问题 +1. 修复:Windows 平台重复增删表的内存泄漏 +1. 修复:无法限制并发拉起 checkpoint 数量导致流计算消耗资源过多 +1. 修复:并发查询时的 too many session 问题 +1. 修复:Windows 平台下 taos shell 在慢查询场景中崩溃的问题 +1. 修复:当打开 dnode日志时,加密数据库无法恢复的问题 +1. 修复:由于 mnode 同步超时,进而导致 taosd 无法启动的问题 +1. 修复:由于在快照同步过程中整理文件组数据的速度过慢,从而导致 Vnode(虚拟节点)无法恢复的问题 +1. 修复:通过行协议向字符串类型的字段中写入带转义符的数据时,taosd 会崩溃 +1. 修复:Error Code 逻辑处理错误导致的元数据文件损坏 +1. 修复:查询语句中包含多个 “not” 条件语句嵌套时,未设置标量模式导致查询错误 +1. 修复:vnode 统计信息上报超时导致的 dnode offline 问题 +1. 修复:在不支持 avx 指令集的服务器上,taosd 启动失败问题 +1. 修复:taosX 数据迁移容错处理 0x09xx 错误码 diff --git a/docs/zh/28-releases/03-notes/index.md b/docs/zh/28-releases/03-notes/index.md index 1cdd3239e7..d1a48ab9a8 100644 --- a/docs/zh/28-releases/03-notes/index.md +++ b/docs/zh/28-releases/03-notes/index.md @@ -4,7 +4,6 @@ sidebar_label: 版本说明 description: 各版本版本说明 --- -[3.3.4.3](./3.3.4.3) [3.3.4.3](./3.3.4.3) [3.3.3.0](./3.3.3.0) [3.3.2.0](./3.3.2.0) From d4c99244d58323bfb5b9f6eddbe5b8a0a2683f86 Mon Sep 17 00:00:00 2001 From: eryue1993 <93116359+eryue1993@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:04:20 +0800 Subject: [PATCH 57/88] Update 02-cache.md --- docs/zh/06-advanced/02-cache.md | 72 +++++++++++---------------------- 1 file changed, 24 insertions(+), 48 deletions(-) diff --git a/docs/zh/06-advanced/02-cache.md b/docs/zh/06-advanced/02-cache.md index 065adbf50a..875452205b 100644 --- a/docs/zh/06-advanced/02-cache.md +++ b/docs/zh/06-advanced/02-cache.md @@ -1,68 +1,44 @@ --- -sidebar_label: 数据缓存 -title: 数据缓存 +sidebar_label: 读缓存 +title: 读缓存 toc_max_heading_level: 4 --- -在工业互联网和物联网大数据应用场景中,时序数据库的性能表现尤为关键。这类应用程序不仅要求数据的实时写入能力,还需求能够迅速获取设备的最新状态或对最新数据进行实时计算。通常,大数据平台会通过部署 Redis 或类似的缓存技术来满足这些需求。然而,这种做法会增加系统的复杂性和运营成本。 +在物联网(IoT)和工业互联网(IIoT)大数据应用场景中,实时数据的价值往往远超历史数据。企业不仅需要数据处理系统具备高效的实时写入能力,更需要能快速获取设备的最新状态,或者对最新数据进行实时计算和分析。无论是工业设备的状态监控、车联网中的车辆位置追踪,还是智能仪表的实时读数,当前值都是业务运行中不可或缺的核心数据。这些数据直接关系到生产安全、运营效率以及用户体验。 -为了解决这一问题,TDengine 采用了针对性的缓存优化策略。通过精心设计的缓存机制,TDengine 实现了数据的实时高效写入和快速查询,从而有效降低整个集群的复杂性和运营成本。这种优化不仅提升了性能,还为用户带来了更简洁、易用的解决方案,使他们能够更专注于核心业务的发展。 +例如,在工业生产中,生产线设备的当前运行状态至关重要。操作员需要实时监控温度、压力、转速等关键指标,一旦设备出现异常,这些数据必须即时呈现,以便迅速调整工艺参数,避免停产或更大的损失。在车联网领域,以滴滴为例,车辆的实时位置数据是滴滴平台优化派单策略、提升运营效率的关键,确保每位乘客快速上车并享受更高质量的出行体验。 -## 写缓存 +同时,看板系统和智能仪表作为现场操作和用户端的窗口,也需要实时数据支撑。无论是工厂管理者通过看板获取的实时生产指标,还是家庭用户随时查询智能水表、电表的用量,实时性不仅影响到运营和决策效率,更直接关系到用户对服务的满意程度。 -TDengine 采用了一种创新的时间驱动缓存管理策略,亦称为写驱动的缓存管理机制。这一策略与传统的读驱动的缓存模式有所不同,其核心思想是将最新写入的数据优先保存在缓存中。当缓存容量达到预设的临界值时,系统会将最早存储的数据批量写入硬盘,从而实现缓存与硬盘之间的动态平衡。 +## 传统缓存方案的局限性 -在物联网数据应用中,用户往往最关注最近产生的数据,即设备的当前状态。TDengine 充分利用了这一业务特性,将最近到达的当前状态数据优先存储在缓存中,以便用户能够快速获取所需信息。 +为了满足这些高频实时查询需求,许多企业选择将 Redis 等缓存技术集成到大数据平台中,通过在数据库和应用之间添加一层缓存来提升查询性能。然而,这种方法也带来了不少问题: +- 系统复杂性增加:需要额外部署和维护缓存集群,对系统架构提出了更高的要求。 +- 运营成本上升:需要额外的硬件资源来支撑缓存,增加了维护和管理的开销。 +- 一致性问题:缓存和数据库之间的数据同步需要额外的机制来保障,否则可能出现数据不一致的情况。 -为了实现数据的分布式存储和高可用性,TDengine 引入了虚拟节点(vnode)的概念。每个 vnode 可以拥有多达 3 个副本,这些副本共同组成一个 vnode group,简称 vgroup。在创建数据库时,用户需要确定每个 vnode 的写入缓存大小,以确保数据的合理分配和高效存储。 +## TDengine 的解决方案:内置读缓存 -创建数据库时的两个关键参数 `vgroups` 和 `buffer` 分别决定了数据库中的数据由多少个 vgroup 进行处理,以及为每个 vnode 分配多少写入缓存。通过合理配置这两个 -参数,用户可以根据实际需求调整数据库的性能和存储容量,从而实现最佳的性能和成本效益。 +为了解决这些问题,TDengine 针对物联网和工业互联网的高频实时查询场景,设计并实现了读缓存机制。这一机制能够自动将每张表的最后一条记录缓存到内存中,从而在不引入第三方缓存技术的情况下,直接满足用户对当前值的实时查询需求。 -例 如, 下面的 SQL 创建了包含 10 个 vgroup,每个 vnode 占 用 256MB 内存的数据库。 -```sql -CREATE DATABASE POWER VGROUPS 10 BUFFER 256 CACHEMODEL 'NONE' PAGES 128 PAGESIZE 16; -``` +TDengine 采用时间驱动的缓存管理策略,将最新数据优先存储在缓存中,查询时无需访问硬盘即可快速返回结果。当缓存容量达到设定上限时,系统会批量将最早的数据写入硬盘,既提升了查询效率,也有效减少了硬盘的写入负担,延长硬件使用寿命。 -缓存越大越好,但超过一定阈值后再增加缓存对写入性能提升并无帮助。 +用户可通过设置 cachemodel 参数,自定义缓存模式,包括缓存最新一行数据、每列最近的非 NULL 值,或同时缓存行和列的数据。这种灵活设计在物联网场景中尤为重要,使设备状态的实时查询更加高效精准。 -## 读缓存 +这种读缓存机制的内置化设计显著降低了查询延迟,避免了引入 Redis 等外部系统的复杂性和运维成本。同时,减少了频繁查询对存储系统的压力,大幅提升系统的整体吞吐能力,确保在高并发场景下依然稳定高效运行。通过读缓存,TDengine 为用户提供了一种更轻量化的实时数据处理方案,不仅优化了查询性能,还降低了整体运维成本,为物联网和工业互联网用户提供强有力的技术支持。 -在创建数据库时,用户可以选择是否启用缓存机制以存储该数据库中每张子表的最新数据。这一缓存机制由数据库创建参数 cachemodel 进行控制。参数 cachemodel 具有如 -下 4 种情况: -- none: 不缓存 -- last_row: 缓存子表最近一行数据,这将显著改善 last_row 函数的性能 -- last_value: 缓存子表每一列最近的非 NULL 值,这将显著改善无特殊影响(比如 WHERE, ORDER BY, GROUP BY, INTERVAL)时的 last 函数的性能 -- both: 同时缓存最近的行和列,即等同于上述 cachemodel 值为 last_row 和 last_value 的行为同时生效 +## TDengine 的读缓存配置 + +在创建数据库时,用户可以选择是否启用缓存机制以存储该数据库中每张子表的最新数据。这一缓存机制由数据库创建参数 cachemodel 进行控制。参数 cachemodel 具有如 下 4 种情况: +- none:不缓存 +- last_row:缓存子表最近一行数据,这将显著改善 last_row 函数的性能 +- last_value:缓存子表每一列最近的非 NULL 值,这将显著改善无特殊影响(比如 WHERE,ORDER BY,GROUP BY, INTERVAL)时的 last 函数的性能 +- both:同时缓存最近的行和列,即等同于上述 cachemodel 值为 last_row 和 last_value 的行为同时生效 当使用数据库读缓存时,可以使用参数 cachesize 来配置每个 vnode 的内存大小。 -- cachesize:表示每个 vnode 中用于缓存子表最近数据的内存大小。默认为 1 ,范围是[1, 65536],单位是 MB。需要根据机器内存合理配置。 +- cachesize:表示每个 vnode 中用于缓存子表最近数据的内存大小。默认为 1 ,范围是[1,65536],单位是 MB。需要根据机器内存合理配置。 -## 元数据缓存 - -为了提升查询和写入操作的效率,每个 vnode 都配备了缓存机制,用于存储其曾经获取过的元数据。这一元数据缓存的大小由创建数据库时的两个参数 pages 和 pagesize 共同决定。其中,pagesize 参数的单位是 KB,用于指定每个缓存页的大小。如下 SQL 会为数据库 power 的每个 vnode 创建 128 个 page、每个 page 16KB 的元数据缓存 - -```sql -CREATE DATABASE POWER PAGES 128 PAGESIZE 16; -``` - -## 文件系统缓存 - -TDengine 采用 WAL 技术作为基本的数据可靠性保障手段。WAL 是一种先进的数据保护机制,旨在确保在发生故障时能够迅速恢复数据。其核心原理在于,在数据实际写入数据存储层之前,先将其变更记录到一个日志文件中。这样一来,即便集群遭遇崩溃或其他故障,也能确保数据安全无损。 - -TDengine 利用这些日志文件实现故障前的状态恢复。在写入 WAL 的过程中,数据是以顺序追加的方式写入硬盘文件的。因此,文件系统缓存在此过程中发挥着关键作用,对写入性能产生显著影响。为了确保数据真正落盘,系统会调用 fsync 函数,该函数负责将文件系统缓存中的数据强制写入硬盘。 - -数据库参数 wal_level 和 wal_fsync_period 共同决定了 WAL 的保存行为。。 -- wal_level:此参数控制 WAL 的保存级别。级别 1 表示仅将数据写入 WAL,但不立即执行 fsync 函数;级别 2 则表示在写入 WAL 的同时执行 fsync 函数。默认情况下,wal_level 设为 1。虽然执行 fsync 函数可以提高数据的持久性,但相应地也会降低写入性能。 -- wal_fsync_period:当 wal_level 设置为 2 时,这个参数控制执行 fsync 的频率。设置为 0 表示每次写入后立即执行 fsync,这可以确保数据的安全性,但可能会牺牲一些性能。当设置为大于 0 的数值时,表示 fsync 周期,默认为 3000,范围是[1, 180000],单位毫秒。 - -```sql -CREATE DATABASE POWER WAL_LEVEL 2 WAL_FSYNC_PERIOD 3000; -``` - -在创建数据库时可以选择不同的参数类型,来选择性能优先或者可靠性优先。 -- 1: 写 WAL 但不执行 fsync ,新写入 WAL 的数据保存在文件系统缓存中但并未写入磁盘,这种方式性能优先 -- 2: 写 WAL 且执行 fsync,新写入 WAL 的数据被立即同步到磁盘上,可靠性更高 +关于数据库的具体创建,相关参数和操作说明请参考[创建数据库](../../reference/taos-sql/database/) ## 实时数据查询的缓存实践 From 2077c9e5617a97ba71380ecb370a74b95e0ce6fb Mon Sep 17 00:00:00 2001 From: factosea <285808407@qq.com> Date: Thu, 21 Nov 2024 14:20:08 +0800 Subject: [PATCH 58/88] fix: group by value --- source/libs/executor/src/executorInt.c | 1 + source/libs/parser/src/parCalcConst.c | 16 +++++++++---- tests/system-test/2-query/project_group.py | 27 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/source/libs/executor/src/executorInt.c b/source/libs/executor/src/executorInt.c index 1b823bf69d..eb49c90035 100644 --- a/source/libs/executor/src/executorInt.c +++ b/source/libs/executor/src/executorInt.c @@ -359,6 +359,7 @@ static int32_t doSetInputDataBlock(SExprSupp* pExprSup, SSDataBlock* pBlock, int SFunctParam* pFuncParam = &pOneExpr->base.pParam[j]; if (pFuncParam->type == FUNC_PARAM_TYPE_COLUMN) { int32_t slotId = pFuncParam->pCol->slotId; + QUERY_CHECK_CONDITION((slotId < pBlock->pDataBlock->size), code, lino, _end, TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR); pInput->pData[j] = taosArrayGet(pBlock->pDataBlock, slotId); pInput->totalRows = pBlock->info.rows; pInput->numOfRows = pBlock->info.rows; diff --git a/source/libs/parser/src/parCalcConst.c b/source/libs/parser/src/parCalcConst.c index 9702c2e760..a2e98bece7 100644 --- a/source/libs/parser/src/parCalcConst.c +++ b/source/libs/parser/src/parCalcConst.c @@ -329,16 +329,22 @@ static int32_t calcConstGroupBy(SCalcConstContext* pCxt, SSelectStmt* pSelect) { if (TSDB_CODE_SUCCESS == code) { SNode* pNode = NULL; FOREACH(pNode, pSelect->pGroupByList) { + bool hasNotValue = false; SNode* pGroupPara = NULL; FOREACH(pGroupPara, ((SGroupingSetNode*)pNode)->pParameterList) { if (QUERY_NODE_VALUE != nodeType(pGroupPara)) { - return code; + hasNotValue = true; + break; + } + } + if (!hasNotValue) { + if (pSelect->hasAggFuncs) { + ERASE_NODE(pSelect->pGroupByList); + } else { + if (!cell->pPrev && !cell->pNext) continue; + ERASE_NODE(pSelect->pGroupByList); } } - } - FOREACH(pNode, pSelect->pGroupByList) { - if (!cell->pPrev) continue; - ERASE_NODE(pSelect->pGroupByList); } } return code; diff --git a/tests/system-test/2-query/project_group.py b/tests/system-test/2-query/project_group.py index 19fe8b1cf0..a251854213 100644 --- a/tests/system-test/2-query/project_group.py +++ b/tests/system-test/2-query/project_group.py @@ -15,6 +15,30 @@ class TDTestCase: self.batchNum = 5 self.ts = 1537146000000 + def groupby_value(self): + tdSql.query('select 1 from stb group by now') + tdSql.checkRows(1) + tdSql.checkData(0, 0, 1) + tdSql.query('select 1 from stb group by "1"') + tdSql.checkRows(1) + tdSql.checkData(0, 0, 1) + tdSql.query('select count(*) from stb group by now') + tdSql.checkRows(1) + tdSql.checkData(0, 0, 12) + tdSql.query('select count(*) from stb group by now+1') + tdSql.checkRows(1) + tdSql.checkData(0, 0, 12) + tdSql.query('select 1, count(*) from stb group by now, "1"') + tdSql.checkRows(1) + tdSql.checkData(0, 0, 1) + tdSql.checkData(0, 1, 12) + tdSql.query('select count(*) as cc from sta1 as a join sta2 as b on a.ts = b.ts group by now') + tdSql.checkRows(1) + tdSql.checkData(0, 0, 3) + tdSql.query('select a.tbname, count(*) as cc from sta1 as a join sta2 as b on a.ts = b.ts group by a.tbname, "1"') + tdSql.checkRows(1) + tdSql.checkData(0, 1, 3) + def run(self): dbname = "db" tdSql.prepare() @@ -59,6 +83,9 @@ class TDTestCase: tdSql.checkRows(2) tdSql.query('select col1 > 0 and col2 > 0 from stb') tdSql.checkRows(12) + + self.groupby_value() + def stop(self): tdSql.close() tdLog.success("%s successfully executed" % __file__) From 8bd9f3c2df0163b14f70a1082376a12eaedb7837 Mon Sep 17 00:00:00 2001 From: eryue1993 <93116359+eryue1993@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:34:42 +0800 Subject: [PATCH 59/88] Create 10-cache add new page for cache --- docs/zh/26-tdinternal/10-cache | 62 ++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 docs/zh/26-tdinternal/10-cache diff --git a/docs/zh/26-tdinternal/10-cache b/docs/zh/26-tdinternal/10-cache new file mode 100644 index 0000000000..543bec555a --- /dev/null +++ b/docs/zh/26-tdinternal/10-cache @@ -0,0 +1,62 @@ +--- +sidebar_label: 数据缓存 +title: 数据订阅 +toc_max_heading_level: 4 +--- +在现代物联网(IoT)和工业互联网(IIoT)应用中,数据的高效管理对系统性能和用户体验至关重要。为了应对高并发环境下的实时读写需求,TDengine 设计了一套完整的缓存机制,包括写缓存、读缓存、元数据缓存和文件系统缓存。这些缓存机制紧密结合,既能优化数据查询的响应速度,又能提高数据写入的效率,同时保障数据的可靠性和系统的高可用性。通过灵活配置缓存参数,TDengine 为用户提供了性能与成本之间的最佳平衡。 + +## 写缓存 + +TDengine 采用了一种创新的时间驱动缓存管理策略,亦称为写驱动的缓存管理机制。这一策略与传统的读驱动的缓存模式有所不同,其核心思想是将最新写入的数据优先保存在缓存中。当缓存容量达到预设的临界值时,系统会将最早存储的数据批量写入硬盘,从而实现缓存与硬盘之间的动态平衡。 + +在物联网数据应用中,用户往往最关注最近产生的数据,即设备的当前状态。TDengine 充分利用了这一业务特性,将最近到达的当前状态数据优先存储在缓存中,以便用户能够快速获取所需信息。 + +为了实现数据的分布式存储和高可用性,TDengine 引入了虚拟节点(vnode)的概念。每个 vnode 可以拥有多达 3 个副本,这些副本共同组成一个 vnode group,简称 vgroup。在创建数据库时,用户需要确定每个 vnode 的写入缓存大小,以确保数据的合理分配和高效存储。 + +创建数据库时的两个关键参数 `vgroups` 和 `buffer` 分别决定了数据库中的数据由多少个 vgroup 进行处理,以及为每个 vnode 分配多少写入缓存。通过合理配置这两个 +参数,用户可以根据实际需求调整数据库的性能和存储容量,从而实现最佳的性能和成本效益。 + +例 如, 下面的 SQL 创建了包含 10 个 vgroup,每个 vnode 占 用 256MB 内存的数据库。 +```sql +CREATE DATABASE POWER VGROUPS 10 BUFFER 256 CACHEMODEL 'NONE' PAGES 128 PAGESIZE 16; +``` + +缓存越大越好,但超过一定阈值后再增加缓存对写入性能提升并无帮助。 + +## 读缓存 + +TDengine 的读缓存机制专为高频实时查询场景设计,尤其适用于物联网和工业互联网等需要实时掌握设备状态的业务场景。在这些场景中,用户往往最关心最新的数据,如设备的当前读数或状态。 + +通过设置 cachemodel 参数,TDengine 用户可以灵活选择适合的缓存模式,包括缓存最新一行数据、每列最近的非 NULL 值,或同时缓存行和列的数据。这种灵活性使 TDengine 能根据具体业务需求提供精准优化,在物联网场景下尤为突出,助力用户快速访问设备的最新状态。 + +这种设计不仅降低了查询的响应延迟,还能有效缓解存储系统的 I/O 压力。在高并发场景下,读缓存能够帮助系统维持更高的吞吐量,确保查询性能的稳定性。借助 TDengine 读缓存,用户无需再集成如 Redis 一类的外部缓存系统,避免了系统架构的复杂化,显著降低运维和部署成本。 + +此外,TDengine 的读缓存机制还能够根据实际业务场景灵活调整。在数据访问热点集中在最新记录的场景中,这种内置缓存能够显著提高用户体验,让关键数据的获取更加快速高效。相比传统缓存方案,这种无缝集成的缓存策略不仅简化了开发流程,还为用户提供了更高的性能保障。 + +关于 TDengine 读缓存的更多详细内容请看[读缓存](../../advanced/cache/) + +## 元数据缓存 + +为了提升查询和写入操作的效率,每个 vnode 都配备了缓存机制,用于存储其曾经获取过的元数据。这一元数据缓存的大小由创建数据库时的两个参数 pages 和 pagesize 共同决定。其中,pagesize 参数的单位是 KB,用于指定每个缓存页的大小。如下 SQL 会为数据库 power 的每个 vnode 创建 128 个 page、每个 page 16KB 的元数据缓存 + +```sql +CREATE DATABASE POWER PAGES 128 PAGESIZE 16; +``` + +## 文件系统缓存 + +TDengine 采用 WAL 技术作为基本的数据可靠性保障手段。WAL 是一种先进的数据保护机制,旨在确保在发生故障时能够迅速恢复数据。其核心原理在于,在数据实际写入数据存储层之前,先将其变更记录到一个日志文件中。这样一来,即便集群遭遇崩溃或其他故障,也能确保数据安全无损。 + +TDengine 利用这些日志文件实现故障前的状态恢复。在写入 WAL 的过程中,数据是以顺序追加的方式写入硬盘文件的。因此,文件系统缓存在此过程中发挥着关键作用,对写入性能产生显著影响。为了确保数据真正落盘,系统会调用 fsync 函数,该函数负责将文件系统缓存中的数据强制写入硬盘。 + +数据库参数 wal_level 和 wal_fsync_period 共同决定了 WAL 的保存行为。。 +- wal_level:此参数控制 WAL 的保存级别。级别 1 表示仅将数据写入 WAL,但不立即执行 fsync 函数;级别 2 则表示在写入 WAL 的同时执行 fsync 函数。默认情况下,wal_level 设为 1。虽然执行 fsync 函数可以提高数据的持久性,但相应地也会降低写入性能。 +- wal_fsync_period:当 wal_level 设置为 2 时,这个参数控制执行 fsync 的频率。设置为 0 则表示每次写入后立即执行 fsync,这可以确保数据的安全性,但可能会牺牲一些性能。当设置为大于 0 的数值时,则表示 fsync 周期,默认为 3000,范围是[1, 180000],单位毫秒。 + +```sql +CREATE DATABASE POWER WAL_LEVEL 2 WAL_FSYNC_PERIOD 3000; +``` + +在创建数据库时,用户可以根据需求选择不同的参数设置,以在性能和可靠性之间找到最佳平衡: +- 性能优先:将数据写入 WAL,但不立即执行 fsync 操作,此时新写入的数据仅保存在文件系统缓存中,尚未同步到磁盘。这种配置能够显著提高写入性能。 +- 可靠性优先:将数据写入 WAL 的同时执行 fsync 操作,将数据立即同步到磁盘,确保数据持久化,可靠性更高。 From bd9b20d30fd77b84da7299b2229ff11ff4c61a5a Mon Sep 17 00:00:00 2001 From: facetosea <25808407@qq.com> Date: Thu, 21 Nov 2024 14:44:27 +0800 Subject: [PATCH 60/88] delete unused code --- source/libs/executor/src/executorInt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/libs/executor/src/executorInt.c b/source/libs/executor/src/executorInt.c index eb49c90035..1b823bf69d 100644 --- a/source/libs/executor/src/executorInt.c +++ b/source/libs/executor/src/executorInt.c @@ -359,7 +359,6 @@ static int32_t doSetInputDataBlock(SExprSupp* pExprSup, SSDataBlock* pBlock, int SFunctParam* pFuncParam = &pOneExpr->base.pParam[j]; if (pFuncParam->type == FUNC_PARAM_TYPE_COLUMN) { int32_t slotId = pFuncParam->pCol->slotId; - QUERY_CHECK_CONDITION((slotId < pBlock->pDataBlock->size), code, lino, _end, TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR); pInput->pData[j] = taosArrayGet(pBlock->pDataBlock, slotId); pInput->totalRows = pBlock->info.rows; pInput->numOfRows = pBlock->info.rows; From 2a26f48a1b21cdfe0e96ee99ed91d720c5021f45 Mon Sep 17 00:00:00 2001 From: yihaoDeng Date: Thu, 21 Nov 2024 14:59:51 +0800 Subject: [PATCH 61/88] fix double compress when retry --- source/libs/transport/inc/transComm.h | 1 + source/libs/transport/src/transCli.c | 30 +- source/libs/transport/src/transComm.c | 32 ++ tests/parallel_test/cases.task | 1 + tests/script/tsim/mnode/basic6.sim | 413 ++++++++++++++++++++++++++ 5 files changed, 472 insertions(+), 5 deletions(-) create mode 100644 tests/script/tsim/mnode/basic6.sim diff --git a/source/libs/transport/inc/transComm.h b/source/libs/transport/inc/transComm.h index 5c79b379ed..c11128149e 100644 --- a/source/libs/transport/inc/transComm.h +++ b/source/libs/transport/inc/transComm.h @@ -452,6 +452,7 @@ void transPrintEpSet(SEpSet* pEpSet); void transFreeMsg(void* msg); int32_t transCompressMsg(char* msg, int32_t len); int32_t transDecompressMsg(char** msg, int32_t* len); +int32_t transDecompressMsgExt(char const* msg, int32_t len, char** out, int32_t* outLen); int32_t transOpenRefMgt(int size, void (*func)(void*)); void transCloseRefMgt(int32_t refMgt); diff --git a/source/libs/transport/src/transCli.c b/source/libs/transport/src/transCli.c index c03d3418fa..e2507f5381 100644 --- a/source/libs/transport/src/transCli.c +++ b/source/libs/transport/src/transCli.c @@ -1334,13 +1334,31 @@ static void cliBatchSendCb(uv_write_t* req, int status) { } } bool cliConnMayAddUserInfo(SCliConn* pConn, STransMsgHead** ppHead, int32_t* msgLen) { + int32_t code = 0; SCliThrd* pThrd = pConn->hostThrd; STrans* pInst = pThrd->pInst; if (pConn->userInited == 1) { return false; } STransMsgHead* pHead = *ppHead; - STransMsgHead* tHead = taosMemoryCalloc(1, *msgLen + sizeof(pInst->user)); + int32_t len = *msgLen; + char* oriMsg = NULL; + int32_t oriLen = 0; + + if (pHead->comp == 1) { + int32_t msgLen = htonl(pHead->msgLen); + code = transDecompressMsgExt((char*)(pHead), msgLen, &oriMsg, &oriLen); + if (code < 0) { + tError("failed to decompress since %s", tstrerror(code)); + return false; + } else { + tDebug("decompress msg and resent, compress size %d, raw size %d", pHead, oriMsg, msgLen, oriLen); + } + + pHead = (STransMsgHead*)oriMsg; + len = oriLen; + } + STransMsgHead* tHead = taosMemoryCalloc(1, len + sizeof(pInst->user)); if (tHead == NULL) { return false; } @@ -1348,14 +1366,17 @@ bool cliConnMayAddUserInfo(SCliConn* pConn, STransMsgHead** ppHead, int32_t* msg memcpy((char*)tHead + TRANS_MSG_OVERHEAD, pInst->user, sizeof(pInst->user)); memcpy((char*)tHead + TRANS_MSG_OVERHEAD + sizeof(pInst->user), (char*)pHead + TRANS_MSG_OVERHEAD, - *msgLen - TRANS_MSG_OVERHEAD); + len - TRANS_MSG_OVERHEAD); tHead->withUserInfo = 1; *ppHead = tHead; - *msgLen += sizeof(pInst->user); + *msgLen = len + sizeof(pInst->user); pConn->pInitUserReq = tHead; pConn->userInited = 1; + if (oriMsg != NULL) { + taosMemoryFree(oriMsg); + } return true; } int32_t cliBatchSend(SCliConn* pConn, int8_t direct) { @@ -1421,9 +1442,8 @@ int32_t cliBatchSend(SCliConn* pConn, int8_t direct) { pReq->contLen = 0; } - int32_t msgLen = transMsgLenFromCont(pReq->contLen); - STransMsgHead* pHead = transHeadFromCont(pReq->pCont); + int32_t msgLen = transMsgLenFromCont(pReq->contLen); char* content = pReq->pCont; int32_t contLen = pReq->contLen; diff --git a/source/libs/transport/src/transComm.c b/source/libs/transport/src/transComm.c index 66bd4a08f3..c0edcd54e4 100644 --- a/source/libs/transport/src/transComm.c +++ b/source/libs/transport/src/transComm.c @@ -77,6 +77,11 @@ int32_t transDecompressMsg(char** msg, int32_t* len) { STransMsgHead* pNewHead = (STransMsgHead*)buf; int32_t decompLen = LZ4_decompress_safe(pCont + sizeof(STransCompMsg), (char*)pNewHead->content, tlen - sizeof(STransMsgHead) - sizeof(STransCompMsg), oriLen); + + if (decompLen != oriLen) { + taosMemoryFree(buf); + return TSDB_CODE_INVALID_MSG; + } memcpy((char*)pNewHead, (char*)pHead, sizeof(STransMsgHead)); *len = oriLen + sizeof(STransMsgHead); @@ -84,9 +89,36 @@ int32_t transDecompressMsg(char** msg, int32_t* len) { taosMemoryFree(pHead); *msg = buf; + return 0; +} +int32_t transDecompressMsgExt(char const* msg, int32_t len, char** out, int32_t* outLen) { + STransMsgHead* pHead = (STransMsgHead*)msg; + char* pCont = transContFromHead(pHead); + + STransCompMsg* pComp = (STransCompMsg*)pCont; + int32_t oriLen = htonl(pComp->contLen); + + int32_t tlen = len; + char* buf = taosMemoryCalloc(1, oriLen + sizeof(STransMsgHead)); + if (buf == NULL) { + return terrno; + } + + STransMsgHead* pNewHead = (STransMsgHead*)buf; + int32_t decompLen = LZ4_decompress_safe(pCont + sizeof(STransCompMsg), (char*)pNewHead->content, + tlen - sizeof(STransMsgHead) - sizeof(STransCompMsg), oriLen); if (decompLen != oriLen) { + tError("msgLen:%d, originLen:%d, decompLen:%d", len, oriLen, decompLen); + taosMemoryFree(buf); return TSDB_CODE_INVALID_MSG; } + memcpy((char*)pNewHead, (char*)pHead, sizeof(STransMsgHead)); + + *out = buf; + *outLen = oriLen + sizeof(STransMsgHead); + pNewHead->msgLen = *outLen; + pNewHead->comp = 0; + return 0; } diff --git a/tests/parallel_test/cases.task b/tests/parallel_test/cases.task index 29233bd56b..492dd11177 100644 --- a/tests/parallel_test/cases.task +++ b/tests/parallel_test/cases.task @@ -1300,6 +1300,7 @@ #,,y,script,./test.sh -f tsim/mnode/basic3.sim ,,y,script,./test.sh -f tsim/mnode/basic4.sim ,,y,script,./test.sh -f tsim/mnode/basic5.sim +,,y,script,./test.sh -f tsim/mnode/basic6.sim ,,y,script,./test.sh -f tsim/show/basic.sim ,,y,script,./test.sh -f tsim/table/autocreate.sim ,,y,script,./test.sh -f tsim/table/basic1.sim diff --git a/tests/script/tsim/mnode/basic6.sim b/tests/script/tsim/mnode/basic6.sim new file mode 100644 index 0000000000..4ee56ff555 --- /dev/null +++ b/tests/script/tsim/mnode/basic6.sim @@ -0,0 +1,413 @@ +system sh/stop_dnodes.sh +system sh/deploy.sh -n dnode1 -i 1 +system sh/deploy.sh -n dnode2 -i 2 +system sh/deploy.sh -n dnode3 -i 3 +system sh/deploy.sh -n dnode4 -i 4 +system sh/cfg.sh -n dnode1 -c compressMsgSize -v 0 +system sh/cfg.sh -n dnode2 -c compressMsgSize -v 0 +system sh/cfg.sh -n dnode3 -c compressMsgSize -v 0 +system sh/cfg.sh -n dnode4 -c compressMsgSize -v 0 +system sh/exec.sh -n dnode1 -s start +sql connect + +print =============== step1: create dnodes +sql create dnode $hostname port 7200 +sql create dnode $hostname port 7300 +sql create dnode $hostname port 7400 + +$x = 0 +step1: + $x = $x + 1 + sleep 1000 + if $x == 5 then + return -1 + endi +sql select * from information_schema.ins_dnodes +if $data(1)[4] != ready then + goto step1 +endi + +print =============== step2: create dnodes - with error +sql_error create mnode on dnode 1; +sql_error create mnode on dnode 2; +sql_error create mnode on dnode 3; +sql_error create mnode on dnode 4; +sql_error create mnode on dnode 5; +sql_error create mnode on dnode 6; + +print =============== step3: create mnode 2 and 3 +system sh/exec.sh -n dnode2 -s start +system sh/exec.sh -n dnode3 -s start +system sh/exec.sh -n dnode4 -s start +$x = 0 +step3: + $x = $x + 1 + sleep 1000 + if $x == 5 then + return -1 + endi +sql select * from information_schema.ins_dnodes +if $data(2)[4] != ready then + goto step3 +endi +if $data(3)[4] != ready then + goto step3 +endi +if $data(4)[4] != ready then + goto step3 +endi + +sql create mnode on dnode 2 +sql create mnode on dnode 3 + +$x = 0 +step31: + $x = $x + 1 + sleep 1000 + if $x == 50 then + return -1 + endi +sql select * from information_schema.ins_mnodes +$leaderNum = 0 +if $data(1)[2] == leader then + $leaderNum = 1 +endi +if $data(2)[2] == leader then + $leaderNum = 1 +endi +if $data(3)[2] == leader then + $leaderNum = 1 +endi +if $leaderNum == 0 then + goto step31 +endi + +print =============== step4: create dnodes - with error +sql_error create mnode on dnode 1 +sql_error create mnode on dnode 2; +sql_error create mnode on dnode 3; +sql_error create mnode on dnode 4; +sql_error create mnode on dnode 5; +sql_error create mnode on dnode 6; + +print =============== step5: drop mnodes - with error +sql_error drop mnode on dnode 1 +sql_error drop mnode on dnode 4 +sql_error drop mnode on dnode 5 +sql_error drop mnode on dnode 6 + +system sh/exec.sh -n dnode2 -s stop +$x = 0 +step5: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi +sql select * from information_schema.ins_dnodes +print ===> $data00 $data01 $data02 $data03 $data04 $data05 +print ===> $data10 $data11 $data12 $data13 $data14 $data15 +print ===> $data20 $data21 $data22 $data23 $data24 $data25 +print ===> $data30 $data31 $data32 $data33 $data34 $data35 +if $data(1)[4] != ready then + goto step5 +endi +if $data(2)[4] != offline then + goto step5 +endi +if $data(3)[4] != ready then + goto step5 +endi +if $data(4)[4] != ready then + goto step5 +endi + +sql_error drop mnode on dnode 2 + +system sh/exec.sh -n dnode2 -s start +$x = 0 +step51: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi +sql select * from information_schema.ins_dnodes +print ===> $data00 $data01 $data02 $data03 $data04 $data05 +print ===> $data10 $data11 $data12 $data13 $data14 $data15 +print ===> $data20 $data21 $data22 $data23 $data24 $data25 +print ===> $data30 $data31 $data32 $data33 $data34 $data35 +if $data(1)[4] != ready then + goto step51 +endi +if $data(2)[4] != ready then + goto step51 +endi +if $data(3)[4] != ready then + goto step51 +endi +if $data(4)[4] != ready then + goto step51 +endi + +print =============== step6: stop mnode1 +system sh/exec.sh -n dnode1 -s stop +# sql_error drop mnode on dnode 1 + +$x = 0 +step61: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi +sql select * from information_schema.ins_mnodes -x step61 +print ===> $data00 $data01 $data02 $data03 $data04 $data05 +print ===> $data10 $data11 $data12 $data13 $data14 $data15 +print ===> $data20 $data21 $data22 $data23 $data24 $data25 +$leaderNum = 0 +if $data(2)[2] == leader then + $leaderNum = 1 +endi +if $data(3)[2] == leader then + $leaderNum = 1 +endi +if $leaderNum != 1 then + goto step61 +endi + +print =============== step7: start mnode1 and wait it online +system sh/exec.sh -n dnode1 -s start + +$x = 0 +step71: + $x = $x + 1 + sleep 1000 + if $x == 50 then + return -1 + endi +sql select * from information_schema.ins_dnodes +print ===> $data00 $data01 $data02 $data03 $data04 $data05 +print ===> $data10 $data11 $data12 $data13 $data14 $data15 +print ===> $data20 $data21 $data22 $data23 $data24 $data25 +print ===> $data30 $data31 $data32 $data33 $data34 $data35 +if $data(1)[4] != ready then + goto step71 +endi +if $data(2)[4] != ready then + goto step71 +endi +if $data(3)[4] != ready then + goto step71 +endi +if $data(4)[4] != ready then + goto step71 +endi + +print =============== step8: stop mnode1 and drop it +system sh/exec.sh -n dnode1 -s stop + +$x = 0 +step81: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi +sql select * from information_schema.ins_mnodes +print ===> $data00 $data01 $data02 $data03 $data04 $data05 +print ===> $data10 $data11 $data12 $data13 $data14 $data15 +print ===> $data20 $data21 $data22 $data23 $data24 $data25 +$leaderNum = 0 +if $data(1)[2] == leader then + $leaderNum = 1 +endi +if $data(2)[2] == leader then + $leaderNum = 1 +endi +if $data(3)[2] == leader then + $leaderNum = 1 +endi +if $leaderNum != 1 then + goto step81 +endi + +print =============== step9: start mnode1 and wait it dropped +print check mnode has leader step9a +$x = 0 +step9a: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi +print check mnode leader +sql select * from information_schema.ins_mnodes +print ===> $data00 $data01 $data02 $data03 $data04 $data05 +print ===> $data10 $data11 $data12 $data13 $data14 $data15 +print ===> $data20 $data21 $data22 $data23 $data24 $data25 +$leaderNum = 0 +if $data(1)[2] == leader then + $leaderNum = 1 +endi +if $data(2)[2] == leader then + $leaderNum = 1 +endi +if $data(3)[2] == leader then + $leaderNum = 1 +endi +if $leaderNum != 1 then + goto step9a +endi + +print start dnode1 step9b +system sh/exec.sh -n dnode1 -s start +$x = 0 +step9b: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi +print check dnode1 ready +sql select * from information_schema.ins_dnodes +print ===> $data00 $data01 $data02 $data03 $data04 $data05 +print ===> $data10 $data11 $data12 $data13 $data14 $data15 +print ===> $data20 $data21 $data22 $data23 $data24 $data25 +print ===> $data30 $data31 $data32 $data33 $data34 $data35 +if $data(1)[4] != ready then + goto step9b +endi +if $data(2)[4] != ready then + goto step9b +endi +if $data(3)[4] != ready then + goto step9b +endi +if $data(4)[4] != ready then + goto step9b +endi + +sleep 4000 +print check mnode has leader step9c +$x = 0 +step9c: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi +print check mnode leader +sql select * from information_schema.ins_mnodes +print ===> $data00 $data01 $data02 $data03 $data04 $data05 +print ===> $data10 $data11 $data12 $data13 $data14 $data15 +print ===> $data20 $data21 $data22 $data23 $data24 $data25 +$leaderNum = 0 +if $data(1)[2] == leader then + $leaderNum = 1 +endi +if $data(2)[2] == leader then + $leaderNum = 1 +endi +if $data(3)[2] == leader then + $leaderNum = 1 +endi +if $leaderNum != 1 then + goto step9c +endi + +print drop mnode step9d +sql drop mnode on dnode 1 + +$x = 0 +step9d: + $x = $x + 1 + sleep 1000 + if $x == 20 then + return -1 + endi +print check mnode leader +sql select * from information_schema.ins_mnodes +print ===> $data00 $data01 $data02 $data03 $data04 $data05 +print ===> $data10 $data11 $data12 $data13 $data14 $data15 +print ===> $data20 $data21 $data22 $data23 $data24 $data25 +$leaderNum = 0 +if $data(1)[2] == leader then + $leaderNum = 1 +endi +if $data(2)[2] == leader then + $leaderNum = 1 +endi +if $data(3)[2] == leader then + $leaderNum = 1 +endi +if $leaderNum != 1 then + goto step9d +endi +if $rows != 2 then + goto step9d +endi + +print =============== stepa: create mnode1 again +sql create mnode on dnode 1 +$x = 0 +stepa: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi +sql select * from information_schema.ins_mnodes +print ===> $data00 $data01 $data02 $data03 $data04 $data05 +print ===> $data10 $data11 $data12 $data13 $data14 $data15 +print ===> $data20 $data21 $data22 $data23 $data24 $data25 +$leaderNum = 0 +if $data(1)[2] == leader then + $leaderNum = 1 +endi +if $data(2)[2] == leader then + $leaderNum = 1 +endi +if $data(3)[2] == leader then + $leaderNum = 1 +endi +if $leaderNum == 0 then + goto stepa +endi +if $leaderNum != 1 then + return -1 +endi + +$x = 0 +stepb: + $x = $x + 1 + sleep 1000 + if $x == 10 then + print ====> dnode not ready! + return -1 + endi +sql select * from information_schema.ins_dnodes +print ===> $data00 $data01 $data02 $data03 $data04 $data05 +print ===> $data10 $data11 $data12 $data13 $data14 $data15 +print ===> $data20 $data21 $data22 $data23 $data24 $data25 +print ===> $data30 $data31 $data32 $data33 $data34 $data35 +if $rows != 4 then + return -1 +endi +if $data(1)[4] != ready then + goto stepb +endi +if $data(2)[4] != ready then + goto stepb +endi +if $data(3)[4] != ready then + goto stepb +endi +if $data(4)[4] != ready then + goto stepb +endi + +system sh/exec.sh -n dnode1 -s stop +system sh/exec.sh -n dnode2 -s stop +system sh/exec.sh -n dnode3 -s stop +system sh/exec.sh -n dnode4 -s stop From af1c0c98bb05d480f0c05e2a2c32e901bfbbddc7 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Thu, 21 Nov 2024 15:14:37 +0800 Subject: [PATCH 62/88] fix(analytics): do some internal refactor and fix the error in parse the option string. --- include/common/tanalytics.h | 38 ++-- include/libs/nodes/cmdnodes.h | 2 +- include/libs/nodes/plannodes.h | 4 +- include/libs/nodes/querynodes.h | 2 +- include/util/taoserror.h | 15 +- include/util/tdef.h | 13 +- source/common/src/systable.c | 6 +- source/common/src/tmsg.c | 6 +- source/dnode/mnode/impl/src/mndAnode.c | 26 +-- .../libs/executor/src/anomalywindowoperator.c | 93 +++++---- source/libs/executor/src/forecastoperator.c | 113 ++++++----- source/libs/parser/src/parAstCreater.c | 2 +- source/libs/parser/src/parTranslater.c | 2 +- source/util/src/tanalytics.c | 183 +++++++++--------- source/util/src/terror.c | 15 +- 15 files changed, 274 insertions(+), 246 deletions(-) diff --git a/include/common/tanalytics.h b/include/common/tanalytics.h index 85eb963129..d0af84ecfb 100644 --- a/include/common/tanalytics.h +++ b/include/common/tanalytics.h @@ -39,14 +39,14 @@ typedef struct { } SAnalyticsUrl; typedef enum { - ANAL_BUF_TYPE_JSON = 0, - ANAL_BUF_TYPE_JSON_COL = 1, - ANAL_BUF_TYPE_OTHERS, + ANALYTICS_BUF_TYPE_JSON = 0, + ANALYTICS_BUF_TYPE_JSON_COL = 1, + ANALYTICS_BUF_TYPE_OTHERS, } EAnalBufType; typedef enum { - ANAL_HTTP_TYPE_GET = 0, - ANAL_HTTP_TYPE_POST, + ANALYTICS_HTTP_TYPE_GET = 0, + ANALYTICS_HTTP_TYPE_POST, } EAnalHttpType; typedef struct { @@ -61,11 +61,11 @@ typedef struct { char fileName[TSDB_FILENAME_LEN]; int32_t numOfCols; SAnalyticsColBuf *pCols; -} SAnalBuf; +} SAnalyticBuf; int32_t taosAnalyticsInit(); void taosAnalyticsCleanup(); -SJson *taosAnalSendReqRetJson(const char *url, EAnalHttpType type, SAnalBuf *pBuf); +SJson *taosAnalSendReqRetJson(const char *url, EAnalHttpType type, SAnalyticBuf *pBuf); int32_t taosAnalGetAlgoUrl(const char *algoName, EAnalAlgoType type, char *url, int32_t urlLen); bool taosAnalGetOptStr(const char *option, const char *optName, char *optValue, int32_t optMaxLen); @@ -73,18 +73,18 @@ bool taosAnalGetOptInt(const char *option, const char *optName, int64_t *optV int64_t taosAnalGetVersion(); void taosAnalUpdate(int64_t newVer, SHashObj *pHash); -int32_t tsosAnalBufOpen(SAnalBuf *pBuf, int32_t numOfCols); -int32_t taosAnalBufWriteOptStr(SAnalBuf *pBuf, const char *optName, const char *optVal); -int32_t taosAnalBufWriteOptInt(SAnalBuf *pBuf, const char *optName, int64_t optVal); -int32_t taosAnalBufWriteOptFloat(SAnalBuf *pBuf, const char *optName, float optVal); -int32_t taosAnalBufWriteColMeta(SAnalBuf *pBuf, int32_t colIndex, int32_t colType, const char *colName); -int32_t taosAnalBufWriteDataBegin(SAnalBuf *pBuf); -int32_t taosAnalBufWriteColBegin(SAnalBuf *pBuf, int32_t colIndex); -int32_t taosAnalBufWriteColData(SAnalBuf *pBuf, int32_t colIndex, int32_t colType, void *colValue); -int32_t taosAnalBufWriteColEnd(SAnalBuf *pBuf, int32_t colIndex); -int32_t taosAnalBufWriteDataEnd(SAnalBuf *pBuf); -int32_t taosAnalBufClose(SAnalBuf *pBuf); -void taosAnalBufDestroy(SAnalBuf *pBuf); +int32_t tsosAnalBufOpen(SAnalyticBuf *pBuf, int32_t numOfCols); +int32_t taosAnalBufWriteOptStr(SAnalyticBuf *pBuf, const char *optName, const char *optVal); +int32_t taosAnalBufWriteOptInt(SAnalyticBuf *pBuf, const char *optName, int64_t optVal); +int32_t taosAnalBufWriteOptFloat(SAnalyticBuf *pBuf, const char *optName, float optVal); +int32_t taosAnalBufWriteColMeta(SAnalyticBuf *pBuf, int32_t colIndex, int32_t colType, const char *colName); +int32_t taosAnalBufWriteDataBegin(SAnalyticBuf *pBuf); +int32_t taosAnalBufWriteColBegin(SAnalyticBuf *pBuf, int32_t colIndex); +int32_t taosAnalBufWriteColData(SAnalyticBuf *pBuf, int32_t colIndex, int32_t colType, void *colValue); +int32_t taosAnalBufWriteColEnd(SAnalyticBuf *pBuf, int32_t colIndex); +int32_t taosAnalBufWriteDataEnd(SAnalyticBuf *pBuf); +int32_t taosAnalBufClose(SAnalyticBuf *pBuf); +void taosAnalBufDestroy(SAnalyticBuf *pBuf); const char *taosAnalAlgoStr(EAnalAlgoType algoType); EAnalAlgoType taosAnalAlgoInt(const char *algoName); diff --git a/include/libs/nodes/cmdnodes.h b/include/libs/nodes/cmdnodes.h index 0b617c7ce3..867f8c8efc 100644 --- a/include/libs/nodes/cmdnodes.h +++ b/include/libs/nodes/cmdnodes.h @@ -322,7 +322,7 @@ typedef struct SAlterDnodeStmt { typedef struct { ENodeType type; - char url[TSDB_ANAL_ANODE_URL_LEN + 3]; + char url[TSDB_ANALYTIC_ANODE_URL_LEN + 3]; } SCreateAnodeStmt; typedef struct { diff --git a/include/libs/nodes/plannodes.h b/include/libs/nodes/plannodes.h index 48852e5552..89bc27a1fa 100644 --- a/include/libs/nodes/plannodes.h +++ b/include/libs/nodes/plannodes.h @@ -334,7 +334,7 @@ typedef struct SWindowLogicNode { int64_t windowSliding; SNodeList* pTsmaSubplans; SNode* pAnomalyExpr; - char anomalyOpt[TSDB_ANAL_ALGO_OPTION_LEN]; + char anomalyOpt[TSDB_ANALYTIC_ALGO_OPTION_LEN]; } SWindowLogicNode; typedef struct SFillLogicNode { @@ -740,7 +740,7 @@ typedef SCountWinodwPhysiNode SStreamCountWinodwPhysiNode; typedef struct SAnomalyWindowPhysiNode { SWindowPhysiNode window; SNode* pAnomalyKey; - char anomalyOpt[TSDB_ANAL_ALGO_OPTION_LEN]; + char anomalyOpt[TSDB_ANALYTIC_ALGO_OPTION_LEN]; } SAnomalyWindowPhysiNode; typedef struct SSortPhysiNode { diff --git a/include/libs/nodes/querynodes.h b/include/libs/nodes/querynodes.h index 763882ab3a..7af74a347a 100644 --- a/include/libs/nodes/querynodes.h +++ b/include/libs/nodes/querynodes.h @@ -351,7 +351,7 @@ typedef struct SAnomalyWindowNode { ENodeType type; // QUERY_NODE_ANOMALY_WINDOW SNode* pCol; // timestamp primary key SNode* pExpr; - char anomalyOpt[TSDB_ANAL_ALGO_OPTION_LEN]; + char anomalyOpt[TSDB_ANALYTIC_ALGO_OPTION_LEN]; } SAnomalyWindowNode; typedef enum EFillMode { diff --git a/include/util/taoserror.h b/include/util/taoserror.h index e33af33d0e..6cedaeeef1 100644 --- a/include/util/taoserror.h +++ b/include/util/taoserror.h @@ -491,13 +491,14 @@ int32_t taosGetErrSize(); #define TSDB_CODE_MND_ANODE_TOO_MANY_ALGO_TYPE TAOS_DEF_ERROR_CODE(0, 0x0438) // analysis -#define TSDB_CODE_ANAL_URL_RSP_IS_NULL TAOS_DEF_ERROR_CODE(0, 0x0440) -#define TSDB_CODE_ANAL_URL_CANT_ACCESS TAOS_DEF_ERROR_CODE(0, 0x0441) -#define TSDB_CODE_ANAL_ALGO_NOT_FOUND TAOS_DEF_ERROR_CODE(0, 0x0442) -#define TSDB_CODE_ANAL_ALGO_NOT_LOAD TAOS_DEF_ERROR_CODE(0, 0x0443) -#define TSDB_CODE_ANAL_BUF_INVALID_TYPE TAOS_DEF_ERROR_CODE(0, 0x0444) -#define TSDB_CODE_ANAL_ANODE_RETURN_ERROR TAOS_DEF_ERROR_CODE(0, 0x0445) -#define TSDB_CODE_ANAL_ANODE_TOO_MANY_ROWS TAOS_DEF_ERROR_CODE(0, 0x0446) +#define TSDB_CODE_ANA_URL_RSP_IS_NULL TAOS_DEF_ERROR_CODE(0, 0x0440) +#define TSDB_CODE_ANA_URL_CANT_ACCESS TAOS_DEF_ERROR_CODE(0, 0x0441) +#define TSDB_CODE_ANA_ALGO_NOT_FOUND TAOS_DEF_ERROR_CODE(0, 0x0442) +#define TSDB_CODE_ANA_ALGO_NOT_LOAD TAOS_DEF_ERROR_CODE(0, 0x0443) +#define TSDB_CODE_ANA_BUF_INVALID_TYPE TAOS_DEF_ERROR_CODE(0, 0x0444) +#define TSDB_CODE_ANA_ANODE_RETURN_ERROR TAOS_DEF_ERROR_CODE(0, 0x0445) +#define TSDB_CODE_ANA_ANODE_TOO_MANY_ROWS TAOS_DEF_ERROR_CODE(0, 0x0446) +#define TSDB_CODE_ANA_WN_DATA TAOS_DEF_ERROR_CODE(0, 0x0447) // mnode-sma #define TSDB_CODE_MND_SMA_ALREADY_EXIST TAOS_DEF_ERROR_CODE(0, 0x0480) diff --git a/include/util/tdef.h b/include/util/tdef.h index 4e1fb21838..823c4bbe4b 100644 --- a/include/util/tdef.h +++ b/include/util/tdef.h @@ -335,12 +335,13 @@ typedef enum ELogicConditionType { #define TSDB_SLOW_QUERY_SQL_LEN 512 #define TSDB_SHOW_SUBQUERY_LEN 1000 #define TSDB_LOG_VAR_LEN 32 -#define TSDB_ANAL_ANODE_URL_LEN 128 -#define TSDB_ANAL_ALGO_NAME_LEN 64 -#define TSDB_ANAL_ALGO_TYPE_LEN 24 -#define TSDB_ANAL_ALGO_KEY_LEN (TSDB_ANAL_ALGO_NAME_LEN + 9) -#define TSDB_ANAL_ALGO_URL_LEN (TSDB_ANAL_ANODE_URL_LEN + TSDB_ANAL_ALGO_TYPE_LEN + 1) -#define TSDB_ANAL_ALGO_OPTION_LEN 256 + +#define TSDB_ANALYTIC_ANODE_URL_LEN 128 +#define TSDB_ANALYTIC_ALGO_NAME_LEN 64 +#define TSDB_ANALYTIC_ALGO_TYPE_LEN 24 +#define TSDB_ANALYTIC_ALGO_KEY_LEN (TSDB_ANALYTIC_ALGO_NAME_LEN + 9) +#define TSDB_ANALYTIC_ALGO_URL_LEN (TSDB_ANALYTIC_ANODE_URL_LEN + TSDB_ANALYTIC_ALGO_TYPE_LEN + 1) +#define TSDB_ANALYTIC_ALGO_OPTION_LEN 256 #define TSDB_MAX_EP_NUM 10 diff --git a/source/common/src/systable.c b/source/common/src/systable.c index 12b789f14e..bfe82aa7ae 100644 --- a/source/common/src/systable.c +++ b/source/common/src/systable.c @@ -402,7 +402,7 @@ static const SSysDbTableSchema userCompactsDetailSchema[] = { static const SSysDbTableSchema anodesSchema[] = { {.name = "id", .bytes = 4, .type = TSDB_DATA_TYPE_INT, .sysInfo = false}, - {.name = "url", .bytes = TSDB_ANAL_ANODE_URL_LEN + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = true}, + {.name = "url", .bytes = TSDB_ANALYTIC_ANODE_URL_LEN + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = true}, {.name = "status", .bytes = 10 + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = true}, {.name = "create_time", .bytes = 8, .type = TSDB_DATA_TYPE_TIMESTAMP, .sysInfo = true}, {.name = "update_time", .bytes = 8, .type = TSDB_DATA_TYPE_TIMESTAMP, .sysInfo = true}, @@ -410,8 +410,8 @@ static const SSysDbTableSchema anodesSchema[] = { static const SSysDbTableSchema anodesFullSchema[] = { {.name = "id", .bytes = 4, .type = TSDB_DATA_TYPE_INT, .sysInfo = false}, - {.name = "type", .bytes = TSDB_ANAL_ALGO_TYPE_LEN + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = true}, - {.name = "algo", .bytes = TSDB_ANAL_ALGO_NAME_LEN + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = true}, + {.name = "type", .bytes = TSDB_ANALYTIC_ALGO_TYPE_LEN + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = true}, + {.name = "algo", .bytes = TSDB_ANALYTIC_ALGO_NAME_LEN + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = true}, }; static const SSysDbTableSchema tsmaSchema[] = { diff --git a/source/common/src/tmsg.c b/source/common/src/tmsg.c index bc8830505e..814ec4a626 100644 --- a/source/common/src/tmsg.c +++ b/source/common/src/tmsg.c @@ -2169,7 +2169,7 @@ int32_t tSerializeRetrieveAnalAlgoRsp(void *buf, int32_t bufLen, SRetrieveAnalAl SAnalyticsUrl *pUrl = pIter; size_t nameLen = 0; const char *name = taosHashGetKey(pIter, &nameLen); - if (nameLen > 0 && nameLen <= TSDB_ANAL_ALGO_KEY_LEN && pUrl->urlLen > 0) { + if (nameLen > 0 && nameLen <= TSDB_ANALYTIC_ALGO_KEY_LEN && pUrl->urlLen > 0) { numOfAlgos++; } pIter = taosHashIterate(pRsp->hash, pIter); @@ -2224,7 +2224,7 @@ int32_t tDeserializeRetrieveAnalAlgoRsp(void *buf, int32_t bufLen, SRetrieveAnal int32_t numOfAlgos = 0; int32_t nameLen; int32_t type; - char name[TSDB_ANAL_ALGO_KEY_LEN]; + char name[TSDB_ANALYTIC_ALGO_KEY_LEN]; SAnalyticsUrl url = {0}; TAOS_CHECK_EXIT(tStartDecode(&decoder)); @@ -2233,7 +2233,7 @@ int32_t tDeserializeRetrieveAnalAlgoRsp(void *buf, int32_t bufLen, SRetrieveAnal for (int32_t f = 0; f < numOfAlgos; ++f) { TAOS_CHECK_EXIT(tDecodeI32(&decoder, &nameLen)); - if (nameLen > 0 && nameLen <= TSDB_ANAL_ALGO_NAME_LEN) { + if (nameLen > 0 && nameLen <= TSDB_ANALYTIC_ALGO_NAME_LEN) { TAOS_CHECK_EXIT(tDecodeCStrTo(&decoder, name)); } diff --git a/source/dnode/mnode/impl/src/mndAnode.c b/source/dnode/mnode/impl/src/mndAnode.c index 87bfe9f7af..e41e7170a3 100644 --- a/source/dnode/mnode/impl/src/mndAnode.c +++ b/source/dnode/mnode/impl/src/mndAnode.c @@ -309,7 +309,7 @@ static int32_t mndCreateAnode(SMnode *pMnode, SRpcMsg *pReq, SMCreateAnodeReq *p anodeObj.updateTime = anodeObj.createdTime; anodeObj.version = 0; anodeObj.urlLen = pCreate->urlLen; - if (anodeObj.urlLen > TSDB_ANAL_ANODE_URL_LEN) { + if (anodeObj.urlLen > TSDB_ANALYTIC_ANODE_URL_LEN) { code = TSDB_CODE_MND_ANODE_TOO_LONG_URL; goto _OVER; } @@ -584,7 +584,7 @@ static int32_t mndRetrieveAnodes(SRpcMsg *pReq, SShowObj *pShow, SSDataBlock *pB int32_t numOfRows = 0; int32_t cols = 0; SAnodeObj *pObj = NULL; - char buf[TSDB_ANAL_ANODE_URL_LEN + VARSTR_HEADER_SIZE]; + char buf[TSDB_ANALYTIC_ANODE_URL_LEN + VARSTR_HEADER_SIZE]; char status[64]; int32_t code = 0; @@ -642,7 +642,7 @@ static int32_t mndRetrieveAnodesFull(SRpcMsg *pReq, SShowObj *pShow, SSDataBlock int32_t numOfRows = 0; int32_t cols = 0; SAnodeObj *pObj = NULL; - char buf[TSDB_ANAL_ALGO_NAME_LEN + VARSTR_HEADER_SIZE]; + char buf[TSDB_ANALYTIC_ALGO_NAME_LEN + VARSTR_HEADER_SIZE]; int32_t code = 0; while (numOfRows < rows) { @@ -693,7 +693,7 @@ static int32_t mndDecodeAlgoList(SJson *pJson, SAnodeObj *pObj) { int32_t code = 0; int32_t protocol = 0; double tmp = 0; - char buf[TSDB_ANAL_ALGO_NAME_LEN + 1] = {0}; + char buf[TSDB_ANALYTIC_ALGO_NAME_LEN + 1] = {0}; code = tjsonGetDoubleValue(pJson, "protocol", &tmp); if (code < 0) return TSDB_CODE_INVALID_JSON_FORMAT; @@ -753,10 +753,10 @@ static int32_t mndDecodeAlgoList(SJson *pJson, SAnodeObj *pObj) { } static int32_t mndGetAnodeAlgoList(const char *url, SAnodeObj *pObj) { - char anodeUrl[TSDB_ANAL_ANODE_URL_LEN + 1] = {0}; - snprintf(anodeUrl, TSDB_ANAL_ANODE_URL_LEN, "%s/%s", url, "list"); + char anodeUrl[TSDB_ANALYTIC_ANODE_URL_LEN + 1] = {0}; + snprintf(anodeUrl, TSDB_ANALYTIC_ANODE_URL_LEN, "%s/%s", url, "list"); - SJson *pJson = taosAnalSendReqRetJson(anodeUrl, ANAL_HTTP_TYPE_GET, NULL); + SJson *pJson = taosAnalSendReqRetJson(anodeUrl, ANALYTICS_HTTP_TYPE_GET, NULL); if (pJson == NULL) return terrno; int32_t code = mndDecodeAlgoList(pJson, pObj); @@ -769,10 +769,10 @@ static int32_t mndGetAnodeStatus(SAnodeObj *pObj, char *status, int32_t statusLe int32_t code = 0; int32_t protocol = 0; double tmp = 0; - char anodeUrl[TSDB_ANAL_ANODE_URL_LEN + 1] = {0}; - snprintf(anodeUrl, TSDB_ANAL_ANODE_URL_LEN, "%s/%s", pObj->url, "status"); + char anodeUrl[TSDB_ANALYTIC_ANODE_URL_LEN + 1] = {0}; + snprintf(anodeUrl, TSDB_ANALYTIC_ANODE_URL_LEN, "%s/%s", pObj->url, "status"); - SJson *pJson = taosAnalSendReqRetJson(anodeUrl, ANAL_HTTP_TYPE_GET, NULL); + SJson *pJson = taosAnalSendReqRetJson(anodeUrl, ANALYTICS_HTTP_TYPE_GET, NULL); if (pJson == NULL) return terrno; code = tjsonGetDoubleValue(pJson, "protocol", &tmp); @@ -808,7 +808,7 @@ static int32_t mndProcessAnalAlgoReq(SRpcMsg *pReq) { SAnodeObj *pObj = NULL; SAnalyticsUrl url; int32_t nameLen; - char name[TSDB_ANAL_ALGO_KEY_LEN]; + char name[TSDB_ANALYTIC_ALGO_KEY_LEN]; SRetrieveAnalAlgoReq req = {0}; SRetrieveAnalAlgoRsp rsp = {0}; @@ -847,13 +847,13 @@ static int32_t mndProcessAnalAlgoReq(SRpcMsg *pReq) { goto _OVER; } } - url.url = taosMemoryMalloc(TSDB_ANAL_ANODE_URL_LEN + TSDB_ANAL_ALGO_TYPE_LEN + 1); + url.url = taosMemoryMalloc(TSDB_ANALYTIC_ANODE_URL_LEN + TSDB_ANALYTIC_ALGO_TYPE_LEN + 1); if (url.url == NULL) { sdbRelease(pSdb, pAnode); goto _OVER; } - url.urlLen = 1 + tsnprintf(url.url, TSDB_ANAL_ANODE_URL_LEN + TSDB_ANAL_ALGO_TYPE_LEN, "%s/%s", pAnode->url, + url.urlLen = 1 + tsnprintf(url.url, TSDB_ANALYTIC_ANODE_URL_LEN + TSDB_ANALYTIC_ALGO_TYPE_LEN, "%s/%s", pAnode->url, taosAnalAlgoUrlStr(url.type)); if (taosHashPut(rsp.hash, name, nameLen, &url, sizeof(SAnalyticsUrl)) != 0) { taosMemoryFree(url.url); diff --git a/source/libs/executor/src/anomalywindowoperator.c b/source/libs/executor/src/anomalywindowoperator.c index 94cc5d9129..f7b87b98b9 100644 --- a/source/libs/executor/src/anomalywindowoperator.c +++ b/source/libs/executor/src/anomalywindowoperator.c @@ -44,9 +44,9 @@ typedef struct { SExprSupp scalarSup; int32_t tsSlotId; STimeWindowAggSupp twAggSup; - char algoName[TSDB_ANAL_ALGO_NAME_LEN]; - char algoUrl[TSDB_ANAL_ALGO_URL_LEN]; - char anomalyOpt[TSDB_ANAL_ALGO_OPTION_LEN]; + char algoName[TSDB_ANALYTIC_ALGO_NAME_LEN]; + char algoUrl[TSDB_ANALYTIC_ALGO_URL_LEN]; + char anomalyOpt[TSDB_ANALYTIC_ALGO_OPTION_LEN]; SAnomalyWindowSupp anomalySup; SWindowRowsSup anomalyWinRowSup; SColumn anomalyCol; @@ -75,13 +75,13 @@ int32_t createAnomalywindowOperatorInfo(SOperatorInfo* downstream, SPhysiNode* p if (!taosAnalGetOptStr(pAnomalyNode->anomalyOpt, "algo", pInfo->algoName, sizeof(pInfo->algoName))) { qError("failed to get anomaly_window algorithm name from %s", pAnomalyNode->anomalyOpt); - code = TSDB_CODE_ANAL_ALGO_NOT_FOUND; + code = TSDB_CODE_ANA_ALGO_NOT_FOUND; goto _error; } if (taosAnalGetAlgoUrl(pInfo->algoName, ANAL_ALGO_TYPE_ANOMALY_DETECT, pInfo->algoUrl, sizeof(pInfo->algoUrl)) != 0) { qError("failed to get anomaly_window algorithm url from %s", pInfo->algoName); - code = TSDB_CODE_ANAL_ALGO_NOT_LOAD; + code = TSDB_CODE_ANA_ALGO_NOT_LOAD; goto _error; } @@ -262,7 +262,7 @@ static void anomalyDestroyOperatorInfo(void* param) { static int32_t anomalyCacheBlock(SAnomalyWindowOperatorInfo* pInfo, SSDataBlock* pSrc) { if (pInfo->anomalySup.cachedRows > ANAL_ANOMALY_WINDOW_MAX_ROWS) { - return TSDB_CODE_ANAL_ANODE_TOO_MANY_ROWS; + return TSDB_CODE_ANA_ANODE_TOO_MANY_ROWS; } SSDataBlock* pDst = NULL; @@ -287,7 +287,7 @@ static int32_t anomalyFindWindow(SAnomalyWindowSupp* pSupp, TSKEY key) { return -1; } -static int32_t anomalyParseJson(SJson* pJson, SArray* pWindows) { +static int32_t anomalyParseJson(SJson* pJson, SArray* pWindows, const char* pId) { int32_t code = 0; int32_t rows = 0; STimeWindow win = {0}; @@ -295,8 +295,18 @@ static int32_t anomalyParseJson(SJson* pJson, SArray* pWindows) { taosArrayClear(pWindows); tjsonGetInt32ValueFromDouble(pJson, "rows", rows, code); - if (code < 0) return TSDB_CODE_INVALID_JSON_FORMAT; - if (rows <= 0) return 0; + if (code < 0) { + return TSDB_CODE_INVALID_JSON_FORMAT; + } + + if (rows < 0) { + char pMsg[1024] = {0}; + tjsonGetStringValue(pJson, "msg", pMsg); + qError("%s failed to exec forecast, msg:%s", pId, pMsg); + return TSDB_CODE_ANA_WN_DATA; + } else if (rows == 0) { + return TSDB_CODE_SUCCESS; + } SJson* res = tjsonGetObjectItem(pJson, "res"); if (res == NULL) return TSDB_CODE_INVALID_JSON_FORMAT; @@ -313,7 +323,10 @@ static int32_t anomalyParseJson(SJson* pJson, SArray* pWindows) { SJson* start = tjsonGetArrayItem(row, 0); SJson* end = tjsonGetArrayItem(row, 1); - if (start == NULL || end == NULL) return TSDB_CODE_INVALID_JSON_FORMAT; + if (start == NULL || end == NULL) { + qError("%s invalid res from analytic sys, code:%s", pId, tstrerror(TSDB_CODE_INVALID_JSON_FORMAT)); + return TSDB_CODE_INVALID_JSON_FORMAT; + } tjsonGetObjectValueBigInt(start, &win.skey); tjsonGetObjectValueBigInt(end, &win.ekey); @@ -322,52 +335,57 @@ static int32_t anomalyParseJson(SJson* pJson, SArray* pWindows) { win.ekey = win.skey + 1; } - if (taosArrayPush(pWindows, &win) == NULL) return TSDB_CODE_OUT_OF_BUFFER; + if (taosArrayPush(pWindows, &win) == NULL) { + qError("%s out of memory in generating anomaly_window", pId); + return TSDB_CODE_OUT_OF_BUFFER; + } } int32_t numOfWins = taosArrayGetSize(pWindows); - qDebug("anomaly window recevied, total:%d", numOfWins); + qDebug("%s anomaly window recevied, total:%d", pId, numOfWins); for (int32_t i = 0; i < numOfWins; ++i) { STimeWindow* pWindow = taosArrayGet(pWindows, i); - qDebug("anomaly win:%d [%" PRId64 ", %" PRId64 ")", i, pWindow->skey, pWindow->ekey); + qDebug("%s anomaly win:%d [%" PRId64 ", %" PRId64 ")", pId, i, pWindow->skey, pWindow->ekey); } - return 0; + return code; } static int32_t anomalyAnalysisWindow(SOperatorInfo* pOperator) { SAnomalyWindowOperatorInfo* pInfo = pOperator->info; SAnomalyWindowSupp* pSupp = &pInfo->anomalySup; SJson* pJson = NULL; - SAnalBuf analBuf = {.bufType = ANAL_BUF_TYPE_JSON}; + SAnalyticBuf analBuf = {.bufType = ANALYTICS_BUF_TYPE_JSON}; char dataBuf[64] = {0}; int32_t code = 0; int64_t ts = 0; + int32_t lino = 0; + const char* pId = GET_TASKID(pOperator->pTaskInfo); - // int64_t ts = taosGetTimestampMs(); snprintf(analBuf.fileName, sizeof(analBuf.fileName), "%s/tdengine-anomaly-%" PRId64 "-%" PRId64, tsTempDir, ts, pSupp->groupId); code = tsosAnalBufOpen(&analBuf, 2); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); const char* prec = TSDB_TIME_PRECISION_MILLI_STR; if (pInfo->anomalyCol.precision == TSDB_TIME_PRECISION_MICRO) prec = TSDB_TIME_PRECISION_MICRO_STR; if (pInfo->anomalyCol.precision == TSDB_TIME_PRECISION_NANO) prec = TSDB_TIME_PRECISION_NANO_STR; code = taosAnalBufWriteColMeta(&analBuf, 0, TSDB_DATA_TYPE_TIMESTAMP, "ts"); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); code = taosAnalBufWriteColMeta(&analBuf, 1, pInfo->anomalyCol.type, "val"); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); code = taosAnalBufWriteDataBegin(&analBuf); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); int32_t numOfBlocks = (int32_t)taosArrayGetSize(pSupp->blocks); // timestamp code = taosAnalBufWriteColBegin(&analBuf, 0); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); + for (int32_t i = 0; i < numOfBlocks; ++i) { SSDataBlock* pBlock = taosArrayGetP(pSupp->blocks, i); if (pBlock == NULL) break; @@ -375,15 +393,17 @@ static int32_t anomalyAnalysisWindow(SOperatorInfo* pOperator) { if (pTsCol == NULL) break; for (int32_t j = 0; j < pBlock->info.rows; ++j) { code = taosAnalBufWriteColData(&analBuf, 0, TSDB_DATA_TYPE_TIMESTAMP, &((TSKEY*)pTsCol->pData)[j]); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); } } + code = taosAnalBufWriteColEnd(&analBuf, 0); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); // data code = taosAnalBufWriteColBegin(&analBuf, 1); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); + for (int32_t i = 0; i < numOfBlocks; ++i) { SSDataBlock* pBlock = taosArrayGetP(pSupp->blocks, i); if (pBlock == NULL) break; @@ -392,48 +412,47 @@ static int32_t anomalyAnalysisWindow(SOperatorInfo* pOperator) { for (int32_t j = 0; j < pBlock->info.rows; ++j) { code = taosAnalBufWriteColData(&analBuf, 1, pValCol->info.type, colDataGetData(pValCol, j)); - if (code != 0) goto _OVER; - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); } } code = taosAnalBufWriteColEnd(&analBuf, 1); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); code = taosAnalBufWriteDataEnd(&analBuf); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); code = taosAnalBufWriteOptStr(&analBuf, "option", pInfo->anomalyOpt); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); code = taosAnalBufWriteOptStr(&analBuf, "algo", pInfo->algoName); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); code = taosAnalBufWriteOptStr(&analBuf, "prec", prec); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); int64_t wncheck = ANAL_FORECAST_DEFAULT_WNCHECK; bool hasWncheck = taosAnalGetOptInt(pInfo->anomalyOpt, "wncheck", &wncheck); if (!hasWncheck) { qDebug("anomaly_window wncheck not found from %s, use default:%" PRId64, pInfo->anomalyOpt, wncheck); } + code = taosAnalBufWriteOptInt(&analBuf, "wncheck", wncheck); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); code = taosAnalBufClose(&analBuf); - if (code != 0) goto _OVER; + QUERY_CHECK_CODE(code, lino, _OVER); - pJson = taosAnalSendReqRetJson(pInfo->algoUrl, ANAL_HTTP_TYPE_POST, &analBuf); + pJson = taosAnalSendReqRetJson(pInfo->algoUrl, ANALYTICS_HTTP_TYPE_POST, &analBuf); if (pJson == NULL) { code = terrno; goto _OVER; } - code = anomalyParseJson(pJson, pSupp->windows); - if (code != 0) goto _OVER; + code = anomalyParseJson(pJson, pSupp->windows, pId); _OVER: if (code != 0) { - qError("failed to analysis window since %s", tstrerror(code)); + qError("%s failed to analysis window since %s, lino:%d", pId, tstrerror(code), lino); } taosAnalBufDestroy(&analBuf); diff --git a/source/libs/executor/src/forecastoperator.c b/source/libs/executor/src/forecastoperator.c index 20dc9e28ba..9ad9950bdb 100644 --- a/source/libs/executor/src/forecastoperator.c +++ b/source/libs/executor/src/forecastoperator.c @@ -29,9 +29,9 @@ #ifdef USE_ANALYTICS typedef struct { - char algoName[TSDB_ANAL_ALGO_NAME_LEN]; - char algoUrl[TSDB_ANAL_ALGO_URL_LEN]; - char algoOpt[TSDB_ANAL_ALGO_OPTION_LEN]; + char algoName[TSDB_ANALYTIC_ALGO_NAME_LEN]; + char algoUrl[TSDB_ANALYTIC_ALGO_URL_LEN]; + char algoOpt[TSDB_ANALYTIC_ALGO_OPTION_LEN]; int64_t maxTs; int64_t minTs; int64_t numOfRows; @@ -47,7 +47,7 @@ typedef struct { int16_t inputValSlot; int8_t inputValType; int8_t inputPrecision; - SAnalBuf analBuf; + SAnalyticBuf analBuf; } SForecastSupp; typedef struct SForecastOperatorInfo { @@ -74,12 +74,12 @@ static FORCE_INLINE int32_t forecastEnsureBlockCapacity(SSDataBlock* pBlock, int static int32_t forecastCacheBlock(SForecastSupp* pSupp, SSDataBlock* pBlock) { if (pSupp->cachedRows > ANAL_FORECAST_MAX_ROWS) { - return TSDB_CODE_ANAL_ANODE_TOO_MANY_ROWS; + return TSDB_CODE_ANA_ANODE_TOO_MANY_ROWS; } int32_t code = TSDB_CODE_SUCCESS; int32_t lino = 0; - SAnalBuf* pBuf = &pSupp->analBuf; + SAnalyticBuf* pBuf = &pSupp->analBuf; qDebug("block:%d, %p rows:%" PRId64, pSupp->numOfBlocks, pBlock, pBlock->info.rows); pSupp->numOfBlocks++; @@ -108,7 +108,7 @@ static int32_t forecastCacheBlock(SForecastSupp* pSupp, SSDataBlock* pBlock) { } static int32_t forecastCloseBuf(SForecastSupp* pSupp) { - SAnalBuf* pBuf = &pSupp->analBuf; + SAnalyticBuf* pBuf = &pSupp->analBuf; int32_t code = 0; for (int32_t i = 0; i < 2; ++i) { @@ -180,8 +180,8 @@ static int32_t forecastCloseBuf(SForecastSupp* pSupp) { return code; } -static int32_t forecastAnalysis(SForecastSupp* pSupp, SSDataBlock* pBlock) { - SAnalBuf* pBuf = &pSupp->analBuf; +static int32_t forecastAnalysis(SForecastSupp* pSupp, SSDataBlock* pBlock, const char* pId) { + SAnalyticBuf* pBuf = &pSupp->analBuf; int32_t resCurRow = pBlock->info.rows; int8_t tmpI8; int16_t tmpI16; @@ -192,28 +192,41 @@ static int32_t forecastAnalysis(SForecastSupp* pSupp, SSDataBlock* pBlock) { int32_t code = 0; SColumnInfoData* pResValCol = taosArrayGet(pBlock->pDataBlock, pSupp->resValSlot); - if (NULL == pResValCol) return TSDB_CODE_OUT_OF_RANGE; + if (NULL == pResValCol) { + return terrno; + } SColumnInfoData* pResTsCol = (pSupp->resTsSlot != -1 ? taosArrayGet(pBlock->pDataBlock, pSupp->resTsSlot) : NULL); SColumnInfoData* pResLowCol = (pSupp->resLowSlot != -1 ? taosArrayGet(pBlock->pDataBlock, pSupp->resLowSlot) : NULL); SColumnInfoData* pResHighCol = (pSupp->resHighSlot != -1 ? taosArrayGet(pBlock->pDataBlock, pSupp->resHighSlot) : NULL); - SJson* pJson = taosAnalSendReqRetJson(pSupp->algoUrl, ANAL_HTTP_TYPE_POST, pBuf); - if (pJson == NULL) return terrno; + SJson* pJson = taosAnalSendReqRetJson(pSupp->algoUrl, ANALYTICS_HTTP_TYPE_POST, pBuf); + if (pJson == NULL) { + return terrno; + } int32_t rows = 0; tjsonGetInt32ValueFromDouble(pJson, "rows", rows, code); - if (code < 0) goto _OVER; - if (rows <= 0) goto _OVER; + if (rows < 0 && code == 0) { + char pMsg[1024] = {0}; + tjsonGetStringValue(pJson, "msg", pMsg); + qError("%s failed to exec forecast, msg:%s", pId, pMsg); + + tjsonDelete(pJson); + return TSDB_CODE_ANA_WN_DATA; + } + + if (code < 0) { + goto _OVER; + } SJson* res = tjsonGetObjectItem(pJson, "res"); if (res == NULL) goto _OVER; int32_t ressize = tjsonGetArraySize(res); bool returnConf = (pSupp->resHighSlot != -1 || pSupp->resLowSlot != -1); - if (returnConf) { - if (ressize != 4) goto _OVER; - } else if (ressize != 2) { + + if ((returnConf && (ressize != 4)) || ((!returnConf) && (ressize != 2))) { goto _OVER; } @@ -313,41 +326,25 @@ static int32_t forecastAnalysis(SForecastSupp* pSupp, SSDataBlock* pBlock) { resCurRow++; } - // for (int32_t i = rows; i < pSupp->optRows; ++i) { - // colDataSetNNULL(pResValCol, rows, (pSupp->optRows - rows)); - // if (pResTsCol != NULL) { - // colDataSetNNULL(pResTsCol, rows, (pSupp->optRows - rows)); - // } - // if (pResLowCol != NULL) { - // colDataSetNNULL(pResLowCol, rows, (pSupp->optRows - rows)); - // } - // if (pResHighCol != NULL) { - // colDataSetNNULL(pResHighCol, rows, (pSupp->optRows - rows)); - // } - // } - - // if (rows == pSupp->optRows) { - // pResValCol->hasNull = false; - // } - pBlock->info.rows += rows; if (pJson != NULL) tjsonDelete(pJson); return 0; _OVER: - if (pJson != NULL) tjsonDelete(pJson); + tjsonDelete(pJson); if (code == 0) { code = TSDB_CODE_INVALID_JSON_FORMAT; } - qError("failed to perform forecast finalize since %s", tstrerror(code)); - return TSDB_CODE_INVALID_JSON_FORMAT; + + qError("%s failed to perform forecast finalize since %s", pId, tstrerror(code)); + return code; } -static int32_t forecastAggregateBlocks(SForecastSupp* pSupp, SSDataBlock* pResBlock) { +static int32_t forecastAggregateBlocks(SForecastSupp* pSupp, SSDataBlock* pResBlock, const char* pId) { int32_t code = TSDB_CODE_SUCCESS; int32_t lino = 0; - SAnalBuf* pBuf = &pSupp->analBuf; + SAnalyticBuf* pBuf = &pSupp->analBuf; code = forecastCloseBuf(pSupp); QUERY_CHECK_CODE(code, lino, _end); @@ -355,10 +352,10 @@ static int32_t forecastAggregateBlocks(SForecastSupp* pSupp, SSDataBlock* pResBl code = forecastEnsureBlockCapacity(pResBlock, 1); QUERY_CHECK_CODE(code, lino, _end); - code = forecastAnalysis(pSupp, pResBlock); + code = forecastAnalysis(pSupp, pResBlock, pId); QUERY_CHECK_CODE(code, lino, _end); - uInfo("block:%d, forecast finalize", pSupp->numOfBlocks); + uInfo("%s block:%d, forecast finalize", pId, pSupp->numOfBlocks); _end: pSupp->numOfBlocks = 0; @@ -373,9 +370,10 @@ static int32_t forecastNext(SOperatorInfo* pOperator, SSDataBlock** ppRes) { SForecastOperatorInfo* pInfo = pOperator->info; SSDataBlock* pResBlock = pInfo->pRes; SForecastSupp* pSupp = &pInfo->forecastSupp; - SAnalBuf* pBuf = &pSupp->analBuf; + SAnalyticBuf* pBuf = &pSupp->analBuf; int64_t st = taosGetTimestampUs(); int32_t numOfBlocks = pSupp->numOfBlocks; + const char* pId = GET_TASKID(pOperator->pTaskInfo); blockDataCleanup(pResBlock); @@ -389,45 +387,46 @@ static int32_t forecastNext(SOperatorInfo* pOperator, SSDataBlock** ppRes) { pSupp->groupId = pBlock->info.id.groupId; numOfBlocks++; pSupp->cachedRows += pBlock->info.rows; - qDebug("group:%" PRId64 ", blocks:%d, rows:%" PRId64 ", total rows:%" PRId64, pSupp->groupId, numOfBlocks, + qDebug("%s group:%" PRId64 ", blocks:%d, rows:%" PRId64 ", total rows:%" PRId64, pId, pSupp->groupId, numOfBlocks, pBlock->info.rows, pSupp->cachedRows); code = forecastCacheBlock(pSupp, pBlock); QUERY_CHECK_CODE(code, lino, _end); } else { - qDebug("group:%" PRId64 ", read finish for new group coming, blocks:%d", pSupp->groupId, numOfBlocks); - code = forecastAggregateBlocks(pSupp, pResBlock); + qDebug("%s group:%" PRId64 ", read finish for new group coming, blocks:%d", pId, pSupp->groupId, numOfBlocks); + code = forecastAggregateBlocks(pSupp, pResBlock, pId); QUERY_CHECK_CODE(code, lino, _end); pSupp->groupId = pBlock->info.id.groupId; numOfBlocks = 1; pSupp->cachedRows = pBlock->info.rows; - qDebug("group:%" PRId64 ", new group, rows:%" PRId64 ", total rows:%" PRId64, pSupp->groupId, pBlock->info.rows, - pSupp->cachedRows); + qDebug("%s group:%" PRId64 ", new group, rows:%" PRId64 ", total rows:%" PRId64, pId, pSupp->groupId, + pBlock->info.rows, pSupp->cachedRows); code = forecastCacheBlock(pSupp, pBlock); QUERY_CHECK_CODE(code, lino, _end); } if (pResBlock->info.rows > 0) { (*ppRes) = pResBlock; - qDebug("group:%" PRId64 ", return to upstream, blocks:%d", pResBlock->info.id.groupId, numOfBlocks); + qDebug("%s group:%" PRId64 ", return to upstream, blocks:%d", pId, pResBlock->info.id.groupId, numOfBlocks); return code; } } if (numOfBlocks > 0) { - qDebug("group:%" PRId64 ", read finish, blocks:%d", pSupp->groupId, numOfBlocks); - code = forecastAggregateBlocks(pSupp, pResBlock); + qDebug("%s group:%" PRId64 ", read finish, blocks:%d", pId, pSupp->groupId, numOfBlocks); + code = forecastAggregateBlocks(pSupp, pResBlock, pId); QUERY_CHECK_CODE(code, lino, _end); } int64_t cost = taosGetTimestampUs() - st; - qDebug("all groups finished, cost:%" PRId64 "us", cost); + qDebug("%s all groups finished, cost:%" PRId64 "us", pId, cost); _end: if (code != TSDB_CODE_SUCCESS) { - qError("%s failed at line %d since %s", __func__, lino, tstrerror(code)); + qError("%s %s failed at line %d since %s", pId, __func__, lino, tstrerror(code)); pTaskInfo->code = code; T_LONG_JMP(pTaskInfo->env, code); } + (*ppRes) = (pResBlock->info.rows == 0) ? NULL : pResBlock; return code; } @@ -498,7 +497,7 @@ static int32_t forecastParseInput(SForecastSupp* pSupp, SNodeList* pFuncs) { pSupp->inputPrecision = pTsNode->node.resType.precision; pSupp->inputValSlot = pValNode->slotId; pSupp->inputValType = pValNode->node.resType.type; - tstrncpy(pSupp->algoOpt, "algo=arima", TSDB_ANAL_ALGO_OPTION_LEN); + tstrncpy(pSupp->algoOpt, "algo=arima", TSDB_ANALYTIC_ALGO_OPTION_LEN); } else { return TSDB_CODE_PLAN_INTERNAL_ERROR; } @@ -516,22 +515,22 @@ static int32_t forecastParseAlgo(SForecastSupp* pSupp) { if (!taosAnalGetOptStr(pSupp->algoOpt, "algo", pSupp->algoName, sizeof(pSupp->algoName))) { qError("failed to get forecast algorithm name from %s", pSupp->algoOpt); - return TSDB_CODE_ANAL_ALGO_NOT_FOUND; + return TSDB_CODE_ANA_ALGO_NOT_FOUND; } if (taosAnalGetAlgoUrl(pSupp->algoName, ANAL_ALGO_TYPE_FORECAST, pSupp->algoUrl, sizeof(pSupp->algoUrl)) != 0) { qError("failed to get forecast algorithm url from %s", pSupp->algoName); - return TSDB_CODE_ANAL_ALGO_NOT_LOAD; + return TSDB_CODE_ANA_ALGO_NOT_LOAD; } return 0; } static int32_t forecastCreateBuf(SForecastSupp* pSupp) { - SAnalBuf* pBuf = &pSupp->analBuf; + SAnalyticBuf* pBuf = &pSupp->analBuf; int64_t ts = 0; // taosGetTimestampMs(); - pBuf->bufType = ANAL_BUF_TYPE_JSON_COL; + pBuf->bufType = ANALYTICS_BUF_TYPE_JSON_COL; snprintf(pBuf->fileName, sizeof(pBuf->fileName), "%s/tdengine-forecast-%" PRId64, tsTempDir, ts); int32_t code = tsosAnalBufOpen(pBuf, 2); if (code != 0) goto _OVER; diff --git a/source/libs/parser/src/parAstCreater.c b/source/libs/parser/src/parAstCreater.c index 245346273f..1a5e3444c0 100644 --- a/source/libs/parser/src/parAstCreater.c +++ b/source/libs/parser/src/parAstCreater.c @@ -1377,7 +1377,7 @@ SNode* createAnomalyWindowNode(SAstCreateContext* pCxt, SNode* pExpr, const STok CHECK_MAKE_NODE(pAnomaly->pCol); pAnomaly->pExpr = pExpr; if (pFuncOpt == NULL) { - tstrncpy(pAnomaly->anomalyOpt, "algo=iqr", TSDB_ANAL_ALGO_OPTION_LEN); + tstrncpy(pAnomaly->anomalyOpt, "algo=iqr", TSDB_ANALYTIC_ALGO_OPTION_LEN); } else { (void)trimString(pFuncOpt->z, pFuncOpt->n, pAnomaly->anomalyOpt, sizeof(pAnomaly->anomalyOpt)); } diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index 9bea3491c3..fcb6361a6b 100755 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -9652,7 +9652,7 @@ static int32_t translateDropUser(STranslateContext* pCxt, SDropUserStmt* pStmt) static int32_t translateCreateAnode(STranslateContext* pCxt, SCreateAnodeStmt* pStmt) { SMCreateAnodeReq createReq = {0}; createReq.urlLen = strlen(pStmt->url) + 1; - if (createReq.urlLen > TSDB_ANAL_ANODE_URL_LEN) { + if (createReq.urlLen > TSDB_ANALYTIC_ANODE_URL_LEN) { return TSDB_CODE_MND_ANODE_TOO_LONG_URL; } diff --git a/source/util/src/tanalytics.c b/source/util/src/tanalytics.c index 99d91700a2..49832063df 100644 --- a/source/util/src/tanalytics.c +++ b/source/util/src/tanalytics.c @@ -34,7 +34,7 @@ typedef struct { } SCurlResp; static SAlgoMgmt tsAlgos = {0}; -static int32_t taosAnalBufGetCont(SAnalBuf *pBuf, char **ppCont, int64_t *pContLen); +static int32_t taosAnalBufGetCont(SAnalyticBuf *pBuf, char **ppCont, int64_t *pContLen); const char *taosAnalAlgoStr(EAnalAlgoType type) { switch (type) { @@ -127,19 +127,26 @@ void taosAnalUpdate(int64_t newVer, SHashObj *pHash) { } bool taosAnalGetOptStr(const char *option, const char *optName, char *optValue, int32_t optMaxLen) { - char buf[TSDB_ANAL_ALGO_OPTION_LEN] = {0}; - int32_t bufLen = tsnprintf(buf, sizeof(buf), "%s=", optName); + char buf[TSDB_ANALYTIC_ALGO_OPTION_LEN] = {0}; + char *pStart = strstr(option, optName); + char *pEnd = strstr(pStart, ANAL_ALGO_SPLIT); - char *pos1 = strstr(option, buf); - char *pos2 = strstr(option, ANAL_ALGO_SPLIT); - if (pos1 != NULL) { + if (pStart != NULL) { if (optMaxLen > 0) { int32_t copyLen = optMaxLen; - if (pos2 != NULL) { - copyLen = (int32_t)(pos2 - pos1 - strlen(optName)); + if (pEnd > pStart) { + copyLen = (int32_t)(pEnd - pStart - strlen(optName)); copyLen = MIN(copyLen, optMaxLen); + tstrncpy(buf, pStart, copyLen); + } else { + int32_t len = MIN(tListLen(buf), strlen(pStart) + 1); + tstrncpy(buf, pStart, len); } - tstrncpy(optValue, pos1 + bufLen, copyLen); + + char *pRight = strstr(buf, "=") + 1; + strtrim(pRight); + + tstrncpy(optValue, pRight, strlen(pRight) + 1); } return true; } else { @@ -148,7 +155,7 @@ bool taosAnalGetOptStr(const char *option, const char *optName, char *optValue, } bool taosAnalGetOptInt(const char *option, const char *optName, int64_t *optValue) { - char buf[TSDB_ANAL_ALGO_OPTION_LEN] = {0}; + char buf[TSDB_ANALYTIC_ALGO_OPTION_LEN] = {0}; int32_t bufLen = tsnprintf(buf, sizeof(buf), "%s=", optName); char *pos1 = strstr(option, buf); @@ -163,7 +170,7 @@ bool taosAnalGetOptInt(const char *option, const char *optName, int64_t *optValu int32_t taosAnalGetAlgoUrl(const char *algoName, EAnalAlgoType type, char *url, int32_t urlLen) { int32_t code = 0; - char name[TSDB_ANAL_ALGO_KEY_LEN] = {0}; + char name[TSDB_ANALYTIC_ALGO_KEY_LEN] = {0}; int32_t nameLen = 1 + tsnprintf(name, sizeof(name) - 1, "%d:%s", type, algoName); char *unused = strntolower(name, name, nameLen); @@ -175,7 +182,7 @@ int32_t taosAnalGetAlgoUrl(const char *algoName, EAnalAlgoType type, char *url, uDebug("algo:%s, type:%s, url:%s", algoName, taosAnalAlgoStr(type), url); } else { url[0] = 0; - terrno = TSDB_CODE_ANAL_ALGO_NOT_FOUND; + terrno = TSDB_CODE_ANA_ALGO_NOT_FOUND; code = terrno; uError("algo:%s, type:%s, url not found", algoName, taosAnalAlgoStr(type)); } @@ -276,16 +283,16 @@ _OVER: return code; } -SJson *taosAnalSendReqRetJson(const char *url, EAnalHttpType type, SAnalBuf *pBuf) { +SJson *taosAnalSendReqRetJson(const char *url, EAnalHttpType type, SAnalyticBuf *pBuf) { int32_t code = -1; char *pCont = NULL; int64_t contentLen; SJson *pJson = NULL; SCurlResp curlRsp = {0}; - if (type == ANAL_HTTP_TYPE_GET) { + if (type == ANALYTICS_HTTP_TYPE_GET) { if (taosCurlGetRequest(url, &curlRsp) != 0) { - terrno = TSDB_CODE_ANAL_URL_CANT_ACCESS; + terrno = TSDB_CODE_ANA_URL_CANT_ACCESS; goto _OVER; } } else { @@ -295,20 +302,20 @@ SJson *taosAnalSendReqRetJson(const char *url, EAnalHttpType type, SAnalBuf *pBu goto _OVER; } if (taosCurlPostRequest(url, &curlRsp, pCont, contentLen) != 0) { - terrno = TSDB_CODE_ANAL_URL_CANT_ACCESS; + terrno = TSDB_CODE_ANA_URL_CANT_ACCESS; goto _OVER; } } if (curlRsp.data == NULL || curlRsp.dataLen == 0) { - terrno = TSDB_CODE_ANAL_URL_RSP_IS_NULL; + terrno = TSDB_CODE_ANA_URL_RSP_IS_NULL; goto _OVER; } pJson = tjsonParse(curlRsp.data); if (pJson == NULL) { if (curlRsp.data[0] == '<') { - terrno = TSDB_CODE_ANAL_ANODE_RETURN_ERROR; + terrno = TSDB_CODE_ANA_ANODE_RETURN_ERROR; } else { terrno = TSDB_CODE_INVALID_JSON_FORMAT; } @@ -360,7 +367,7 @@ _OVER: return code; } -static int32_t taosAnalJsonBufWriteOptInt(SAnalBuf *pBuf, const char *optName, int64_t optVal) { +static int32_t taosAnalJsonBufWriteOptInt(SAnalyticBuf *pBuf, const char *optName, int64_t optVal) { char buf[64] = {0}; int32_t bufLen = tsnprintf(buf, sizeof(buf), "\"%s\": %" PRId64 ",\n", optName, optVal); if (taosWriteFile(pBuf->filePtr, buf, bufLen) != bufLen) { @@ -369,7 +376,7 @@ static int32_t taosAnalJsonBufWriteOptInt(SAnalBuf *pBuf, const char *optName, i return 0; } -static int32_t taosAnalJsonBufWriteOptStr(SAnalBuf *pBuf, const char *optName, const char *optVal) { +static int32_t taosAnalJsonBufWriteOptStr(SAnalyticBuf *pBuf, const char *optName, const char *optVal) { char buf[128] = {0}; int32_t bufLen = tsnprintf(buf, sizeof(buf), "\"%s\": \"%s\",\n", optName, optVal); if (taosWriteFile(pBuf->filePtr, buf, bufLen) != bufLen) { @@ -378,7 +385,7 @@ static int32_t taosAnalJsonBufWriteOptStr(SAnalBuf *pBuf, const char *optName, c return 0; } -static int32_t taosAnalJsonBufWriteOptFloat(SAnalBuf *pBuf, const char *optName, float optVal) { +static int32_t taosAnalJsonBufWriteOptFloat(SAnalyticBuf *pBuf, const char *optName, float optVal) { char buf[128] = {0}; int32_t bufLen = tsnprintf(buf, sizeof(buf), "\"%s\": %f,\n", optName, optVal); if (taosWriteFile(pBuf->filePtr, buf, bufLen) != bufLen) { @@ -387,7 +394,7 @@ static int32_t taosAnalJsonBufWriteOptFloat(SAnalBuf *pBuf, const char *optName, return 0; } -static int32_t taosAnalJsonBufWriteStr(SAnalBuf *pBuf, const char *buf, int32_t bufLen) { +static int32_t taosAnalJsonBufWriteStr(SAnalyticBuf *pBuf, const char *buf, int32_t bufLen) { if (bufLen <= 0) { bufLen = strlen(buf); } @@ -397,9 +404,9 @@ static int32_t taosAnalJsonBufWriteStr(SAnalBuf *pBuf, const char *buf, int32_t return 0; } -static int32_t taosAnalJsonBufWriteStart(SAnalBuf *pBuf) { return taosAnalJsonBufWriteStr(pBuf, "{\n", 0); } +static int32_t taosAnalJsonBufWriteStart(SAnalyticBuf *pBuf) { return taosAnalJsonBufWriteStr(pBuf, "{\n", 0); } -static int32_t tsosAnalJsonBufOpen(SAnalBuf *pBuf, int32_t numOfCols) { +static int32_t tsosAnalJsonBufOpen(SAnalyticBuf *pBuf, int32_t numOfCols) { pBuf->filePtr = taosOpenFile(pBuf->fileName, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_WRITE_THROUGH); if (pBuf->filePtr == NULL) { return terrno; @@ -409,7 +416,7 @@ static int32_t tsosAnalJsonBufOpen(SAnalBuf *pBuf, int32_t numOfCols) { if (pBuf->pCols == NULL) return TSDB_CODE_OUT_OF_MEMORY; pBuf->numOfCols = numOfCols; - if (pBuf->bufType == ANAL_BUF_TYPE_JSON) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON) { return taosAnalJsonBufWriteStart(pBuf); } @@ -426,7 +433,7 @@ static int32_t tsosAnalJsonBufOpen(SAnalBuf *pBuf, int32_t numOfCols) { return taosAnalJsonBufWriteStart(pBuf); } -static int32_t taosAnalJsonBufWriteColMeta(SAnalBuf *pBuf, int32_t colIndex, int32_t colType, const char *colName) { +static int32_t taosAnalJsonBufWriteColMeta(SAnalyticBuf *pBuf, int32_t colIndex, int32_t colType, const char *colName) { char buf[128] = {0}; bool first = (colIndex == 0); bool last = (colIndex == pBuf->numOfCols - 1); @@ -452,16 +459,16 @@ static int32_t taosAnalJsonBufWriteColMeta(SAnalBuf *pBuf, int32_t colIndex, int return 0; } -static int32_t taosAnalJsonBufWriteDataBegin(SAnalBuf *pBuf) { +static int32_t taosAnalJsonBufWriteDataBegin(SAnalyticBuf *pBuf) { return taosAnalJsonBufWriteStr(pBuf, "\"data\": [\n", 0); } -static int32_t taosAnalJsonBufWriteStrUseCol(SAnalBuf *pBuf, const char *buf, int32_t bufLen, int32_t colIndex) { +static int32_t taosAnalJsonBufWriteStrUseCol(SAnalyticBuf *pBuf, const char *buf, int32_t bufLen, int32_t colIndex) { if (bufLen <= 0) { bufLen = strlen(buf); } - if (pBuf->bufType == ANAL_BUF_TYPE_JSON) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON) { if (taosWriteFile(pBuf->filePtr, buf, bufLen) != bufLen) { return terrno; } @@ -474,11 +481,11 @@ static int32_t taosAnalJsonBufWriteStrUseCol(SAnalBuf *pBuf, const char *buf, in return 0; } -static int32_t taosAnalJsonBufWriteColBegin(SAnalBuf *pBuf, int32_t colIndex) { +static int32_t taosAnalJsonBufWriteColBegin(SAnalyticBuf *pBuf, int32_t colIndex) { return taosAnalJsonBufWriteStrUseCol(pBuf, "[\n", 0, colIndex); } -static int32_t taosAnalJsonBufWriteColEnd(SAnalBuf *pBuf, int32_t colIndex) { +static int32_t taosAnalJsonBufWriteColEnd(SAnalyticBuf *pBuf, int32_t colIndex) { if (colIndex == pBuf->numOfCols - 1) { return taosAnalJsonBufWriteStrUseCol(pBuf, "\n]\n", 0, colIndex); @@ -487,7 +494,7 @@ static int32_t taosAnalJsonBufWriteColEnd(SAnalBuf *pBuf, int32_t colIndex) { } } -static int32_t taosAnalJsonBufWriteColData(SAnalBuf *pBuf, int32_t colIndex, int32_t colType, void *colValue) { +static int32_t taosAnalJsonBufWriteColData(SAnalyticBuf *pBuf, int32_t colIndex, int32_t colType, void *colValue) { char buf[64]; int32_t bufLen = 0; @@ -541,12 +548,12 @@ static int32_t taosAnalJsonBufWriteColData(SAnalBuf *pBuf, int32_t colIndex, int return taosAnalJsonBufWriteStrUseCol(pBuf, buf, bufLen, colIndex); } -static int32_t taosAnalJsonBufWriteDataEnd(SAnalBuf *pBuf) { +static int32_t taosAnalJsonBufWriteDataEnd(SAnalyticBuf *pBuf) { int32_t code = 0; char *pCont = NULL; int64_t contLen = 0; - if (pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { for (int32_t i = 0; i < pBuf->numOfCols; ++i) { SAnalyticsColBuf *pCol = &pBuf->pCols[i]; @@ -570,14 +577,14 @@ static int32_t taosAnalJsonBufWriteDataEnd(SAnalBuf *pBuf) { return taosAnalJsonBufWriteStr(pBuf, "],\n", 0); } -static int32_t taosAnalJsonBufWriteEnd(SAnalBuf *pBuf) { +static int32_t taosAnalJsonBufWriteEnd(SAnalyticBuf *pBuf) { int32_t code = taosAnalJsonBufWriteOptInt(pBuf, "rows", pBuf->pCols[0].numOfRows); if (code != 0) return code; return taosAnalJsonBufWriteStr(pBuf, "\"protocol\": 1.0\n}", 0); } -int32_t taosAnalJsonBufClose(SAnalBuf *pBuf) { +int32_t taosAnalJsonBufClose(SAnalyticBuf *pBuf) { int32_t code = taosAnalJsonBufWriteEnd(pBuf); if (code != 0) return code; @@ -588,7 +595,7 @@ int32_t taosAnalJsonBufClose(SAnalBuf *pBuf) { if (code != 0) return code; } - if (pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { for (int32_t i = 0; i < pBuf->numOfCols; ++i) { SAnalyticsColBuf *pCol = &pBuf->pCols[i]; if (pCol->filePtr != NULL) { @@ -603,14 +610,14 @@ int32_t taosAnalJsonBufClose(SAnalBuf *pBuf) { return 0; } -void taosAnalBufDestroy(SAnalBuf *pBuf) { +void taosAnalBufDestroy(SAnalyticBuf *pBuf) { if (pBuf->fileName[0] != 0) { if (pBuf->filePtr != NULL) (void)taosCloseFile(&pBuf->filePtr); // taosRemoveFile(pBuf->fileName); pBuf->fileName[0] = 0; } - if (pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { for (int32_t i = 0; i < pBuf->numOfCols; ++i) { SAnalyticsColBuf *pCol = &pBuf->pCols[i]; if (pCol->fileName[0] != 0) { @@ -627,102 +634,102 @@ void taosAnalBufDestroy(SAnalBuf *pBuf) { pBuf->numOfCols = 0; } -int32_t tsosAnalBufOpen(SAnalBuf *pBuf, int32_t numOfCols) { - if (pBuf->bufType == ANAL_BUF_TYPE_JSON || pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { +int32_t tsosAnalBufOpen(SAnalyticBuf *pBuf, int32_t numOfCols) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON || pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { return tsosAnalJsonBufOpen(pBuf, numOfCols); } else { - return TSDB_CODE_ANAL_BUF_INVALID_TYPE; + return TSDB_CODE_ANA_BUF_INVALID_TYPE; } } -int32_t taosAnalBufWriteOptStr(SAnalBuf *pBuf, const char *optName, const char *optVal) { - if (pBuf->bufType == ANAL_BUF_TYPE_JSON || pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { +int32_t taosAnalBufWriteOptStr(SAnalyticBuf *pBuf, const char *optName, const char *optVal) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON || pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { return taosAnalJsonBufWriteOptStr(pBuf, optName, optVal); } else { - return TSDB_CODE_ANAL_BUF_INVALID_TYPE; + return TSDB_CODE_ANA_BUF_INVALID_TYPE; } } -int32_t taosAnalBufWriteOptInt(SAnalBuf *pBuf, const char *optName, int64_t optVal) { - if (pBuf->bufType == ANAL_BUF_TYPE_JSON || pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { +int32_t taosAnalBufWriteOptInt(SAnalyticBuf *pBuf, const char *optName, int64_t optVal) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON || pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { return taosAnalJsonBufWriteOptInt(pBuf, optName, optVal); } else { - return TSDB_CODE_ANAL_BUF_INVALID_TYPE; + return TSDB_CODE_ANA_BUF_INVALID_TYPE; } } -int32_t taosAnalBufWriteOptFloat(SAnalBuf *pBuf, const char *optName, float optVal) { - if (pBuf->bufType == ANAL_BUF_TYPE_JSON || pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { +int32_t taosAnalBufWriteOptFloat(SAnalyticBuf *pBuf, const char *optName, float optVal) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON || pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { return taosAnalJsonBufWriteOptFloat(pBuf, optName, optVal); } else { - return TSDB_CODE_ANAL_BUF_INVALID_TYPE; + return TSDB_CODE_ANA_BUF_INVALID_TYPE; } } -int32_t taosAnalBufWriteColMeta(SAnalBuf *pBuf, int32_t colIndex, int32_t colType, const char *colName) { - if (pBuf->bufType == ANAL_BUF_TYPE_JSON || pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { +int32_t taosAnalBufWriteColMeta(SAnalyticBuf *pBuf, int32_t colIndex, int32_t colType, const char *colName) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON || pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { return taosAnalJsonBufWriteColMeta(pBuf, colIndex, colType, colName); } else { - return TSDB_CODE_ANAL_BUF_INVALID_TYPE; + return TSDB_CODE_ANA_BUF_INVALID_TYPE; } } -int32_t taosAnalBufWriteDataBegin(SAnalBuf *pBuf) { - if (pBuf->bufType == ANAL_BUF_TYPE_JSON || pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { +int32_t taosAnalBufWriteDataBegin(SAnalyticBuf *pBuf) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON || pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { return taosAnalJsonBufWriteDataBegin(pBuf); } else { - return TSDB_CODE_ANAL_BUF_INVALID_TYPE; + return TSDB_CODE_ANA_BUF_INVALID_TYPE; } } -int32_t taosAnalBufWriteColBegin(SAnalBuf *pBuf, int32_t colIndex) { - if (pBuf->bufType == ANAL_BUF_TYPE_JSON || pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { +int32_t taosAnalBufWriteColBegin(SAnalyticBuf *pBuf, int32_t colIndex) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON || pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { return taosAnalJsonBufWriteColBegin(pBuf, colIndex); } else { - return TSDB_CODE_ANAL_BUF_INVALID_TYPE; + return TSDB_CODE_ANA_BUF_INVALID_TYPE; } } -int32_t taosAnalBufWriteColData(SAnalBuf *pBuf, int32_t colIndex, int32_t colType, void *colValue) { - if (pBuf->bufType == ANAL_BUF_TYPE_JSON || pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { +int32_t taosAnalBufWriteColData(SAnalyticBuf *pBuf, int32_t colIndex, int32_t colType, void *colValue) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON || pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { return taosAnalJsonBufWriteColData(pBuf, colIndex, colType, colValue); } else { - return TSDB_CODE_ANAL_BUF_INVALID_TYPE; + return TSDB_CODE_ANA_BUF_INVALID_TYPE; } } -int32_t taosAnalBufWriteColEnd(SAnalBuf *pBuf, int32_t colIndex) { - if (pBuf->bufType == ANAL_BUF_TYPE_JSON || pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { +int32_t taosAnalBufWriteColEnd(SAnalyticBuf *pBuf, int32_t colIndex) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON || pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { return taosAnalJsonBufWriteColEnd(pBuf, colIndex); } else { - return TSDB_CODE_ANAL_BUF_INVALID_TYPE; + return TSDB_CODE_ANA_BUF_INVALID_TYPE; } } -int32_t taosAnalBufWriteDataEnd(SAnalBuf *pBuf) { - if (pBuf->bufType == ANAL_BUF_TYPE_JSON || pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { +int32_t taosAnalBufWriteDataEnd(SAnalyticBuf *pBuf) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON || pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { return taosAnalJsonBufWriteDataEnd(pBuf); } else { - return TSDB_CODE_ANAL_BUF_INVALID_TYPE; + return TSDB_CODE_ANA_BUF_INVALID_TYPE; } } -int32_t taosAnalBufClose(SAnalBuf *pBuf) { - if (pBuf->bufType == ANAL_BUF_TYPE_JSON || pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { +int32_t taosAnalBufClose(SAnalyticBuf *pBuf) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON || pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { return taosAnalJsonBufClose(pBuf); } else { - return TSDB_CODE_ANAL_BUF_INVALID_TYPE; + return TSDB_CODE_ANA_BUF_INVALID_TYPE; } } -static int32_t taosAnalBufGetCont(SAnalBuf *pBuf, char **ppCont, int64_t *pContLen) { +static int32_t taosAnalBufGetCont(SAnalyticBuf *pBuf, char **ppCont, int64_t *pContLen) { *ppCont = NULL; *pContLen = 0; - if (pBuf->bufType == ANAL_BUF_TYPE_JSON || pBuf->bufType == ANAL_BUF_TYPE_JSON_COL) { + if (pBuf->bufType == ANALYTICS_BUF_TYPE_JSON || pBuf->bufType == ANALYTICS_BUF_TYPE_JSON_COL) { return taosAnalJsonBufGetCont(pBuf->fileName, ppCont, pContLen); } else { - return TSDB_CODE_ANAL_BUF_INVALID_TYPE; + return TSDB_CODE_ANA_BUF_INVALID_TYPE; } } @@ -730,7 +737,7 @@ static int32_t taosAnalBufGetCont(SAnalBuf *pBuf, char **ppCont, int64_t *pContL int32_t taosAnalyticsInit() { return 0; } void taosAnalyticsCleanup() {} -SJson *taosAnalSendReqRetJson(const char *url, EAnalHttpType type, SAnalBuf *pBuf) { return NULL; } +SJson *taosAnalSendReqRetJson(const char *url, EAnalHttpType type, SAnalyticBuf *pBuf) { return NULL; } int32_t taosAnalGetAlgoUrl(const char *algoName, EAnalAlgoType type, char *url, int32_t urlLen) { return 0; } bool taosAnalGetOptStr(const char *option, const char *optName, char *optValue, int32_t optMaxLen) { return true; } @@ -738,18 +745,18 @@ bool taosAnalGetOptInt(const char *option, const char *optName, int64_t *optV int64_t taosAnalGetVersion() { return 0; } void taosAnalUpdate(int64_t newVer, SHashObj *pHash) {} -int32_t tsosAnalBufOpen(SAnalBuf *pBuf, int32_t numOfCols) { return 0; } -int32_t taosAnalBufWriteOptStr(SAnalBuf *pBuf, const char *optName, const char *optVal) { return 0; } -int32_t taosAnalBufWriteOptInt(SAnalBuf *pBuf, const char *optName, int64_t optVal) { return 0; } -int32_t taosAnalBufWriteOptFloat(SAnalBuf *pBuf, const char *optName, float optVal) { return 0; } -int32_t taosAnalBufWriteColMeta(SAnalBuf *pBuf, int32_t colIndex, int32_t colType, const char *colName) { return 0; } -int32_t taosAnalBufWriteDataBegin(SAnalBuf *pBuf) { return 0; } -int32_t taosAnalBufWriteColBegin(SAnalBuf *pBuf, int32_t colIndex) { return 0; } -int32_t taosAnalBufWriteColData(SAnalBuf *pBuf, int32_t colIndex, int32_t colType, void *colValue) { return 0; } -int32_t taosAnalBufWriteColEnd(SAnalBuf *pBuf, int32_t colIndex) { return 0; } -int32_t taosAnalBufWriteDataEnd(SAnalBuf *pBuf) { return 0; } -int32_t taosAnalBufClose(SAnalBuf *pBuf) { return 0; } -void taosAnalBufDestroy(SAnalBuf *pBuf) {} +int32_t tsosAnalBufOpen(SAnalyticBuf *pBuf, int32_t numOfCols) { return 0; } +int32_t taosAnalBufWriteOptStr(SAnalyticBuf *pBuf, const char *optName, const char *optVal) { return 0; } +int32_t taosAnalBufWriteOptInt(SAnalyticBuf *pBuf, const char *optName, int64_t optVal) { return 0; } +int32_t taosAnalBufWriteOptFloat(SAnalyticBuf *pBuf, const char *optName, float optVal) { return 0; } +int32_t taosAnalBufWriteColMeta(SAnalyticBuf *pBuf, int32_t colIndex, int32_t colType, const char *colName) { return 0; } +int32_t taosAnalBufWriteDataBegin(SAnalyticBuf *pBuf) { return 0; } +int32_t taosAnalBufWriteColBegin(SAnalyticBuf *pBuf, int32_t colIndex) { return 0; } +int32_t taosAnalBufWriteColData(SAnalyticBuf *pBuf, int32_t colIndex, int32_t colType, void *colValue) { return 0; } +int32_t taosAnalBufWriteColEnd(SAnalyticBuf *pBuf, int32_t colIndex) { return 0; } +int32_t taosAnalBufWriteDataEnd(SAnalyticBuf *pBuf) { return 0; } +int32_t taosAnalBufClose(SAnalyticBuf *pBuf) { return 0; } +void taosAnalBufDestroy(SAnalyticBuf *pBuf) {} const char *taosAnalAlgoStr(EAnalAlgoType algoType) { return 0; } EAnalAlgoType taosAnalAlgoInt(const char *algoName) { return 0; } diff --git a/source/util/src/terror.c b/source/util/src/terror.c index 00f72123dc..b8fccc5603 100644 --- a/source/util/src/terror.c +++ b/source/util/src/terror.c @@ -361,13 +361,14 @@ TAOS_DEFINE_ERROR(TSDB_CODE_MND_ANODE_TOO_MANY_ALGO, "Anode too many algori TAOS_DEFINE_ERROR(TSDB_CODE_MND_ANODE_TOO_LONG_ALGO_NAME, "Anode too long algorithm name") TAOS_DEFINE_ERROR(TSDB_CODE_MND_ANODE_TOO_MANY_ALGO_TYPE, "Anode too many algorithm type") -TAOS_DEFINE_ERROR(TSDB_CODE_ANAL_URL_RSP_IS_NULL, "Analysis service response is NULL") -TAOS_DEFINE_ERROR(TSDB_CODE_ANAL_URL_CANT_ACCESS, "Analysis service can't access") -TAOS_DEFINE_ERROR(TSDB_CODE_ANAL_ALGO_NOT_FOUND, "Analysis algorithm not found") -TAOS_DEFINE_ERROR(TSDB_CODE_ANAL_ALGO_NOT_LOAD, "Analysis algorithm not loaded") -TAOS_DEFINE_ERROR(TSDB_CODE_ANAL_BUF_INVALID_TYPE, "Analysis invalid buffer type") -TAOS_DEFINE_ERROR(TSDB_CODE_ANAL_ANODE_RETURN_ERROR, "Analysis failed since anode return error") -TAOS_DEFINE_ERROR(TSDB_CODE_ANAL_ANODE_TOO_MANY_ROWS, "Analysis failed since too many input rows for anode") +TAOS_DEFINE_ERROR(TSDB_CODE_ANA_URL_RSP_IS_NULL, "Analysis service response is NULL") +TAOS_DEFINE_ERROR(TSDB_CODE_ANA_URL_CANT_ACCESS, "Analysis service can't access") +TAOS_DEFINE_ERROR(TSDB_CODE_ANA_ALGO_NOT_FOUND, "Analysis algorithm not found") +TAOS_DEFINE_ERROR(TSDB_CODE_ANA_ALGO_NOT_LOAD, "Analysis algorithm not loaded") +TAOS_DEFINE_ERROR(TSDB_CODE_ANA_BUF_INVALID_TYPE, "Analysis invalid buffer type") +TAOS_DEFINE_ERROR(TSDB_CODE_ANA_ANODE_RETURN_ERROR, "Analysis failed since anode return error") +TAOS_DEFINE_ERROR(TSDB_CODE_ANA_ANODE_TOO_MANY_ROWS, "Analysis failed since too many input rows for anode") +TAOS_DEFINE_ERROR(TSDB_CODE_ANA_WN_DATA, "white-noise data not processed") // mnode-sma TAOS_DEFINE_ERROR(TSDB_CODE_MND_SMA_ALREADY_EXIST, "SMA already exists") From 37d79af7b9d51f7a3ebdeb4340d65ab202586126 Mon Sep 17 00:00:00 2001 From: yihaoDeng Date: Thu, 21 Nov 2024 15:28:40 +0800 Subject: [PATCH 63/88] fix compile error --- source/libs/transport/src/transCli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/libs/transport/src/transCli.c b/source/libs/transport/src/transCli.c index e2507f5381..6d90208081 100644 --- a/source/libs/transport/src/transCli.c +++ b/source/libs/transport/src/transCli.c @@ -1352,7 +1352,7 @@ bool cliConnMayAddUserInfo(SCliConn* pConn, STransMsgHead** ppHead, int32_t* msg tError("failed to decompress since %s", tstrerror(code)); return false; } else { - tDebug("decompress msg and resent, compress size %d, raw size %d", pHead, oriMsg, msgLen, oriLen); + tDebug("decompress msg and resent, compress size %d, raw size %d", msgLen, oriLen); } pHead = (STransMsgHead*)oriMsg; From ac3081a4a4f0269fe8aae119085cea77a1b37257 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Thu, 21 Nov 2024 15:39:25 +0800 Subject: [PATCH 64/88] fix(analytics): fix errors in parsing options. --- source/util/src/tanalytics.c | 46 ++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/source/util/src/tanalytics.c b/source/util/src/tanalytics.c index 49832063df..c0f864b27b 100644 --- a/source/util/src/tanalytics.c +++ b/source/util/src/tanalytics.c @@ -128,30 +128,34 @@ void taosAnalUpdate(int64_t newVer, SHashObj *pHash) { bool taosAnalGetOptStr(const char *option, const char *optName, char *optValue, int32_t optMaxLen) { char buf[TSDB_ANALYTIC_ALGO_OPTION_LEN] = {0}; - char *pStart = strstr(option, optName); - char *pEnd = strstr(pStart, ANAL_ALGO_SPLIT); + char *pStart = NULL; + char *pEnd = NULL; - if (pStart != NULL) { - if (optMaxLen > 0) { - int32_t copyLen = optMaxLen; - if (pEnd > pStart) { - copyLen = (int32_t)(pEnd - pStart - strlen(optName)); - copyLen = MIN(copyLen, optMaxLen); - tstrncpy(buf, pStart, copyLen); - } else { - int32_t len = MIN(tListLen(buf), strlen(pStart) + 1); - tstrncpy(buf, pStart, len); - } - - char *pRight = strstr(buf, "=") + 1; - strtrim(pRight); - - tstrncpy(optValue, pRight, strlen(pRight) + 1); - } - return true; - } else { + pStart = strstr(option, optName); + if (pStart == NULL) { return false; } + + pEnd = strstr(pStart, ANAL_ALGO_SPLIT); + if (optMaxLen > 0) { + int32_t copyLen = 0; + if (pEnd > pStart) { + copyLen = (int32_t)(pEnd - pStart); + copyLen = MIN(copyLen + 1, TSDB_ANALYTIC_ALGO_OPTION_LEN); + tstrncpy(buf, pStart, copyLen); + } else { + int32_t len = MIN(tListLen(buf), strlen(pStart) + 1); + tstrncpy(buf, pStart, len); + } + + char *pRight = strstr(buf, "=") + 1; + strtrim(pRight); + + int32_t vLen = MIN(optMaxLen, strlen(pRight) + 1); + tstrncpy(optValue, pRight, vLen); + } + + return true; } bool taosAnalGetOptInt(const char *option, const char *optName, int64_t *optValue) { From 274e40b559e3d9b340c80e66be3fe6972961d4a2 Mon Sep 17 00:00:00 2001 From: kailixu Date: Thu, 21 Nov 2024 16:16:48 +0800 Subject: [PATCH 65/88] test: improve the robustness of alterConfig.py --- tests/army/alter/alterConfig.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/army/alter/alterConfig.py b/tests/army/alter/alterConfig.py index ef8de0011b..6a22dd014f 100644 --- a/tests/army/alter/alterConfig.py +++ b/tests/army/alter/alterConfig.py @@ -113,6 +113,15 @@ class TDTestCase(TBase): if not result: raise Exception(f"key:{key} not found") + def checkRows(self, sql, nExpect, nRetry): + for i in range(nRetry): + res = tdSql.getResult(sql) + if len(res) == nExpect: + break + time.sleep(1) + if len(res) != nExpect: + raise Exception(f"rows:{len(res)} != {nExpect}") + def alterBypassFlag(self): """Add test case for altering bypassFlag(TD-32907) """ @@ -151,8 +160,7 @@ class TDTestCase(TBase): tdSql.query("select * from stb0") tdSql.checkRows(2) tdSql.execute("flush database db") - tdSql.query("select * from stb0") - tdSql.checkRows(0) + self.checkRows("select * from stb0", 0, 10) tdSql.execute("alter all dnodes 'bypassFlag 0'") self.checkKeyValue(tdSql.getResult("show local variables"), "bypassFlag", "0") self.checkKeyValue(tdSql.getResult("show dnode 1 variables like 'bypassFlag'"), "bypassFlag", "0", 1, 2) @@ -161,8 +169,9 @@ class TDTestCase(TBase): tdSql.query("select * from stb0") tdSql.checkRows(2) tdSql.execute("flush database db") - tdSql.query("select * from stb0") - tdSql.checkRows(2) + for i in range(5): + self.checkRows("select * from stb0", 2, 1) + time.sleep(1) # run def run(self): From e103d3963bd017d75b1018d8d5eda4e944db0f8d Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Thu, 21 Nov 2024 16:40:06 +0800 Subject: [PATCH 66/88] fix(analytics): check return value. --- source/dnode/mnode/impl/src/mndAnode.c | 45 ++++++++++--------- .../libs/executor/src/anomalywindowoperator.c | 9 +++- source/libs/executor/src/forecastoperator.c | 8 +++- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/source/dnode/mnode/impl/src/mndAnode.c b/source/dnode/mnode/impl/src/mndAnode.c index e41e7170a3..c64208600a 100644 --- a/source/dnode/mnode/impl/src/mndAnode.c +++ b/source/dnode/mnode/impl/src/mndAnode.c @@ -491,23 +491,24 @@ static int32_t mndSetDropAnodeRedoLogs(STrans *pTrans, SAnodeObj *pObj) { int32_t code = 0; SSdbRaw *pRedoRaw = mndAnodeActionEncode(pObj); if (pRedoRaw == NULL) { - code = TSDB_CODE_MND_RETURN_VALUE_NULL; - if (terrno != 0) code = terrno; - TAOS_RETURN(code); + code = terrno; + return code; } + TAOS_CHECK_RETURN(mndTransAppendRedolog(pTrans, pRedoRaw)); TAOS_CHECK_RETURN(sdbSetRawStatus(pRedoRaw, SDB_STATUS_DROPPING)); - TAOS_RETURN(code); + + return code; } static int32_t mndSetDropAnodeCommitLogs(STrans *pTrans, SAnodeObj *pObj) { int32_t code = 0; SSdbRaw *pCommitRaw = mndAnodeActionEncode(pObj); if (pCommitRaw == NULL) { - code = TSDB_CODE_MND_RETURN_VALUE_NULL; - if (terrno != 0) code = terrno; - TAOS_RETURN(code); + code = terrno; + return code; } + TAOS_CHECK_RETURN(mndTransAppendCommitlog(pTrans, pCommitRaw)); TAOS_CHECK_RETURN(sdbSetRawStatus(pCommitRaw, SDB_STATUS_DROPPED)); TAOS_RETURN(code); @@ -521,25 +522,25 @@ static int32_t mndSetDropAnodeInfoToTrans(SMnode *pMnode, STrans *pTrans, SAnode } static int32_t mndDropAnode(SMnode *pMnode, SRpcMsg *pReq, SAnodeObj *pObj) { - int32_t code = -1; + int32_t code = 0; + int32_t lino = 0; STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_RETRY, TRN_CONFLICT_NOTHING, pReq, "drop-anode"); - if (pTrans == NULL) { - code = TSDB_CODE_MND_RETURN_VALUE_NULL; - if (terrno != 0) code = terrno; - goto _OVER; - } + TSDB_CHECK_NULL(pTrans, code, lino, _OVER, terrno); + mndTransSetSerial(pTrans); + mInfo("trans:%d, to drop anode:%d", pTrans->id, pObj->id); - mInfo("trans:%d, used to drop anode:%d", pTrans->id, pObj->id); - TAOS_CHECK_GOTO(mndSetDropAnodeInfoToTrans(pMnode, pTrans, pObj, false), NULL, _OVER); - TAOS_CHECK_GOTO(mndTransPrepare(pMnode, pTrans), NULL, _OVER); + code = mndSetDropAnodeInfoToTrans(pMnode, pTrans, pObj, false); + mndReleaseAnode(pMnode, pObj); - code = 0; + TSDB_CHECK_CODE(code, lino, _OVER); + + code = mndTransPrepare(pMnode, pTrans); _OVER: mndTransDrop(pTrans); - TAOS_RETURN(code); + return code; } static int32_t mndProcessDropAnodeReq(SRpcMsg *pReq) { @@ -560,20 +561,20 @@ static int32_t mndProcessDropAnodeReq(SRpcMsg *pReq) { pObj = mndAcquireAnode(pMnode, dropReq.anodeId); if (pObj == NULL) { - code = TSDB_CODE_MND_RETURN_VALUE_NULL; - if (terrno != 0) code = terrno; + code = terrno; goto _OVER; } code = mndDropAnode(pMnode, pReq, pObj); - if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; + if (code == 0) { + code = TSDB_CODE_ACTION_IN_PROGRESS; + } _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("anode:%d, failed to drop since %s", dropReq.anodeId, tstrerror(code)); } - mndReleaseAnode(pMnode, pObj); tFreeSMDropAnodeReq(&dropReq); TAOS_RETURN(code); } diff --git a/source/libs/executor/src/anomalywindowoperator.c b/source/libs/executor/src/anomalywindowoperator.c index f7b87b98b9..3bc9c806b0 100644 --- a/source/libs/executor/src/anomalywindowoperator.c +++ b/source/libs/executor/src/anomalywindowoperator.c @@ -301,8 +301,13 @@ static int32_t anomalyParseJson(SJson* pJson, SArray* pWindows, const char* pId) if (rows < 0) { char pMsg[1024] = {0}; - tjsonGetStringValue(pJson, "msg", pMsg); - qError("%s failed to exec forecast, msg:%s", pId, pMsg); + code = tjsonGetStringValue(pJson, "msg", pMsg); + if (code) { + qError("%s failed to get error msg from rsp, unknown error", pId); + } else { + qError("%s failed to exec forecast, msg:%s", pId, pMsg); + } + return TSDB_CODE_ANA_WN_DATA; } else if (rows == 0) { return TSDB_CODE_SUCCESS; diff --git a/source/libs/executor/src/forecastoperator.c b/source/libs/executor/src/forecastoperator.c index 9ad9950bdb..fc11f70277 100644 --- a/source/libs/executor/src/forecastoperator.c +++ b/source/libs/executor/src/forecastoperator.c @@ -210,8 +210,12 @@ static int32_t forecastAnalysis(SForecastSupp* pSupp, SSDataBlock* pBlock, const tjsonGetInt32ValueFromDouble(pJson, "rows", rows, code); if (rows < 0 && code == 0) { char pMsg[1024] = {0}; - tjsonGetStringValue(pJson, "msg", pMsg); - qError("%s failed to exec forecast, msg:%s", pId, pMsg); + code = tjsonGetStringValue(pJson, "msg", pMsg); + if (code != 0) { + qError("%s failed to get msg from rsp, unknown error", pId); + } else { + qError("%s failed to exec forecast, msg:%s", pId, pMsg); + } tjsonDelete(pJson); return TSDB_CODE_ANA_WN_DATA; From 9d11a87e531ee99e8df5efd065dd7293cee2ea7d Mon Sep 17 00:00:00 2001 From: happyguoxy Date: Thu, 21 Nov 2024 18:03:28 +0800 Subject: [PATCH 67/88] test:alter crashgen report --- tests/pytest/auto_crash_gen.py | 50 ++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/tests/pytest/auto_crash_gen.py b/tests/pytest/auto_crash_gen.py index 4e4679db6a..26cd303d62 100755 --- a/tests/pytest/auto_crash_gen.py +++ b/tests/pytest/auto_crash_gen.py @@ -16,7 +16,18 @@ msg_dict = {0: "success", 1: "failed", 2: "other errors", 3: "crash occured", 4: # formal hostname = socket.gethostname() -group_url = 'https://open.feishu.cn/open-apis/bot/v2/hook/56c333b5-eae9-4c18-b0b6-7e4b7174f5c9' +group_url_test = ( + 'https://open.feishu.cn/open-apis/bot/v2/hook/7e409a8e-4390-4043-80d0-4e0dd2cbae7d' +) + +notification_robot_url = ( + "https://open.feishu.cn/open-apis/bot/v2/hook/56c333b5-eae9-4c18-b0b6-7e4b7174f5c9" +) + +alert_robot_url = ( + "https://open.feishu.cn/open-apis/bot/v2/hook/02363732-91f1-49c4-879c-4e98cf31a5f3" +) + def get_msg(text): return { @@ -37,12 +48,12 @@ def get_msg(text): } -def send_msg(json): +def send_msg(url:str,json:dict): headers = { 'Content-Type': 'application/json' } - req = requests.post(url=group_url, headers=headers, json=json) + req = requests.post(url=url, headers=headers, json=json) inf = req.json() if "StatusCode" in inf and inf["StatusCode"] == 0: pass @@ -355,18 +366,27 @@ def main(): core_dir = "none" text = f''' - exit status: {msg_dict[status]} - test scope: crash_gen - owner: pxiao - hostname: {hostname} - start time: {starttime} - end time: {endtime} - git commit : {git_commit} - log dir: {log_dir} - core dir: {core_dir} - cmd: {cmd}''' - - send_msg(get_msg(text)) + Result: {msg_dict[status]} + + Details + Owner: Jayden Jia + Start time: {starttime} + End time: {endtime} + Hostname: {hostname} + Commit: {git_commit} + Cmd: {cmd} + Log dir: {log_dir} + Core dir: {core_dir} + ''' + text_result=text.split("Result: ")[1].split("Details")[0].strip() + print(text_result) + + if text_result == "success": + send_msg(notification_robot_url, get_msg(text)) + else: + send_msg(alert_robot_url, get_msg(text)) + + #send_msg(get_msg(text)) except Exception as e: print("exception:", e) exit(status) From 4a05f3ffd0cbe60ee1a50681623816137dfb2ab5 Mon Sep 17 00:00:00 2001 From: happyguoxy Date: Thu, 21 Nov 2024 18:03:42 +0800 Subject: [PATCH 68/88] test:alter crashgen report --- tests/pytest/auto_crash_gen_valgrind.py | 52 +++++++++++++++++-------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/tests/pytest/auto_crash_gen_valgrind.py b/tests/pytest/auto_crash_gen_valgrind.py index 1e0de6ace1..9eab6acf3b 100755 --- a/tests/pytest/auto_crash_gen_valgrind.py +++ b/tests/pytest/auto_crash_gen_valgrind.py @@ -19,7 +19,18 @@ msg_dict = {0: "success", 1: "failed", 2: "other errors", 3: "crash occured", 4: # formal hostname = socket.gethostname() -group_url = 'https://open.feishu.cn/open-apis/bot/v2/hook/56c333b5-eae9-4c18-b0b6-7e4b7174f5c9' +group_url_test = ( + 'https://open.feishu.cn/open-apis/bot/v2/hook/7e409a8e-4390-4043-80d0-4e0dd2cbae7d' +) + +notification_robot_url = ( + "https://open.feishu.cn/open-apis/bot/v2/hook/56c333b5-eae9-4c18-b0b6-7e4b7174f5c9" +) + +alert_robot_url = ( + "https://open.feishu.cn/open-apis/bot/v2/hook/02363732-91f1-49c4-879c-4e98cf31a5f3" +) + def get_msg(text): return { @@ -40,13 +51,12 @@ def get_msg(text): } -def send_msg(json): +def send_msg(url:str,json:dict): headers = { 'Content-Type': 'application/json' } - - req = requests.post(url=group_url, headers=headers, json=json) + req = requests.post(url=url, headers=headers, json=json) inf = req.json() if "StatusCode" in inf and inf["StatusCode"] == 0: pass @@ -389,18 +399,28 @@ def main(): core_dir = "none" text = f''' - exit status: {msg_dict[status]} - test scope: crash_gen - owner: pxiao - hostname: {hostname} - start time: {starttime} - end time: {endtime} - git commit : {git_commit} - log dir: {log_dir} - core dir: {core_dir} - cmd: {cmd}''' - - send_msg(get_msg(text)) + Result: {msg_dict[status]} + + Details + Owner: Jayden Jia + Start time: {starttime} + End time: {endtime} + Hostname: {hostname} + Commit: {git_commit} + Cmd: {cmd} + Log dir: {log_dir} + Core dir: {core_dir} + ''' + + text_result=text.split("Result: ")[1].split("Details")[0].strip() + print(text_result) + + if text_result == "success": + send_msg(notification_robot_url, get_msg(text)) + else: + send_msg(alert_robot_url, get_msg(text)) + + #send_msg(get_msg(text)) except Exception as e: print("exception:", e) exit(status) From d426209113a3b10c4d507283e8a94da5e17568d8 Mon Sep 17 00:00:00 2001 From: happyguoxy Date: Thu, 21 Nov 2024 18:03:53 +0800 Subject: [PATCH 69/88] test:alter crashgen report --- .../pytest/auto_crash_gen_valgrind_cluster.py | 51 +++++++++++++------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/tests/pytest/auto_crash_gen_valgrind_cluster.py b/tests/pytest/auto_crash_gen_valgrind_cluster.py index 22f453e51e..b6c98fbf94 100755 --- a/tests/pytest/auto_crash_gen_valgrind_cluster.py +++ b/tests/pytest/auto_crash_gen_valgrind_cluster.py @@ -16,7 +16,18 @@ msg_dict = {0: "success", 1: "failed", 2: "other errors", 3: "crash occured", 4: # formal hostname = socket.gethostname() -group_url = 'https://open.feishu.cn/open-apis/bot/v2/hook/56c333b5-eae9-4c18-b0b6-7e4b7174f5c9' +group_url_test = ( + 'https://open.feishu.cn/open-apis/bot/v2/hook/7e409a8e-4390-4043-80d0-4e0dd2cbae7d' +) + +notification_robot_url = ( + "https://open.feishu.cn/open-apis/bot/v2/hook/56c333b5-eae9-4c18-b0b6-7e4b7174f5c9" +) + +alert_robot_url = ( + "https://open.feishu.cn/open-apis/bot/v2/hook/02363732-91f1-49c4-879c-4e98cf31a5f3" +) + def get_msg(text): return { @@ -37,12 +48,12 @@ def get_msg(text): } -def send_msg(json): +def send_msg(url:str,json:dict): headers = { 'Content-Type': 'application/json' } - req = requests.post(url=group_url, headers=headers, json=json) + req = requests.post(url=url, headers=headers, json=json) inf = req.json() if "StatusCode" in inf and inf["StatusCode"] == 0: pass @@ -376,18 +387,28 @@ def main(): core_dir = "none" text = f''' - exit status: {msg_dict[status]} - test scope: crash_gen - owner: pxiao - hostname: {hostname} - start time: {starttime} - end time: {endtime} - git commit : {git_commit} - log dir: {log_dir} - core dir: {core_dir} - cmd: {cmd}''' - - send_msg(get_msg(text)) + Result: {msg_dict[status]} + + Details + Owner: Jayden Jia + Start time: {starttime} + End time: {endtime} + Hostname: {hostname} + Commit: {git_commit} + Cmd: {cmd} + Log dir: {log_dir} + Core dir: {core_dir} + ''' + + text_result=text.split("Result: ")[1].split("Details")[0].strip() + print(text_result) + + if text_result == "success": + send_msg(notification_robot_url, get_msg(text)) + else: + send_msg(alert_robot_url, get_msg(text)) + + #send_msg(get_msg(text)) except Exception as e: print("exception:", e) exit(status) From 69604c2d7dafe8b826546bf1b27e62bc97180697 Mon Sep 17 00:00:00 2001 From: happyguoxy Date: Thu, 21 Nov 2024 18:05:44 +0800 Subject: [PATCH 70/88] test:alter crashgen report --- .../pytest/auto_crash_gen_valgrind_cluster.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/pytest/auto_crash_gen_valgrind_cluster.py b/tests/pytest/auto_crash_gen_valgrind_cluster.py index b6c98fbf94..522ad48640 100755 --- a/tests/pytest/auto_crash_gen_valgrind_cluster.py +++ b/tests/pytest/auto_crash_gen_valgrind_cluster.py @@ -387,18 +387,18 @@ def main(): core_dir = "none" text = f''' - Result: {msg_dict[status]} +Result: {msg_dict[status]} - Details - Owner: Jayden Jia - Start time: {starttime} - End time: {endtime} - Hostname: {hostname} - Commit: {git_commit} - Cmd: {cmd} - Log dir: {log_dir} - Core dir: {core_dir} - ''' +Details +Owner: Jayden Jia +Start time: {starttime} +End time: {endtime} +Hostname: {hostname} +Commit: {git_commit} +Cmd: {cmd} +Log dir: {log_dir} +Core dir: {core_dir} +''' text_result=text.split("Result: ")[1].split("Details")[0].strip() print(text_result) From 7baf37162885a6b293f2bfee11129afbf5668eae Mon Sep 17 00:00:00 2001 From: happyguoxy Date: Thu, 21 Nov 2024 18:05:51 +0800 Subject: [PATCH 71/88] test:alter crashgen report --- tests/pytest/auto_crash_gen_valgrind.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/pytest/auto_crash_gen_valgrind.py b/tests/pytest/auto_crash_gen_valgrind.py index 9eab6acf3b..b346aca308 100755 --- a/tests/pytest/auto_crash_gen_valgrind.py +++ b/tests/pytest/auto_crash_gen_valgrind.py @@ -399,18 +399,18 @@ def main(): core_dir = "none" text = f''' - Result: {msg_dict[status]} - - Details - Owner: Jayden Jia - Start time: {starttime} - End time: {endtime} - Hostname: {hostname} - Commit: {git_commit} - Cmd: {cmd} - Log dir: {log_dir} - Core dir: {core_dir} - ''' +Result: {msg_dict[status]} + +Details +Owner: Jayden Jia +Start time: {starttime} +End time: {endtime} +Hostname: {hostname} +Commit: {git_commit} +Cmd: {cmd} +Log dir: {log_dir} +Core dir: {core_dir} +''' text_result=text.split("Result: ")[1].split("Details")[0].strip() print(text_result) From c02e4085d6d81dde6399baf0c32038febd3912f9 Mon Sep 17 00:00:00 2001 From: happyguoxy Date: Thu, 21 Nov 2024 18:05:58 +0800 Subject: [PATCH 72/88] test:alter crashgen report --- tests/pytest/auto_crash_gen.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/pytest/auto_crash_gen.py b/tests/pytest/auto_crash_gen.py index 26cd303d62..f6b31b4691 100755 --- a/tests/pytest/auto_crash_gen.py +++ b/tests/pytest/auto_crash_gen.py @@ -366,18 +366,18 @@ def main(): core_dir = "none" text = f''' - Result: {msg_dict[status]} - - Details - Owner: Jayden Jia - Start time: {starttime} - End time: {endtime} - Hostname: {hostname} - Commit: {git_commit} - Cmd: {cmd} - Log dir: {log_dir} - Core dir: {core_dir} - ''' +Result: {msg_dict[status]} + +Details +Owner: Jayden Jia +Start time: {starttime} +End time: {endtime} +Hostname: {hostname} +Commit: {git_commit} +Cmd: {cmd} +Log dir: {log_dir} +Core dir: {core_dir} +''' text_result=text.split("Result: ")[1].split("Details")[0].strip() print(text_result) From 4b947a568ad8152e1950fb91844211e54345e757 Mon Sep 17 00:00:00 2001 From: Shengliang Guan Date: Thu, 21 Nov 2024 18:13:55 +0800 Subject: [PATCH 73/88] doc: minor changes --- docs/zh/26-tdinternal/{10-cache => 10-cache.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/zh/26-tdinternal/{10-cache => 10-cache.md} (100%) diff --git a/docs/zh/26-tdinternal/10-cache b/docs/zh/26-tdinternal/10-cache.md similarity index 100% rename from docs/zh/26-tdinternal/10-cache rename to docs/zh/26-tdinternal/10-cache.md From 8838ea4d38c0c7e9e2ba134ae91b6438c191eba6 Mon Sep 17 00:00:00 2001 From: yihaoDeng Date: Thu, 21 Nov 2024 18:15:26 +0800 Subject: [PATCH 74/88] fix conn timeout --- source/libs/transport/inc/transComm.h | 2 +- source/libs/transport/src/transCli.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/source/libs/transport/inc/transComm.h b/source/libs/transport/inc/transComm.h index 5c79b379ed..bc682eca4c 100644 --- a/source/libs/transport/inc/transComm.h +++ b/source/libs/transport/inc/transComm.h @@ -96,7 +96,7 @@ typedef void* queue[2]; // #define TRANS_RETRY_COUNT_LIMIT 100 // retry count limit // #define TRANS_RETRY_INTERVAL 15 // retry interval (ms) -#define TRANS_CONN_TIMEOUT 3000 // connect timeout (ms) +#define TRANS_CONN_TIMEOUT 5000 // connect timeout (ms) #define TRANS_READ_TIMEOUT 3000 // read timeout (ms) #define TRANS_PACKET_LIMIT 1024 * 1024 * 512 diff --git a/source/libs/transport/src/transCli.c b/source/libs/transport/src/transCli.c index c03d3418fa..27f84640fb 100644 --- a/source/libs/transport/src/transCli.c +++ b/source/libs/transport/src/transCli.c @@ -725,6 +725,7 @@ void cliConnTimeout(uv_timer_t* handle) { return; } + cliMayUpdateFqdnCache(pThrd->fqdn2ipCache, conn->dstAddr); tTrace("%s conn %p conn timeout", CONN_GET_INST_LABEL(conn), conn); TAOS_UNUSED(transUnrefCliHandle(conn)); } From af149f41eda9096330568cd753059f97f9c2f5c7 Mon Sep 17 00:00:00 2001 From: yihaoDeng Date: Thu, 21 Nov 2024 18:26:18 +0800 Subject: [PATCH 75/88] fix conn timeout --- source/libs/transport/src/transCli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/libs/transport/src/transCli.c b/source/libs/transport/src/transCli.c index 27f84640fb..2a2d5076e2 100644 --- a/source/libs/transport/src/transCli.c +++ b/source/libs/transport/src/transCli.c @@ -726,7 +726,7 @@ void cliConnTimeout(uv_timer_t* handle) { } cliMayUpdateFqdnCache(pThrd->fqdn2ipCache, conn->dstAddr); - tTrace("%s conn %p conn timeout", CONN_GET_INST_LABEL(conn), conn); + tTrace("%s conn %p failed to %s since conn timeout", CONN_GET_INST_LABEL(conn), conn->dstAddr, conn); TAOS_UNUSED(transUnrefCliHandle(conn)); } From 72b95fa3165a6555807ef270bf821b436379f956 Mon Sep 17 00:00:00 2001 From: yihaoDeng Date: Thu, 21 Nov 2024 19:48:15 +0800 Subject: [PATCH 76/88] fix compile error --- source/libs/transport/src/transCli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/libs/transport/src/transCli.c b/source/libs/transport/src/transCli.c index 2a2d5076e2..4b29ace8c3 100644 --- a/source/libs/transport/src/transCli.c +++ b/source/libs/transport/src/transCli.c @@ -726,7 +726,7 @@ void cliConnTimeout(uv_timer_t* handle) { } cliMayUpdateFqdnCache(pThrd->fqdn2ipCache, conn->dstAddr); - tTrace("%s conn %p failed to %s since conn timeout", CONN_GET_INST_LABEL(conn), conn->dstAddr, conn); + tTrace("%s conn %p failed to connect %s since conn timeout", CONN_GET_INST_LABEL(conn), conn, conn->dstAddr, conn); TAOS_UNUSED(transUnrefCliHandle(conn)); } From 91ce69e6af527e8b06a877920d7d11e8f0b6c1d7 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Thu, 21 Nov 2024 20:02:23 +0800 Subject: [PATCH 77/88] fix(analytics): check the null ptr. --- source/libs/executor/src/forecastoperator.c | 2 +- source/util/src/tanalytics.c | 8 +++++++- source/util/src/terror.c | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/source/libs/executor/src/forecastoperator.c b/source/libs/executor/src/forecastoperator.c index fc11f70277..bf1efc54ca 100644 --- a/source/libs/executor/src/forecastoperator.c +++ b/source/libs/executor/src/forecastoperator.c @@ -374,7 +374,7 @@ static int32_t forecastNext(SOperatorInfo* pOperator, SSDataBlock** ppRes) { SForecastOperatorInfo* pInfo = pOperator->info; SSDataBlock* pResBlock = pInfo->pRes; SForecastSupp* pSupp = &pInfo->forecastSupp; - SAnalyticBuf* pBuf = &pSupp->analBuf; + SAnalyticBuf* pBuf = &pSupp->analBuf; int64_t st = taosGetTimestampUs(); int32_t numOfBlocks = pSupp->numOfBlocks; const char* pId = GET_TASKID(pOperator->pTaskInfo); diff --git a/source/util/src/tanalytics.c b/source/util/src/tanalytics.c index c0f864b27b..579746be40 100644 --- a/source/util/src/tanalytics.c +++ b/source/util/src/tanalytics.c @@ -148,7 +148,13 @@ bool taosAnalGetOptStr(const char *option, const char *optName, char *optValue, tstrncpy(buf, pStart, len); } - char *pRight = strstr(buf, "=") + 1; + char *pRight = strstr(buf, "="); + if (pRight == NULL) { + return false; + } else { + pRight += 1; + } + strtrim(pRight); int32_t vLen = MIN(optMaxLen, strlen(pRight) + 1); diff --git a/source/util/src/terror.c b/source/util/src/terror.c index b8fccc5603..9e8a85d301 100644 --- a/source/util/src/terror.c +++ b/source/util/src/terror.c @@ -363,7 +363,7 @@ TAOS_DEFINE_ERROR(TSDB_CODE_MND_ANODE_TOO_MANY_ALGO_TYPE, "Anode too many algori TAOS_DEFINE_ERROR(TSDB_CODE_ANA_URL_RSP_IS_NULL, "Analysis service response is NULL") TAOS_DEFINE_ERROR(TSDB_CODE_ANA_URL_CANT_ACCESS, "Analysis service can't access") -TAOS_DEFINE_ERROR(TSDB_CODE_ANA_ALGO_NOT_FOUND, "Analysis algorithm not found") +TAOS_DEFINE_ERROR(TSDB_CODE_ANA_ALGO_NOT_FOUND, "Analysis algorithm is missing") TAOS_DEFINE_ERROR(TSDB_CODE_ANA_ALGO_NOT_LOAD, "Analysis algorithm not loaded") TAOS_DEFINE_ERROR(TSDB_CODE_ANA_BUF_INVALID_TYPE, "Analysis invalid buffer type") TAOS_DEFINE_ERROR(TSDB_CODE_ANA_ANODE_RETURN_ERROR, "Analysis failed since anode return error") From 9ea53119e5c66efd2e32a1d3dd6b20e9a9a7c143 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Thu, 21 Nov 2024 22:44:53 +0800 Subject: [PATCH 78/88] fix(analytics): check return value. --- source/util/src/tanalytics.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/source/util/src/tanalytics.c b/source/util/src/tanalytics.c index 579746be40..68bbbb7e99 100644 --- a/source/util/src/tanalytics.c +++ b/source/util/src/tanalytics.c @@ -138,11 +138,10 @@ bool taosAnalGetOptStr(const char *option, const char *optName, char *optValue, pEnd = strstr(pStart, ANAL_ALGO_SPLIT); if (optMaxLen > 0) { - int32_t copyLen = 0; if (pEnd > pStart) { - copyLen = (int32_t)(pEnd - pStart); - copyLen = MIN(copyLen + 1, TSDB_ANALYTIC_ALGO_OPTION_LEN); - tstrncpy(buf, pStart, copyLen); + int32_t len = (int32_t)(pEnd - pStart); + len = MIN(len + 1, TSDB_ANALYTIC_ALGO_OPTION_LEN); + tstrncpy(buf, pStart, len); } else { int32_t len = MIN(tListLen(buf), strlen(pStart) + 1); tstrncpy(buf, pStart, len); @@ -155,7 +154,7 @@ bool taosAnalGetOptStr(const char *option, const char *optName, char *optValue, pRight += 1; } - strtrim(pRight); + int32_t unused = strtrim(pRight); int32_t vLen = MIN(optMaxLen, strlen(pRight) + 1); tstrncpy(optValue, pRight, vLen); From 59b63d5642d60629928c740c69d2f165ae80a06e Mon Sep 17 00:00:00 2001 From: yihaoDeng Date: Fri, 22 Nov 2024 08:26:07 +0800 Subject: [PATCH 79/88] fix conn timeout --- source/libs/transport/src/transCli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/libs/transport/src/transCli.c b/source/libs/transport/src/transCli.c index 4b29ace8c3..5bd6d244a4 100644 --- a/source/libs/transport/src/transCli.c +++ b/source/libs/transport/src/transCli.c @@ -726,7 +726,7 @@ void cliConnTimeout(uv_timer_t* handle) { } cliMayUpdateFqdnCache(pThrd->fqdn2ipCache, conn->dstAddr); - tTrace("%s conn %p failed to connect %s since conn timeout", CONN_GET_INST_LABEL(conn), conn, conn->dstAddr, conn); + tTrace("%s conn %p failed to connect %s since conn timeout", CONN_GET_INST_LABEL(conn), conn, conn->dstAddr); TAOS_UNUSED(transUnrefCliHandle(conn)); } From 3809cdc556f36b0f4656022cdcce4eb9370e974b Mon Sep 17 00:00:00 2001 From: haoranchen Date: Fri, 22 Nov 2024 10:45:33 +0800 Subject: [PATCH 80/88] ci: skip run test when target branch is docs-cloud --- Jenkinsfile2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile2 b/Jenkinsfile2 index 4bb0754343..3207f2a52c 100644 --- a/Jenkinsfile2 +++ b/Jenkinsfile2 @@ -450,8 +450,8 @@ pipeline { stage('run test') { when { - allOf { - not { expression { file_no_doc_changed == '' }} + expression { + file_no_doc_changed != '' && env.CHANGE_TARGET != 'docs-cloud' } } parallel { From ae39eee324f64aac0c04dd62a5ff1d5f041203a9 Mon Sep 17 00:00:00 2001 From: eryue1993 <93116359+eryue1993@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:57:50 +0800 Subject: [PATCH 81/88] Update 10-cache.md --- docs/zh/26-tdinternal/10-cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh/26-tdinternal/10-cache.md b/docs/zh/26-tdinternal/10-cache.md index 543bec555a..698f4ee87a 100644 --- a/docs/zh/26-tdinternal/10-cache.md +++ b/docs/zh/26-tdinternal/10-cache.md @@ -1,6 +1,6 @@ --- sidebar_label: 数据缓存 -title: 数据订阅 +title: 数据缓存 toc_max_heading_level: 4 --- 在现代物联网(IoT)和工业互联网(IIoT)应用中,数据的高效管理对系统性能和用户体验至关重要。为了应对高并发环境下的实时读写需求,TDengine 设计了一套完整的缓存机制,包括写缓存、读缓存、元数据缓存和文件系统缓存。这些缓存机制紧密结合,既能优化数据查询的响应速度,又能提高数据写入的效率,同时保障数据的可靠性和系统的高可用性。通过灵活配置缓存参数,TDengine 为用户提供了性能与成本之间的最佳平衡。 From 772cd9bdfa62a5fc1fa443b54f0ed90d49ab6ee1 Mon Sep 17 00:00:00 2001 From: Shengliang Guan Date: Fri, 22 Nov 2024 13:45:54 +0800 Subject: [PATCH 82/88] doc: remove analysis dir --- .../06-advanced/06-data-analysis/01-arima.md | 54 --- .../06-data-analysis/02-holtwinters.md | 43 --- .../06-data-analysis/03-anomaly-detection.md | 46 --- .../zh/06-advanced/06-data-analysis/addins.md | 170 --------- docs/zh/06-advanced/06-data-analysis/index.md | 322 ------------------ .../06-data-analysis/pic/data-analysis.png | Bin 50231 -> 0 bytes .../06-advanced/06-data-analysis/pic/dir.png | Bin 7286 -> 0 bytes 7 files changed, 635 deletions(-) delete mode 100644 docs/zh/06-advanced/06-data-analysis/01-arima.md delete mode 100644 docs/zh/06-advanced/06-data-analysis/02-holtwinters.md delete mode 100644 docs/zh/06-advanced/06-data-analysis/03-anomaly-detection.md delete mode 100644 docs/zh/06-advanced/06-data-analysis/addins.md delete mode 100644 docs/zh/06-advanced/06-data-analysis/index.md delete mode 100644 docs/zh/06-advanced/06-data-analysis/pic/data-analysis.png delete mode 100644 docs/zh/06-advanced/06-data-analysis/pic/dir.png diff --git a/docs/zh/06-advanced/06-data-analysis/01-arima.md b/docs/zh/06-advanced/06-data-analysis/01-arima.md deleted file mode 100644 index b9d63e924f..0000000000 --- a/docs/zh/06-advanced/06-data-analysis/01-arima.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: "ARIMA" -sidebar_label: "ARIMA" ---- - -本节讲述 ARIMA 算法模型的使用方法。 - -## 功能概述 - -ARIMA 即自回归移动平均模型(Autoregressive Integrated Moving Average, ARIMA),也记作 ARIMA(p,d,q),是统计模型中最常见的一种用来进行时间序列预测的模型。 -ARIMA 模型是一种自回归模型,只需要自变量即可预测后续的值。ARIMA 模型要求时间序列**平稳**,或经过差分处理后平稳,如果是不平稳的数据,**无法**获得正确的结果。 - ->平稳的时间序列:其性质不随观测时间的变化而变化。具有趋势或季节性的时间序列不是平稳时间序列——趋势和季节性使得时间序列在不同时段呈现不同性质。 - -以下参数可以动态输入,控制预测过程中生成合适的 ARIMA 模型。 - -- p= 自回归模型阶数 -- d= 差分阶数 -- q= 移动平均模型阶数 - - -### 参数 -分析平台中使用自动化的 ARIMA 模型进行计算,因此每次计算的时候会根据输入的数据自动拟合最合适的模型,然后根据该模型进行预测输出结果。 -|参数|说明|必填项| -|---|---|-----| -|period|输入时间序列每个周期包含的数据点个数,如果不设置该参数或该参数设置为 0,将使用非季节性/周期性的 ARIMA 模型预测|选填| -|start_p|自回归模型阶数的起始值,0 开始的整数,不推荐大于 10|选填| -|max_p|自回归模型阶数的结束值,0 开始的整数,不推荐大于 10|选填| -|start_q|移动平均模型阶数的起始值,0 开始的整数,不推荐大于 10|选填| -|max_q|移动平均模型阶数的结束值,0 开始的整数,不推荐大于 10|选填| -|d|差分阶数|选填| - -`start_p`、`max_p` `start_q` `max_q` 四个参数约束了模型在多大的范围内去搜寻合适的最优解。相同输入数据的条件下,参数范围越大,消耗的资源越多,系统响应的时间越长。 - -### 示例及结果 -针对 i32 列进行数据预测,输入列 i32 每 10 个点是一个周期,start_p 起始是 1, 最大拟合是 5,start_q 是 1,最大值是 5,预测结果中返回 95% 置信区间范围边界。 -``` -FORECAST(i32, "algo=arima,alpha=95,period=10,start_p=1,max_p=5,start_q=1,max_q=5") -``` - -```json5 -{ -"rows": fc_rows, // 返回结果的行数 -"period": period, // 返回结果的周期性,同输入 -"alpha": alpha, // 返回结果的置信区间,同输入 -"algo": "arima", // 返回结果使用的算法 -"mse": mse, // 拟合输入时间序列时候生成模型的最小均方误差(MSE) -"res": res // 列模式的结果 -} -``` - -### 参考文献 -- https://en.wikipedia.org/wiki/Autoregressive_moving-average_model -- https://baike.baidu.com/item/%E8%87%AA%E5%9B%9E%E5%BD%92%E6%BB%91%E5%8A%A8%E5%B9%B3%E5%9D%87%E6%A8%A1%E5%9E%8B/5023931?fromtitle=ARMA%E6%A8%A1%E5%9E%8B&fromid=8048415 diff --git a/docs/zh/06-advanced/06-data-analysis/02-holtwinters.md b/docs/zh/06-advanced/06-data-analysis/02-holtwinters.md deleted file mode 100644 index 38662ca2b3..0000000000 --- a/docs/zh/06-advanced/06-data-analysis/02-holtwinters.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: "HoltWinters" -sidebar_label: "HoltWinters" ---- - -本节讲述 HoltWinters 算法模型的使用方法。 - -## 功能概述 -HoltWinters 模型又称为多次指数平滑模型(EMA)。适用于含有线性趋势和周期波动的非平稳序列,利用指数平滑法让模型参数不断适应非平稳序列的变化,并对未来趋势进行**短期**预测。 -HoltWinters 有两种不同的季节性组成部分,当季节变化在该时间序列中大致保持不变时,通常选择**加法模型**;而当季节变化与时间序列的水平成比例变化时,通常选择**乘法模型**。 -该模型对于返回数据不提供计算的置信区间范围结果,在 95% 置信区间的上下界结果与预测结果相同。 - - -### 参数 - -分析平台中使用自动化的 HoltWinters 模型进行计算,因此每次计算的时候会根据输入的数据自动拟合最合适的模型,然后根据该模型进行预测输出结果。 -|参数|说明|必填项| -|---|---|---| -|period|输入时间序列每个周期包含的数据点个数。如果不设置该参数或该参数设置为 0,将使用一次(简单)指数平滑方式进行数据拟合,并据此进行未来数据的预测|选填| -|trend|趋势模型使用加法模型还是乘法模型|选填| -|seasonal|季节性采用加法模型还是乘法模型|选填| - -参数 `trend` 和 `seasonal`的均可以选择 `add` (加法模型)或 `mul`(乘法模型)。 - -### 示例及结果 -针对 i32 列进行数据预测,输入列 i32 每 10 个点是一个周期,趋势采用乘法模型,季节采用乘法模型 -``` -FORECAST(i32, "algo=holtwinters,period=10,trend=mul,seasonal=mul") -``` - -```json5 -{ -"rows": rows, // 返回结果的行数 -"period": period, // 返回结果的周期性,该结果与输入的周期性相同,如果没有周期性,该值为 0 -"algo": 'holtwinters' // 返回结果使用的计算模型 -"mse": mse, // 最小均方误差(minmum square error) -"res": res // 具体的结果,按照列形式返回的结果。一般意义上包含了两列 [timestamp][fc_results]。 -} -``` - -### 参考文献 -- https://en.wikipedia.org/wiki/Exponential_smoothing -- https://orangematter.solarwinds.com/2019/12/15/holt-winters-forecasting-simplified/ diff --git a/docs/zh/06-advanced/06-data-analysis/03-anomaly-detection.md b/docs/zh/06-advanced/06-data-analysis/03-anomaly-detection.md deleted file mode 100644 index bdfa455ae3..0000000000 --- a/docs/zh/06-advanced/06-data-analysis/03-anomaly-detection.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: "Anomaly-detection" -sidebar_label: "Anomaly-detection" ---- - -本节讲述异常检测算法模型的使用方法。 - -## 概述 -分析平台提供了 6 种异常检查模型,6 种异常检查模型分为 3 个类别,分别属于基于统计的异常检测模型、基于数据密度的检测模型、基于深度学习的异常检测模型。在不指定异常检测使用的方法的情况下,默认调用 iqr 的方法进行计算。 - - -### 统计学异常检测方法 - -- k-sigma[1]: 即 ***68–95–99.7 rule*** 。***k***值默认为 3,即序列均值的 3 倍标准差范围为边界,超过边界的是异常值。KSigma 要求数据整体上服从正态分布,如果一个点偏离均值 K 倍标准差,则该点被视为异常点. - -|参数|说明|是否必选|默认值| -|---|---|---|---| -|k|标准差倍数|选填|3| - - -- IQR[2]:四分位距 (Interquartile range, IQR) 是一种衡量变异性的方法. 四分位数将一个按等级排序的数据集划分为四个相等的部分。即 Q1(第 1 个四分位数)、Q2(第 2 个四分位数)和 Q3(第 3 个四分位数)。IQR 定义为 Q3–Q1,位于 Q3+1.5。无输入参数。 - -- Grubbs[3]: 又称为 Grubbs' test,即最大标准残差测试。Grubbs 通常用作检验最大值、最小值偏离均值的程度是否为异常,该单变量数据集遵循近似标准正态分布。非正态分布数据集不能使用该方法。无输入参数。 - -- SHESD[4]: 带有季节性的 ESD 检测算法。ESD 可以检测时间序列数据的多异常点。需要指定异常点比例的上界***k***,最差的情况是至多 49.9%。数据集的异常比例一般不超过 5% - -|参数|说明|是否必选|默认值| -|---|---|---|---| -|k|异常点在输入数据集中占比,范围是$`1\le K \le 49.9`$ |选填|5| - - -### 基于数据密度的检测方法 -LOF[5]: 局部离群因子(LOF,又叫局部异常因子)算法是 Breunig 于 2000 年提出的一种基于密度的局部离群点检测算法,该方法适用于不同类簇密度分散情况迥异的数据。根据数据点周围的数据密集情况,首先计算每个数据点的一个局部可达密度,然后通过局部可达密度进一步计算得到每个数据点的一个离群因子,该离群因子即标识了一个数据点的离群程度,因子值越大,表示离群程度越高,因子值越小,表示离群程度越低。最后,输出离群程度最大的 top(n) 个点。 - - -### 基于深度学习的检测方法 -使用自动编码器的异常检测模型。可以对具有周期性的数据具有较好的检测结果。但是使用该模型需要针对输入的时序数据进行训练,同时将训练完成的模型部署到服务目录中,才能够运行与使用。 - - -### 参考文献 -1. https://en.wikipedia.org/wiki/68%E2%80%9395%E2%80%9399.7_rule -2. https://en.wikipedia.org/wiki/Interquartile_range -3. Adikaram, K. K. L. B.; Hussein, M. A.; Effenberger, M.; Becker, T. (2015-01-14). "Data Transformation Technique to Improve the Outlier Detection Power of Grubbs's Test for Data Expected to Follow Linear Relation". Journal of Applied Mathematics. 2015: 1–9. doi:10.1155/2015/708948. -4. Hochenbaum, O. S. Vallis, and A. Kejariwal. 2017. Automatic Anomaly Detection in the Cloud Via Statistical Learning. arXiv preprint arXiv:1704.07706 (2017). -5. Breunig, M. M.; Kriegel, H.-P.; Ng, R. T.; Sander, J. (2000). LOF: Identifying Density-based Local Outliers (PDF). Proceedings of the 2000 ACM SIGMOD International Conference on Management of Data. SIGMOD. pp. 93–104. doi:10.1145/335191.335388. ISBN 1-58113-217-4. - diff --git a/docs/zh/06-advanced/06-data-analysis/addins.md b/docs/zh/06-advanced/06-data-analysis/addins.md deleted file mode 100644 index c0b8921718..0000000000 --- a/docs/zh/06-advanced/06-data-analysis/addins.md +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: "addins" -sidebar_label: "addins" ---- - -本节说明如何将自己开发的预测算法和异常检测算法整合到 TDengine 分析平台,并能够通过 SQL 语句进行调用。 - -## 目录结构 - -![数据分析功能架构图](./pic/dir.png) - -|目录|说明| -|---|---| -|taos|Python 源代码目录,其下包含了算法具体保存目录 algo,放置杂项目录 misc,单元测试和集成测试目录 test。 algo 目录下 ad 放置异常检测算法代码,fc 放置预测算法代码| -|script|是安装脚本和发布脚本放置目录| -|model|放置针对数据集完成的训练模型| -|cfg|配置文件目录| - -## 约定与限制 - -定义异常检测算法的 Python 代码文件需放在 /taos/algo/ad 目录中,预测算法 Python 代码文件需要放在 /taos/algo/fc 目录中,以确保系统启动的时候能够正常加载对应目录下的 Python 文件。 - - -### 类命名规范 - -算法类的名称需要以下划线开始,以 Service 结尾。例如:_KsigmaService 是 KSigma 异常检测算法的实现类。 - -### 类继承约定 - -- 异常检测算法需要从 `AbstractAnomalyDetectionService` 继承,并实现其核心抽象方法 `execute` -- 预测算法需要从 `AbstractForecastService` 继承,同样需要实现其核心抽象方法 `execute` - -### 类属性初始化 -每个算法实现的类需要静态初始化两个类属性,分别是: - -- `name`:触发调用的关键词,全小写英文字母 -- `desc`:算法的描述信息 - -### 核心方法输入与输出约定 - -`execute` 是算法处理的核心方法。调用该方法的时候,`self.list` 已经设置好输入数组。 - -异常检测输出结果 - -`execute` 的返回值是长度与 `self.list` 相同的数组,数组位置为 -1 的即为异常值点。例如:输入数组是 [2, 2, 2, 2, 100], 如果 100 是异常点,那么返回值是 [1, 1, 1, 1, -1]。 - -预测输出结果 - -对于预测算法,`AbstractForecastService` 的对象属性说明如下: - -|属性名称|说明|默认值| -|---|---|---| -|period|输入时间序列的周期性,多少个数据点表示一个完整的周期。如果没有周期性,那么设置为 0 即可| 0| -|start_ts|预测结果的开始时间| 0| -|time_step|预测结果的两个数据点之间时间间隔|0 | -|fc_rows|预测结果的数量| 0 | -|return_conf|预测结果中是否包含置信区间范围,如果不包含置信区间,那么上界和下界与自身相同| 1| -|conf|置信区间分位数 0.05| - - -预测返回结果如下: -```python -return { - "rows": self.fc_rows, # 预测数据行数 - "period": self.period, # 数据周期性,同输入 - "algo": "holtwinters", # 预测使用的算法 - "mse": mse, # 预测算法的 mse - "res": res # 结果数组 [时间戳数组, 预测结果数组, 预测结果执行区间下界数组,预测结果执行区间上界数组] -} -``` - - -## 示例代码 - -```python -import numpy as np -from service import AbstractAnomalyDetectionService - -# 算法实现类名称 需要以下划线 "_" 开始,并以 Service 结束,如下 _IqrService 是 IQR 异常检测算法的实现类。 -class _IqrService(AbstractAnomalyDetectionService): - """ IQR algorithm 定义类,从 AbstractAnomalyDetectionService 继承,并实现 AbstractAnomalyDetectionService 类的抽象函数 """ - - # 定义算法调用关键词,全小写ASCII码(必须添加) - name = 'iqr' - - # 该算法的描述信息(建议添加) - desc = """found the anomaly data according to the inter-quartile range""" - - def __init__(self): - super().__init__() - - def execute(self): - """ execute 是算法实现逻辑的核心实现,直接修改该实现即可 """ - - # self.list 是输入数值列,list 类型,例如:[1,2,3,4,5]。设置 self.list 的方法在父类中已经进行了定义。实现自己的算法,修改该文件即可,以下代码使用自己的实现替换即可。 - #lower = np.quantile(self.list, 0.25) - #upper = np.quantile(self.list, 0.75) - - #min_val = lower - 1.5 * (upper - lower) - #max_val = upper + 1.5 * (upper - lower) - #threshold = [min_val, max_val] - - # 返回值是与输入数值列长度相同的数据列,异常值对应位置是 -1。例如上述输入数据列,返回数值列是 [1, 1, 1, 1, -1],表示 [5] 是异常值。 - return [-1 if k < threshold[0] or k > threshold[1] else 1 for k in self.list] - - - def set_params(self, params): - """该算法无需任何输入参数,直接重载父类该函数,不处理算法参数设置逻辑""" - pass -``` - - -## 单元测试 - -在测试文件目录中的 anomaly_test.py 中增加单元测试用例。 - -```python -def test_iqr(self): - """ 测试 _IqrService 类 """ - s = loader.get_service("iqr") - - # 设置需要进行检测的输入数据 - s.set_input_list(AnomalyDetectionTest.input_list) - - # 测试 set_params 的处理逻辑 - try: - s.set_params({"k": 2}) - except ValueError as e: - self.assertEqual(1, 0) - - r = s.execute() - - # 绘制异常检测结果 - draw_ad_results(AnomalyDetectionTest.input_list, r, "iqr") - - # 检查结果 - self.assertEqual(r[-1], -1) - self.assertEqual(len(r), len(AnomalyDetectionTest.input_list)) -``` - -## 需要模型的算法 - -针对特定数据集,进行模型训练的算法,在训练完成后。需要将训练得到的模型保存在 model 目录中。需要注意的是,针对每个算法,需要建立独立的文件夹。例如 auto_encoder 的训练算法在 model 目录下建立 autoencoder 的目录,使用该算法针对不同数据集训练得到的模型,均需要放置在该目录下。 - -训练完成后的模型,使用 joblib 进行保存。 - -并在 model 目录下建立对应的文件夹存放该模型。 - -保存模型的调用,可参考 encoder.py 的方式,用户通过调用 set_params 方法,并指定参数 `{"model": "ad_encoder_keras"}` 的方式,可以调用该模型进行计算。 - -具体的调用方式如下: - -```python -def test_autoencoder_ad(self): - # 获取特定的算法服务 - s = loader.get_service("ac") - data = self.__load_remote_data_for_ad() - - # 设置异常检查的输入数据 - s.set_input_list(data) - - # 指定调用的模型,该模型是之前针对该数据集进行训练获得 - s.set_params({"model": "ad_encoder_keras"}) - # 执行检查动作,并返回结果 - r = s.execute() - - num_of_error = -(sum(filter(lambda x: x == -1, r))) - self.assertEqual(num_of_error, 109) -``` - diff --git a/docs/zh/06-advanced/06-data-analysis/index.md b/docs/zh/06-advanced/06-data-analysis/index.md deleted file mode 100644 index 2cbea1caba..0000000000 --- a/docs/zh/06-advanced/06-data-analysis/index.md +++ /dev/null @@ -1,322 +0,0 @@ ---- -sidebar_label: 数据分析 -title: 数据分析功能 ---- - -## 概述 - -ANode(Analysis Node)是 TDengine 提供数据分析功能的扩展组件,通过 Restful 接口提供分析服务,拓展 TDengine 的功能,支持时间序列高级分析。 -ANode 是无状态的数据分析节点,集群中可以存在多个 ANode 节点,相互之间没有关联。将 ANode 注册到 TDengine 集群以后,通过 SQL 语句即可调用并完成时序分析任务。 -下图是数据分析的技术架构示意图。 - -![数据分析功能架构图](./pic/data-analysis.png) - -## 安装部署 -### 环境准备 -ANode 要求节点上准备有 Python 3.10 及以上版本,以及相应的 Python 包自动安装组件 Pip,同时请确保能够正常连接互联网。 - -### 安装及卸载 -使用专门的 ANode 安装包 TDengine-enterprise-anode-1.x.x.tar.gz 进行 ANode 的安装部署工作,安装过程与 TDengine 的安装流程一致。 - -```bash -tar -xzvf TDengine-enterprise-anode-1.0.0.tar.gz -cd TDengine-enterprise-anode-1.0.0 -sudo ./install.sh -``` - -卸载 ANode,执行命令 `rmtaosanode` 即可。 - -### 其他 -为了避免 ANode 安装后影响目标节点现有的 Python 库。 ANode 使用 Python 虚拟环境运行,安装后的默认 Python 目录处于 `/var/lib/taos/taosanode/venv/`。为了避免反复安装虚拟环境带来的开销,卸载 ANode 并不会自动删除该虚拟环境,如果您确认不需要 Python 的虚拟环境,可以手动删除。 - -## 启动及停止服务 -安装 ANode 以后,可以使用 `systemctl` 来管理 ANode 的服务。使用如下命令可以启动/停止/检查状态。 - -```bash -systemctl start taosanoded -systemctl stop taosanoded -systemctl status taosanoded -``` - -## 目录及配置说明 -|目录/文件|说明| -|---------------|------| -|/usr/local/taos/taosanode/bin|可执行文件目录| -|/usr/local/taos/taosanode/resource|资源文件目录,链接到文件夹 /var/lib/taos/taosanode/resource/| -|/usr/local/taos/taosanode/lib|库文件目录| -|/var/lib/taos/taosanode/model/|模型文件目录,链接到文件夹 /var/lib/taos/taosanode/model| -|/var/log/taos/taosanode/|日志文件目录| -|/etc/taos/taosanode.ini|配置文件| - -### 配置说明 - -Anode 提供的 RestFul 服务使用 uWSGI 驱动,因此 ANode 和 uWSGI 的配置信息存放在同一个配置文件中,具体如下: - -```ini -[uwsgi] -# charset -env = LC_ALL = en_US.UTF-8 - -# ip:port -http = 127.0.0.1:6050 - -# the local unix socket file than communicate to Nginx -#socket = 127.0.0.1:8001 -#socket-timeout = 10 - -# base directory -chdir = /usr/local/taos/taosanode/lib - -# initialize python file -wsgi-file = /usr/local/taos/taosanode/lib/taos/app.py - -# call module of uWSGI -callable = app - -# auto remove unix Socket and pid file when stopping -vacuum = true - -# socket exec model -#chmod-socket = 664 - -# uWSGI pid -uid = root - -# uWSGI gid -gid = root - -# main process -master = true - -# the number of worker processes -processes = 2 - -# pid file -pidfile = /usr/local/taos/taosanode/taosanode.pid - -# enable threads -enable-threads = true - -# the number of threads for each process -threads = 4 - -# memory useage report -memory-report = true - -# smooth restart -reload-mercy = 10 - -# conflict with systemctl, so do NOT uncomment this -# daemonize = /var/log/taos/taosanode/taosanode.log - -# log directory -logto = /var/log/taos/taosanode/taosanode.log - -# wWSGI monitor port -stats = 127.0.0.1:8387 - -# python virtual environment directory -virtualenv = /usr/local/taos/taosanode/venv/ - -[taosanode] -# default app log file -app-log = /var/log/taos/taosanode/taosanode.app.log - -# model storage directory -model-dir = /usr/local/taos/taosanode/model/ - -# default log level -log-level = DEBUG - -# draw the query results -draw-result = 0 -``` - -**提示** -请勿设置 `daemonize` 参数,该参数会导致 uWSGI 与 systemctl 冲突,从而无法正常启动。 - - -## ANode 基本操作 -### 管理 ANode -#### 创建 ANode -```sql -CREATE ANODE {node_url} -``` -node_url 是提供服务的 ANode 的 IP 和 PORT, 例如:`create anode 'http://localhost:6050'`。启动 ANode 以后如果不注册到 TDengine 集群中,则无法提供正常的服务。不建议 ANode 注册到两个或多个集群中。 - -#### 查看 ANode -列出集群中所有的数据分析节点,包括其 `FQDN`, `PORT`, `STATUS`。 -```sql -SHOW ANODES; -``` - -#### 查看提供的时序数据分析服务 - -```SQL -SHOW ANODES FULL; -``` - -#### 强制刷新集群中的分析算法缓存 -```SQL -UPDATE ANODE {node_id} -UPDATE ALL ANODES -``` - -#### 删除 ANode -```sql -DROP ANODE {anode_id} -``` -删除 ANode 只是将 ANode 从 TDengine 集群中删除,管理 ANode 的启停仍然需要使用`systemctl`命令。 - -### 时序数据分析功能 - -#### 白噪声检查 - -分析平台提供的 Restful 服务要求输入的时间序列不能是白噪声时间序列(White Noise Data, WND)和随机数序列 , 因此针对所有数据均默认进行白噪声检查。当前白噪声检查采用通行的 `Ljung-Box` 检验,`Ljung-Box` 统计量检查过程需要遍历整个输入序列并进行计算。 -如果用户能够明确输入序列一定不是白噪声序列,那么可以通过输入参数,指定预测之前忽略该检查,从而节省分析过程的 CPU 计算资源。 -同时支持独立地针对输入序列进行白噪声检测(该检测功能暂不独立对外开放)。 - - -#### 数据重采样和时间戳对齐 - -分析平台支持将输入数据进行重采样预处理,从而确保输出结果按照用户指定的等间隔进行处理。处理过程分为两种类别: - -- 数据时间戳对齐。由于真实数据可能并非严格按照查询指定的时间戳输入。此时分析平台会自动将数据的时间间隔按照指定的时间间隔进行对齐。例如输入时间序列 [11, 22, 29, 41],用户指定时间间隔为 10,该序列将被对齐重整为以下序列 [10, 20, 30, 40]。 -- 数据时间重采样。用户输入时间序列的采样频率超过了输出结果的频率,例如输入时间序列的采样频率是 5,输出结果的频率是 10,输入时间序列 [0, 5, 10, 15, 20, 25, 30] 将被重采用为间隔 为 10 的序列 [0, 10, 20,30],[5, 15, 25] 处的数据将被丢弃。 - -需要注意的是,数据输入平台不支持缺失数据补齐后进行的预测分析,如果输入时间序列数据 [11, 22, 29, 49],并且用户要求的时间间隔为 10,重整对齐后的序列是 [10, 20, 30, 50] 那么该序列进行预测分析将返回错误。 - - -#### 时序数据异常检测 -异常检测是针对输入的时序数据,使用预设或用户指定的算法确定时间序列中**可能**出现异常的时间序列点,对于时间序列中若干个连续的异常点,将自动合并成为一个连续的(闭区间)异常窗口。对于只有单个点的场景,异常窗口窗口退化成为一个起始时间和结束时间相同的点。 -异常检测生成的异常窗口受检测算法和算法参数的共同影响,对于异常窗口范围内的数据,可以应用 TDengine 提供的聚合和标量函数进行查询或变换处理。 -对于输入时间序列 (1, 20), (2, 22), (3, 91), (4, 120), (5, 18), (6, 19)。系统检测到 (3, 91), (4, 120) 为异常点,那么返回的异常窗口是闭区间 [3, 4]。 - - -##### 语法 - -```SQL -ANOMALY_WINDOW(column_name, option_expr) - -option_expr: {" -algo=expr1 -[,wncheck=1|0] -[,expr2] -"} -``` - -1. `column`:进行时序数据异常检测的输入数据列,当前只支持单列,且只能是数值类型,不能是字符类型(例如:`NCHAR` `VARCHAR` `VARBINARY`等类型),**不支持函数表达式**。 -2. `options`:字符串。其中使用 K=V 调用异常检测算法及与算法相关的参数。采用逗号分隔的 K=V 字符串表示,其中的字符串不需要使用单引号、双引号、或转义号等符号,不能使用中文及其他宽字符。例如:`algo=ksigma,k=2` 表示进行异常检测的算法是 ksigma,该算法接受的输入参数是 2。 -3. 异常检测的结果可以作为外层查询的子查询输入,在 `SELECT` 子句中使用的聚合函数或标量函数与其他类型的窗口查询相同。 -4. 输入数据默认进行白噪声检查,如果输入数据是白噪声,将不会有任何(异常)窗口信息返回。 - -**参数说明** -|参数|含义|默认值| -|---|---|---| -|algo|异常检测调用的算法|iqr| -|wncheck|对输入数据列是否进行白噪声检查|取值为 0 或者 1,默认值为 1,表示进行白噪声检查| - -异常检测的返回结果以窗口形式呈现,因此窗口查询相关的伪列在这种场景下仍然可用。可以使用的伪列如下: -1. `_WSTART`: 异常窗口开始时间戳 -2. `_WEND`:异常窗口结束时间戳 -3. `_WDURATION`:异常窗口持续时间 - -**示例** -```SQL ---- 使用 iqr 算法进行异常检测,检测列 i32 列。 -SELECT _wstart, _wend, SUM(i32) -FROM ai.atb -ANOMALY_WINDOW(i32, "algo=iqr"); - ---- 使用 ksigma 算法进行异常检测,输入参数 k 值为 2,检测列 i32 列 -SELECT _wstart, _wend, SUM(i32) -FROM ai.atb -ANOMALY_WINDOW(i32, "algo=ksigma,k=2"); -``` - -``` -taos> SELECT _wstart, _wend, count(*) FROM ai.atb ANOMAYL_WINDOW(i32); - _wstart | _wend | count(*) | -==================================================================== - 2020-01-01 00:00:16.000 | 2020-01-01 00:00:16.001 | 1 | -Query OK, 1 row(s) in set (0.028946s) -``` - - -**可用异常检测算法** -- iqr -- ksigma -- grubbs -- lof -- shesd -- tac - - -#### 时序数据预测 -数据预测以一段训练数据作为输入,预测接下来一个连续时间区间内,时序数据的趋势。 - -##### 语法 -```SQL -FORECAST(column_expr, option_expr) - -option_expr: {" -algo=expr1 -[,wncheck=1|0] -[,conf=conf_val] -[,every=every_val] -[,rows=rows_val] -[,start=start_ts_val] -[,expr2] -"} - -``` -1. `column_expr`:预测的时序数据列。与异常检测相同,只支持数值类型输入。 -2. `options`:异常检测函数的参数,使用规则与 anomaly_window 相同。预测还支持 `conf`, `every`, `rows`, `start`, `rows` 几个参数,其含义如下: - -**参数说明** - -|参数|含义|默认值| -|---|---|---| -|algo|预测分析使用的算法|holtwinters| -|wncheck|白噪声(white noise data)检查|默认值为 1,0 表示不进行检查| -|conf|预测数据的置信区间范围 ,取值范围 [0, 100]|95| -|every|预测数据的采样间隔|输入数据的采样间隔| -|start|预测结果的开始时间戳|输入数据最后一个时间戳加上一个采样时间段| -|rows|预测结果的记录数|10| - -1. 预测查询结果新增了三个伪列,具体如下:`_FROWTS`:预测结果的时间戳、`_FLOW`:置信区间下界、`_FHIGH`:置信区间上界, 对于没有置信区间的预测算法,其置信区间同预测结果 -2. 更改参数 `START`:返回预测结果的起始时间,改变起始时间不会影响返回的预测数值,只影响起始时间。 -3. `EVERY`:可以与输入数据的采样频率不同。采样频率只能低于或等于输入数据采样频率,不能**高于**输入数据的采样频率。 -4. 对于某些不需要计算置信区间的算法,即使指定了置信区间,返回的结果中其上下界退化成为一个点。 - -**示例** - -```SQL ---- 使用 arima 算法进行预测,预测结果是 10 条记录(默认值),数据进行白噪声检查,默认置信区间 95%. -SELECT _flow, _fhigh, _frowts, FORECAST(i32, "algo=arima") -FROM ai.ftb; - ---- 使用 arima 算法进行预测,输入数据的是周期数据,每 10 个采样点是一个周期。返回置信区间是 95%. -SELECT _flow, _fhigh, _frowts, FORECAST(i32, "algo=arima,alpha=95,period=10") -FROM ai.ftb; -``` -``` -taos> select _flow, _fhigh, _frowts, forecast(i32) from ai.ftb; - _flow | _fhigh | _frowts | forecast(i32) | -======================================================================================== - 10.5286684 | 41.8038254 | 2020-01-01 00:01:35.001 | 26 | - -21.9861946 | 83.3938904 | 2020-01-01 00:01:36.001 | 30 | - -78.5686035 | 144.6729126 | 2020-01-01 00:01:37.001 | 33 | - -154.9797363 | 230.3057709 | 2020-01-01 00:01:38.001 | 37 | - -253.9852905 | 337.6083984 | 2020-01-01 00:01:39.001 | 41 | - -375.7857971 | 466.4594727 | 2020-01-01 00:01:40.001 | 45 | - -514.8043823 | 622.4426270 | 2020-01-01 00:01:41.001 | 53 | - -680.6343994 | 796.2861328 | 2020-01-01 00:01:42.001 | 57 | - -868.4956665 | 992.8603516 | 2020-01-01 00:01:43.001 | 62 | - -1076.1566162 | 1214.4498291 | 2020-01-01 00:01:44.001 | 69 | -``` - - -**可用预测算法** -- arima -- holtwinters diff --git a/docs/zh/06-advanced/06-data-analysis/pic/data-analysis.png b/docs/zh/06-advanced/06-data-analysis/pic/data-analysis.png deleted file mode 100644 index 44fd82832f3e26d546ad15756decba130e5fc4a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50231 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

|1yF&^SgV9Uvu*tSEEClMA`Rag{L~7 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 dP2#WrIJ45LmAISpld_dyuIEnH{S-k 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 Date: Fri, 22 Nov 2024 13:48:43 +0800 Subject: [PATCH 83/88] doc: add gpt --- docs/zh/06-advanced/06-TDgpt/02-management.md | 128 ++++++++++++++++++ docs/zh/06-advanced/06-TDgpt/03-preprocess.md | 49 +++++++ .../06-TDgpt/04-forecast/02-arima.md | 59 ++++++++ .../06-TDgpt/04-forecast/03-holtwinters.md | 48 +++++++ .../06-advanced/06-TDgpt/04-forecast/index.md | 93 +++++++++++++ .../02-statistics-approach.md | 57 ++++++++ .../05-anomaly-detection/03-data-density.md | 20 +++ .../04-machine-learning.md | 17 +++ .../06-TDgpt/05-anomaly-detection/index.md | 69 ++++++++++ .../06-TDgpt/06-dev/02-forecast.md | 110 +++++++++++++++ docs/zh/06-advanced/06-TDgpt/06-dev/03-ad.md | 76 +++++++++++ docs/zh/06-advanced/06-TDgpt/06-dev/index.md | 82 +++++++++++ docs/zh/06-advanced/06-TDgpt/index.md | 25 ++++ docs/zh/06-advanced/06-TDgpt/pic/activity.png | Bin 0 -> 46913 bytes .../06-TDgpt/pic/anomaly-detection.png | Bin 0 -> 331711 bytes .../06-TDgpt/pic/data-analysis.png | Bin 0 -> 57979 bytes .../06-TDgpt/pic/white-noise-data.png | Bin 0 -> 88637 bytes 17 files changed, 833 insertions(+) create mode 100644 docs/zh/06-advanced/06-TDgpt/02-management.md create mode 100644 docs/zh/06-advanced/06-TDgpt/03-preprocess.md create mode 100644 docs/zh/06-advanced/06-TDgpt/04-forecast/02-arima.md create mode 100644 docs/zh/06-advanced/06-TDgpt/04-forecast/03-holtwinters.md create mode 100644 docs/zh/06-advanced/06-TDgpt/04-forecast/index.md create mode 100644 docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/02-statistics-approach.md create mode 100644 docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/03-data-density.md create mode 100644 docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/04-machine-learning.md create mode 100644 docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/index.md create mode 100644 docs/zh/06-advanced/06-TDgpt/06-dev/02-forecast.md create mode 100644 docs/zh/06-advanced/06-TDgpt/06-dev/03-ad.md create mode 100644 docs/zh/06-advanced/06-TDgpt/06-dev/index.md create mode 100644 docs/zh/06-advanced/06-TDgpt/index.md create mode 100644 docs/zh/06-advanced/06-TDgpt/pic/activity.png create mode 100644 docs/zh/06-advanced/06-TDgpt/pic/anomaly-detection.png create mode 100755 docs/zh/06-advanced/06-TDgpt/pic/data-analysis.png create mode 100644 docs/zh/06-advanced/06-TDgpt/pic/white-noise-data.png diff --git a/docs/zh/06-advanced/06-TDgpt/02-management.md b/docs/zh/06-advanced/06-TDgpt/02-management.md new file mode 100644 index 0000000000..9aaa123299 --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/02-management.md @@ -0,0 +1,128 @@ +--- +title: "安装部署" +sidebar_label: "安装部署" +--- + +### 环境准备 +使用 TDgpt 的高级时序数据分析功能需要在 TDengine 集群中安装部署 AI node(Anode)。Anode 可以运行在 Linux/Windows/MacOS 等平台上,同时需要 3.10 或以上版本的 Python 环境支持。 +> 部署 Anode 需要 TDengine Enterprise 3.3.4.3 及以后版本,请首先确认搭配 Anode 使用的 TDengine 能够支持 Anode。 + +### 安装及卸载 +不同操作系统上安装及部署 Anode 有一些差异,主要是卸载操作、安装路径、服务启停等方面。本文以 Linux 系统为例,说明安装部署的流程。 +使用 Linux 环境下的安装包 TDengine-enterprise-anode-1.x.x.tar.gz 可进行 Anode 的安装部署工作,命令如下: + +```bash +tar -xzvf TDengine-enterprise-anode-1.0.0.tar.gz +cd TDengine-enterprise-anode-1.0.0 +sudo ./install.sh +``` + +对于已经安装的 Anode,执行命令 `rmtaosanode` 即可完成卸载。 +为了避免影响系统已有的 Python 环境,Anode 使用虚拟环境运行。安装 Anode 会在目录 `/var/lib/taos/taosanode/venv/` 中创建默认的 Python 虚拟环境,Anode 运行所需要的库均安装在该目录下。为了避免反复安装虚拟环境带来的开销,卸载命令 `rmtaosanode` 并不会自动删除该虚拟环境,如果您确认不再需要 Python 的虚拟环境,手动删除该目录即可。 + +### 启停服务 +在 Linux 系统中,安装 Anode 以后会自动创建 `taosanoded` 服务。可以使用 `systemd` 来管理 Anode 服务,使用如下命令启动/停止/检查 Anode。 + +```bash +systemctl start taosanoded +systemctl stop taosanoded +systemctl status taosanoded +``` + +### 目录及配置说明 +安装完成后,Anode 主体目录结构如下: + +|目录/文件|说明| +|---------------|------| +|/usr/local/taos/taosanode/bin|可执行文件目录| +|/usr/local/taos/taosanode/resource|资源文件目录,链接到文件夹 /var/lib/taos/taosanode/resource/| +|/usr/local/taos/taosanode/lib|库文件目录| +|/var/lib/taos/taosanode/model/|模型文件目录,链接到文件夹 /var/lib/taos/taosanode/model| +|/var/log/taos/taosanode/|日志文件目录| +|/etc/taos/taosanode.ini|配置文件| + +#### 配置说明 + +Anode 的服务需要使用 uWSGI 驱动驱动运行,因此 Anode 和 uWSGI 的配置信息共同存放在相同的配置文件 `taosanode.ini` 中,该配置文件默认位于 `/etc/taos/` 目录下。 +具体内容及配置项说明如下: + +```ini +[uwsgi] + +# Anode RESTful service ip:port +http = 127.0.0.1:6090 + +# base directory for Anode python files, do NOT modified this +chdir = /usr/local/taos/taosanode/lib + +# initialize Anode python file +wsgi-file = /usr/local/taos/taosanode/lib/taos/app.py + +# pid file +pidfile = /usr/local/taos/taosanode/taosanode.pid + +# conflict with systemctl, so do NOT uncomment this +# daemonize = /var/log/taos/taosanode/taosanode.log + +# log directory +logto = /var/log/taos/taosanode/taosanode.log + +# wWSGI monitor port +stats = 127.0.0.1:8387 + +# python virtual environment directory, used by Anode +virtualenv = /usr/local/taos/taosanode/venv/ + +[taosanode] +# default app log file +app-log = /var/log/taos/taosanode/taosanode.app.log + +# model storage directory +model-dir = /usr/local/taos/taosanode/model/ + +# default log level +log-level = DEBUG + +``` + +**提示** +请勿设置 `daemonize` 参数,该参数会导致 uWSGI 与 systemctl 冲突,从而导致 Anode 无法正常启动。 +上面的示例配置文件 `taosanode.ini` 只包含了使用 Anode 提供服务的基础配置参数,对于 uWSGI 的其他配置参数的设置及其说明请参考 [uWSGIS官方文档](https://uwsgi-docs-zh.readthedocs.io/zh-cn/latest/Options.html)。 + +Anode 运行配置主要是以下: +- app-log: Anode 服务运行产生的日志,用户可以调整其到需要的位置 +- model-dir: 采用算法针对已经存在的数据集的运行完成生成的模型存储位置 +- log-level: app-log文件的日志级别 + + +### Anode 基本操作 +对于 Anode 的管理,用户需要通过 TDengine 的命令行接口 taos 进行。因此下述介绍的管理命令都需要先打开 taos, 连接到 TDengine 运行实例。 +#### 创建 Anode +```sql +CREATE ANODE {node_url} +``` +node_url 是提供服务的 Anode 的 IP 和 PORT组成的字符串, 例如:`create anode '127.0.0.1:6090'`。Anode 启动后还需要注册到 TDengine 集群中才能提供服务。不建议将 Anode 同时注册到两个集群中。 + +#### 查看 Anode +列出集群中所有的数据分析节点,包括其 `FQDN`, `PORT`, `STATUS`等属性。 +```sql +SHOW ANODES; +``` + +#### 查看提供的时序数据分析服务 + +```SQL +SHOW ANODES FULL; +``` + +#### 刷新集群中的分析算法缓存 +```SQL +UPDATE ANODE {anode_id} +UPDATE ALL ANODES +``` + +#### 删除 Anode +```sql +DROP ANODE {anode_id} +``` +删除 Anode 只是将 Anode 从 TDengine 集群中删除,管理 Anode 的启停仍然需要使用 `systemctl` 命令。卸载 Anode 则需要使用上面提到的 `rmtaosanode` 命令。 diff --git a/docs/zh/06-advanced/06-TDgpt/03-preprocess.md b/docs/zh/06-advanced/06-TDgpt/03-preprocess.md new file mode 100644 index 0000000000..9efd2bdf11 --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/03-preprocess.md @@ -0,0 +1,49 @@ +--- +title: "数据分析预处理" +sidebar_label: "数据分析预处理" +--- + +import activity from './pic/activity.png'; +import wndata from './pic/white-noise-data.png' + +### 分析流程 +时序数据分析之前需要有预处理的过程,为减轻分析算法的负担,TDgpt 在将时序数据发给具体分析算法进行分析时,已经对数据做了预处理,整体的流程如下图所示。 + +预处理流程 + +TDgpt 首先对输入数据进行白噪声检查(White Noise Data check), 检查通过以后针对预测分析,还要进行输入(历史)数据的重采样和时间戳对齐处理(异常检测跳过数据重采样和时间戳对齐步骤)。 +预处理完成以后,再进行预测或异常检测操作。预处理过程部署于预测或异常检测处理逻辑的一部分。 + +### 白噪声检查 + +white-noise-data + +白噪声时序数据可以简单地认为是随机数构成的时间数据序列(如上图所示的正态分布随机数序列),随机数构成的时间序列没有分析的价值,因此会直接返回。白噪声检查采用经典的 `Ljung-Box` 统计量检验,计算 `Ljung-Box` 统计量需遍历整个输入时间序列。如果用户能够明确输入序列一定不是白噪声序列,那么可以在参数列表中增加参数 `wncheck=0` 强制要求分析平台忽略白噪声检查,从而节省计算资源。 +TDgpt 暂不提供独立的时间序列白噪声检测功能。 + + +### 重采样和时间戳对齐 + +对于进行预测分析的时间序列数据,在进行预测分析前需要进行必要的预处理。预处理主要解决以下两个问题: + +- 真实时间序列数据时间戳未对齐。由于数据生成设备的原因或网关赋值时间戳的时候并不能保证按照严格的时间间隔赋值,时间序列数据并不能保证是严格按照采样频率对齐。例如采样频率为 1Hz 的一个时间序列数据序列,其时间戳序列如下: + + > ['20:12:21.143', '20:12:22.187', '20:12:23.032', '20:12:24.384', '20:12:25.033'] + + 预测返回的时间序列时间戳会严格对齐,例如返回后续的两个预测结果的时间戳,其时间一定如下:['20:12:26.000', '20:12:27.000']。因此上述的输入时间戳序列要进行时间戳对齐,变换成为如下时间戳序列 + + > ['20:12:21.000', '20:12:22.000', '20:12:23.000', '20:12:24.000', '20:12:25.000'] + + +- 数据时间重采样。用户输入时间序列的采样频率超过了输出结果的频率,例如输入时间序列的采样时间间隔是 5 sec,但是要求输出预测结果的采样时间间隔是 10sec + + > ['20:12:20.000', '20:12:25.000', '20:12:30.000', '20:12:35.000', '20:12:40.000'] + + 重采样为采样间隔为 10sec 的时间戳序列 + + > ['20:12:20.000', '20:12:30.000', '20:12:40.000'] + + 然后将其作为预测分析的输入, ['20:12:25.000', '20:12:35.000'] 数据被丢弃。 + +需要注意的是,预处理过程不支持缺失数据补齐操作,如果输入时间序列数据 ['20:12:10.113', '20:12:21.393', '20:12:29.143', '20:12:51.330'],并且要求的采样时间间隔为 10sec,重整对齐后的时间戳序列是 ['20:12:10.000', '20:12:20.000', '20:12:30.000', '20:12:50.000'] 那么对该序列进行预测分析将返回错误。 + diff --git a/docs/zh/06-advanced/06-TDgpt/04-forecast/02-arima.md b/docs/zh/06-advanced/06-TDgpt/04-forecast/02-arima.md new file mode 100644 index 0000000000..469f557984 --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/04-forecast/02-arima.md @@ -0,0 +1,59 @@ +--- +title: "ARIMA" +sidebar_label: "ARIMA" +--- + +本节说明 ARIMA 算法模型的使用方法。 + +## 功能概述 + +ARIMA:Autoregressive Integrated Moving Average,即自回归移动平均模型,记作 ARIMA(p,d,q),是统计模型中最常见的一种用来进行时间序列预测的模型。 +ARIMA 模型是一种自回归模型,只需要自变量即可预测后续的值。ARIMA 模型要求时间序列**平稳**,或经过差分处理后平稳,如果是不平稳的数据,**无法**获得正确的结果。 + +> 平稳的时间序列:其性质不随观测时间的变化而变化。具有趋势或季节性的时间序列不是平稳时间序列——趋势和季节性使得时间序列在不同时段呈现不同性质。 + +以下参数可以动态输入,控制预测过程中生成合适的 ARIMA 模型。 + +- p= 自回归模型阶数 +- d= 差分阶数 +- q= 移动平均模型阶数 + + +### 参数 +分析平台中使用自动化的 ARIMA 模型进行计算,因此每次计算的时候会根据输入的数据自动拟合最合适的模型,然后根据该模型进行预测输出结果。 +|参数|说明|必填项| +|---|---|-----| +|period|输入时间序列每个周期包含的数据点个数,如果不设置该参数或该参数设置为 0,将使用非季节性/周期性的 ARIMA 模型预测|选填| +|start_p|自回归模型阶数的起始值,0 开始的整数,不推荐大于 10|选填| +|max_p|自回归模型阶数的结束值,0 开始的整数,不推荐大于 10|选填| +|start_q|移动平均模型阶数的起始值,0 开始的整数,不推荐大于 10|选填| +|max_q|移动平均模型阶数的结束值,0 开始的整数,不推荐大于 10|选填| +|d|差分阶数|选填| + +`start_p`、`max_p` `start_q` `max_q` 四个参数约束了模型在多大的范围内去搜寻合适的最优解。相同输入数据的条件下,参数范围越大,消耗的资源越多,系统响应的时间越长。 + +### 示例及结果 +针对 i32 列进行数据预测,输入列 i32 每 10 个点是一个周期,start_p 起始是 1, 最大拟合是 5,start_q 是 1,最大值是 5,预测结果中返回 95% 置信区间范围边界。 +``` +FORECAST(i32, "algo=arima,alpha=95,period=10,start_p=1,max_p=5,start_q=1,max_q=5") +``` + +完整的调用SQL语句如下: +```SQL +SELECT _frowts, FORECAST(i32, "algo=arima,alpha=95,period=10,start_p=1,max_p=5,start_q=1,max_q=5") from foo +``` + +```json5 +{ +"rows": fc_rows, // 返回结果的行数 +"period": period, // 返回结果的周期性,同输入 +"alpha": alpha, // 返回结果的置信区间,同输入 +"algo": "arima", // 返回结果使用的算法 +"mse": mse, // 拟合输入时间序列时候生成模型的最小均方误差(MSE) +"res": res // 列模式的结果 +} +``` + +### 参考文献 +- https://en.wikipedia.org/wiki/Autoregressive_moving-average_model +- [https://baike.baidu.com/item/自回归滑动平均模型/5023931](https://baike.baidu.com/item/%E8%87%AA%E5%9B%9E%E5%BD%92%E6%BB%91%E5%8A%A8%E5%B9%B3%E5%9D%87%E6%A8%A1%E5%9E%8B/5023931) diff --git a/docs/zh/06-advanced/06-TDgpt/04-forecast/03-holtwinters.md b/docs/zh/06-advanced/06-TDgpt/04-forecast/03-holtwinters.md new file mode 100644 index 0000000000..7e92a8ae1a --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/04-forecast/03-holtwinters.md @@ -0,0 +1,48 @@ +--- +title: "HoltWinters" +sidebar_label: "HoltWinters" +--- + +本节讲述 HoltWinters 算法模型的使用方法。 + +## 功能概述 +HoltWinters 模型又称为多次指数平滑模型(EMA)。适用于含有线性趋势和周期波动的非平稳序列,利用指数平滑法让模型参数不断适应非平稳序列的变化,并对未来趋势进行**短期**预测。 +HoltWinters 有两种不同的季节性组成部分,当季节变化在该时间序列中大致保持不变时,通常选择**加法模型**;而当季节变化与时间序列的水平成比例变化时,通常选择**乘法模型**。 +该模型对于返回数据不提供计算的置信区间范围结果,在 95% 置信区间的上下界结果与预测结果相同。 + + +### 参数 + +分析平台中使用自动化的 HoltWinters 模型进行计算,因此每次计算的时候会根据输入的数据自动拟合最合适的模型,然后根据该模型进行预测输出结果。 +|参数|说明|必填项| +|---|---|---| +|period|输入时间序列每个周期包含的数据点个数。如果不设置该参数或该参数设置为 0,将使用一次(简单)指数平滑方式进行数据拟合,并据此进行未来数据的预测|选填| +|trend|趋势模型使用加法模型还是乘法模型|选填| +|seasonal|季节性采用加法模型还是乘法模型|选填| + +参数 `trend` 和 `seasonal`的均可以选择 `add` (加法模型)或 `mul`(乘法模型)。 + +### 示例及结果 +针对 i32 列进行数据预测,输入列 i32 每 10 个点是一个周期,趋势参数采用乘法模型,季节参数采用乘法模型 +``` +FORECAST(i32, "algo=holtwinters,period=10,trend=mul,seasonal=mul") +``` + +完整的调用SQL语句如下: +```SQL +SELECT _frowts, FORECAST(i32, "algo=holtwinters, peroid=10,trend=mul,seasonal=mul") from foo +``` + +```json5 +{ +"rows": rows, // 返回结果的行数 +"period": period, // 返回结果的周期性,该结果与输入的周期性相同,如果没有周期性,该值为 0 +"algo": 'holtwinters' // 返回结果使用的计算模型 +"mse": mse, // 最小均方误差(minmum square error) +"res": res // 具体的结果,按照列形式返回的结果。一般意义上包含了两列 [timestamp][fc_results]。 +} +``` + +### 参考文献 +- https://en.wikipedia.org/wiki/Exponential_smoothing +- https://orangematter.solarwinds.com/2019/12/15/holt-winters-forecasting-simplified/ diff --git a/docs/zh/06-advanced/06-TDgpt/04-forecast/index.md b/docs/zh/06-advanced/06-TDgpt/04-forecast/index.md new file mode 100644 index 0000000000..c7388ab9c0 --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/04-forecast/index.md @@ -0,0 +1,93 @@ +--- +title: 预测算法 +description: 预测算法 +--- + +时序数据预测处理以持续一个时间段的时序数据作为输入,预测接下来一个连续时间区间内时间序列数据趋势。用户可以指定输出的(预测)时间序列数据点的数量,因此其输出的结果行数不确定。为此,TDengine 使用新 SQL 函数 `FORECAST` 提供时序数据预测服务。基础数据(用于预测的历史时间序列数据)是该函数的输入,预测结果是该函数的输出。用户可以通过 `FORECAST` 函数调用 Anode 提供的预测算法提供的服务。 + +在后续章节中,使用时序数据表`foo`作为示例,介绍预测和异常检测算法的使用方式,`foo` 表的模式如下: + +|列名称|类型|说明| +|---|---|---| +|ts| timestamp| 主时间戳列| +|i32| int32| 4字节整数,设备测量值 metric| + +```bash +taos> select * from foo; + ts | k | +======================================== + 2020-01-01 00:00:12.681 | 13 | + 2020-01-01 00:00:13.727 | 14 | + 2020-01-01 00:00:14.378 | 8 | + 2020-01-01 00:00:15.774 | 10 | + 2020-01-01 00:00:16.170 | 16 | + 2020-01-01 00:00:17.558 | 26 | + 2020-01-01 00:00:18.938 | 32 | + 2020-01-01 00:00:19.308 | 27 | +``` + +### 语法 +```SQL +FORECAST(column_expr, option_expr) + +option_expr: {" +algo=expr1 +[,wncheck=1|0] +[,conf=conf_val] +[,every=every_val] +[,rows=rows_val] +[,start=start_ts_val] +[,expr2] +"} + +``` +1. `column_expr`:预测的时序数据列。与异常检测相同,只支持数值类型列输入。 +2. `options`:异常检测函数的参数,使用规则与 anomaly_window 相同。预测支持 `conf`, `every`, `rows`, `start`, `rows` 几个控制参数,其含义如下: + +### 参数说明 + +|参数|含义|默认值| +|---|---|---| +|algo|预测分析使用的算法|holtwinters| +|wncheck|白噪声(white noise data)检查|默认值为 1,0 表示不进行检查| +|conf|预测数据的置信区间范围 ,取值范围 [0, 100]|95| +|every|预测数据的采样间隔|输入数据的采样间隔| +|start|预测结果的开始时间戳|输入数据最后一个时间戳加上一个采样间隔时间区间| +|rows|预测结果的记录数|10| + +1. 预测查询结果新增三个伪列,具体如下:`_FROWTS`:预测结果的时间戳、`_FLOW`:置信区间下界、`_FHIGH`:置信区间上界, 对于没有置信区间的预测算法,其置信区间同预测结果 +2. 更改参数 `START`:返回预测结果的起始时间,改变起始时间不会影响返回的预测数值,只影响起始时间。 +3. `EVERY`:可以与输入数据的采样频率不同。采样频率只能低于或等于输入数据采样频率,不能**高于**输入数据的采样频率。 +4. 对于某些不需要计算置信区间的算法,即使指定了置信区间,返回的结果中其上下界退化成为一个点。 + +### 示例 + +```SQL +--- 使用 arima 算法进行预测,预测结果是 10 条记录(默认值),数据进行白噪声检查,默认置信区间 95%. +SELECT _flow, _fhigh, _frowts, FORECAST(i32, "algo=arima") +FROM foo; + +--- 使用 arima 算法进行预测,输入数据的是周期数据,每 10 个采样点是一个周期,返回置信区间是95%的上下边界,同时忽略白噪声检查 +SELECT _flow, _fhigh, _frowts, FORECAST(i32, "algo=arima,alpha=95,period=10,wncheck=0") +FROM foo; +``` +``` +taos> select _flow, _fhigh, _frowts, forecast(i32) from foo; + _flow | _fhigh | _frowts | forecast(i32) | +======================================================================================== + 10.5286684 | 41.8038254 | 2020-01-01 00:01:35.000 | 26 | + -21.9861946 | 83.3938904 | 2020-01-01 00:01:36.000 | 30 | + -78.5686035 | 144.6729126 | 2020-01-01 00:01:37.000 | 33 | + -154.9797363 | 230.3057709 | 2020-01-01 00:01:38.000 | 37 | + -253.9852905 | 337.6083984 | 2020-01-01 00:01:39.000 | 41 | + -375.7857971 | 466.4594727 | 2020-01-01 00:01:40.000 | 45 | + -514.8043823 | 622.4426270 | 2020-01-01 00:01:41.000 | 53 | + -680.6343994 | 796.2861328 | 2020-01-01 00:01:42.000 | 57 | + -868.4956665 | 992.8603516 | 2020-01-01 00:01:43.000 | 62 | + -1076.1566162 | 1214.4498291 | 2020-01-01 00:01:44.000 | 69 | +``` + + +## 内置预测算法 +- [arima](./02-arima.md) +- [holtwinters](./03-holtwinters.md) diff --git a/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/02-statistics-approach.md b/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/02-statistics-approach.md new file mode 100644 index 0000000000..d0d6815c25 --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/02-statistics-approach.md @@ -0,0 +1,57 @@ +--- +title: "统计学算法" +sidebar_label: "统计学算法" +--- + +- k-sigma[1]: 即 ***68–95–99.7 rule*** 。***k***值默认为 3,即序列均值的 3 倍标准差范围为边界,超过边界的是异常值。KSigma 要求数据整体上服从正态分布,如果一个点偏离均值 K 倍标准差,则该点被视为异常点. + +|参数|说明|是否必选|默认值| +|---|---|---|---| +|k|标准差倍数|选填|3| + +```SQL +--- 指定调用的算法为ksigma, 参数 k 为 2 +SELECT _WSTART, COUNT(*) +FROM foo +ANOMALY_WINDOW(foo.i32, "algo=ksigma,k=2") +``` + +- IQR[2]:Interquartile range(IQR),四分位距是一种衡量变异性的方法。四分位数将一个按等级排序的数据集划分为四个相等的部分。即 Q1(第 1 个四分位数)、Q2(第 2 个四分位数)和 Q3(第 3 个四分位数)。 $IQR=Q3-Q1$,对于 $v$, $Q1-(1.5 \times IQR) \le v \le Q3+(1.5 \times IQR)$ 是正常值,范围之外的是异常值。无输入参数。 + +```SQL +--- 指定调用的算法为 iqr, 无参数 +SELECT _WSTART, COUNT(*) +FROM foo +ANOMALY_WINDOW(foo.i32, "algo=iqr") +``` + +- Grubbs[3]: Grubbs' test,即最大标准残差测试。Grubbs 通常用作检验最大值、最小值偏离均值的程度是否为异常,要求单变量数据集遵循近似标准正态分布。非正态分布数据集不能使用该方法。无输入参数。 + +```SQL +--- 指定调用的算法为 grubbs, 无参数 +SELECT _WSTART, COUNT(*) +FROM foo +ANOMALY_WINDOW(foo.i32, "algo=grubbs") +``` + +- SHESD[4]: 带有季节性的 ESD 检测算法。ESD 可以检测时间序列数据的多异常点。需要指定异常检测方向('pos' / 'neg' / 'both'),异常值比例的上界***max_anoms***,最差的情况是至多 49.9%。数据集的异常比例一般不超过 5% + +|参数|说明|是否必选|默认值| +|---|---|---|---| +|direction|异常检测方向类型('pos' / 'neg' / 'both')|否|"both"| +|max_anoms|异常值比例 $0 < K \le 49.9$|否|0.05| +|period|一个周期包含的数据点|否|0| + + +```SQL +--- 指定调用的算法为 shesd, 参数 direction 为 both,异常值比例 5% +SELECT _WSTART, COUNT(*) +FROM foo +ANOMALY_WINDOW(foo.i32, "algo=shesd,direction=both,anoms=0.05") +``` + +### 参考文献 +1. [https://en.wikipedia.org/wiki/68–95–99.7 rule](https://en.wikipedia.org/wiki/68%E2%80%9395%E2%80%9399.7_rule) +2. https://en.wikipedia.org/wiki/Interquartile_range +3. Adikaram, K. K. L. B.; Hussein, M. A.; Effenberger, M.; Becker, T. (2015-01-14). "Data Transformation Technique to Improve the Outlier Detection Power of Grubbs's Test for Data Expected to Follow Linear Relation". Journal of Applied Mathematics. 2015: 1–9. doi:10.1155/2015/708948. +4. Hochenbaum, O. S. Vallis, and A. Kejariwal. 2017. Automatic Anomaly Detection in the Cloud Via Statistical Learning. arXiv preprint arXiv:1704.07706 (2017). diff --git a/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/03-data-density.md b/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/03-data-density.md new file mode 100644 index 0000000000..7c0998c917 --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/03-data-density.md @@ -0,0 +1,20 @@ +--- +title: "数据密度算法" +sidebar_label: "数据密度算法" +--- + +### 基于数据密度的检测方法 +LOF[1]: Local Outlier Factor(LOF),局部离群因子/局部异常因子, +是 Breunig 在 2000 年提出的一种基于密度的局部离群点检测算法,该方法适用于不同类簇密度分散情况迥异的数据。根据数据点周围的数据密集情况,首先计算每个数据点的一个局部可达密度,然后通过局部可达密度进一步计算得到每个数据点的一个离群因子, +该离群因子即标识了一个数据点的离群程度,因子值越大,表示离群程度越高,因子值越小,表示离群程度越低。最后,输出离群程度最大的 $topK$ 个点。 + +```SQL +--- 指定调用的算法为LOF,即可调用该算法 +SELECT count(*) +FROM foo +ANOMALY_WINDOW(foo.i32, "algo=lof") +``` + +### 参考文献 + +1. Breunig, M. M.; Kriegel, H.-P.; Ng, R. T.; Sander, J. (2000). LOF: Identifying Density-based Local Outliers (PDF). Proceedings of the 2000 ACM SIGMOD International Conference on Management of Data. SIGMOD. pp. 93–104. doi:10.1145/335191.335388. ISBN 1-58113-217-4. diff --git a/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/04-machine-learning.md b/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/04-machine-learning.md new file mode 100644 index 0000000000..d72b8e70a9 --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/04-machine-learning.md @@ -0,0 +1,17 @@ +--- +title: "机器学习算法" +sidebar_label: "机器学习算法" +--- + +Autoencoder[1]: TDgpt 内置使用自编码器(Autoencoder)的异常检测算法,对周期性的时间序列数据具有较好的检测结果。使用该模型需要针对输入时序数据进行预训练,同时将训练完成的模型保存在到服务目录 `ad_autoencoder` 中,然后在 SQL 语句中指定调用该算法模型即可使用。 + +```SQL +--- 在 options 中增加 model 的名称,ad_autoencoder_foo, 针对 foo 数据集(表)训练的采用自编码器的异常检测模型进行异常检测 +SELECT COUNT(*), _WSTART +FROM foo +ANOMALY_DETECTION(col1, 'algo=encoder, model=ad_autoencoder_foo'); +``` + +### 参考文献 + +1. https://en.wikipedia.org/wiki/Autoencoder diff --git a/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/index.md b/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/index.md new file mode 100644 index 0000000000..632492ce72 --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/index.md @@ -0,0 +1,69 @@ +--- +title: 异常检测算法 +description: 异常检测算法 +--- + +import ad from '../pic/anomaly-detection.png'; + +TDengine 中定义了异常(状态)窗口来提供异常检测服务。异常窗口可以视为一种特殊的**事件窗口(Event Window)**,即异常检测算法确定的连续异常时间序列数据所在的时间窗口。与普通事件窗口区别在于——时间窗口的起始时间和结束时间均是分析算法识别确定,不是用户给定的表达式进行判定。因此,在 `WHERE` 子句中使用 `ANOMALY_WINDOW` 关键词即可调用时序数据异常检测服务,同时窗口伪列(`_WSTART`, `_WEND`, `_WDURATION`)也能够像其他时间窗口一样用于描述异常窗口的起始时间(`_WSTART`)、结束时间(`_WEND`)、持续时间(`_WDURATION`)。例如: + +```SQL +--- 使用异常检测算法 IQR 对输入列 col_val 进行异常检测。同时输出异常窗口的起始时间、结束时间、以及异常窗口内 col 列的和。 +SELECT _wstart, _wend, SUM(col) +FROM foo +ANOMALY_WINDOW(col_val, "algo=iqr"); +``` + +如下图所示,Anode 将返回时序数据异常窗口 $[10:51:30, 10:53:40]$ + +异常检测 + +在此基础上,用户可以针对异常窗口内的时序数据进行查询聚合、变换处理等操作。 + +### 语法 + +```SQL +ANOMALY_WINDOW(column_name, option_expr) + +option_expr: {" +algo=expr1 +[,wncheck=1|0] +[,expr2] +"} +``` + +1. `column_name`:进行时序数据异常检测的输入数据列,当前只支持单列,且只能是数值类型,不能是字符类型(例如:`NCHAR` `VARCHAR` `VARBINARY`等类型),**不支持函数表达式**。 +2. `options`:字符串。其中使用 K=V 调用异常检测算法及与算法相关的参数。采用逗号分隔的 K=V 字符串表示,其中的字符串不需要使用单引号、双引号、或转义号等符号,不能使用中文及其他宽字符。例如:`algo=ksigma,k=2` 表示进行异常检测的算法是 ksigma,该算法接受的输入参数是 2。 +3. 异常检测的结果可以作为外层查询的子查询输入,在 `SELECT` 子句中使用的聚合函数或标量函数与其他类型的窗口查询相同。 +4. 输入数据默认进行白噪声检查,如果输入数据是白噪声,将不会有任何(异常)窗口信息返回。 + +### 参数说明 +|参数|含义|默认值| +|---|---|---| +|algo|异常检测调用的算法|iqr| +|wncheck|对输入数据列是否进行白噪声检查,取值为0或1|1| + + +### 示例 +```SQL +--- 使用 iqr 算法进行异常检测,检测列 i32 列。 +SELECT _wstart, _wend, SUM(i32) +FROM foo +ANOMALY_WINDOW(i32, "algo=iqr"); + +--- 使用 ksigma 算法进行异常检测,输入参数 k 值为 2,检测列 i32 列 +SELECT _wstart, _wend, SUM(i32) +FROM foo +ANOMALY_WINDOW(i32, "algo=ksigma,k=2"); + +taos> SELECT _wstart, _wend, count(*) FROM foo ANOMAYL_WINDOW(i32); + _wstart | _wend | count(*) | +==================================================================== + 2020-01-01 00:00:16.000 | 2020-01-01 00:00:17.000 | 2 | +Query OK, 1 row(s) in set (0.028946s) +``` + + +### 内置异常检测算法 +分析平台内置了6个异常检查模型,分为3个类别,分别是[基于统计学的算法](./02-statistics-approach.md)、[基于数据密度的算法](./03-data-density.md)、以及[基于机器学习的算法](./04-machine-learning.md)。在不指定异常检测使用的方法的情况下,默认调用 IQR 进行异常检测。 + diff --git a/docs/zh/06-advanced/06-TDgpt/06-dev/02-forecast.md b/docs/zh/06-advanced/06-TDgpt/06-dev/02-forecast.md new file mode 100644 index 0000000000..954076c8fd --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/06-dev/02-forecast.md @@ -0,0 +1,110 @@ +--- +title: "预测算法" +sidebar_label: "预测算法" +--- + +### 输入约定 +`execute` 是预测算法处理的核心方法。框架调用该方法之前,在对象属性参数 `self.list` 中已经设置完毕用于预测的历史时间序列数据。 + +### 输出约定及父类属性说明 +`execute` 方法执行完成后的返回一个如下字典对象, 预测返回结果如下: +```python +return { + "mse": mse, # 预测算法的拟合数据最小均方误差(minimum squared error) + "res": res # 结果数组 [时间戳数组, 预测结果数组, 预测结果执行区间下界数组,预测结果执行区间上界数组] +} +``` + + +预测算法的父类 `AbstractForecastService` 包含的对象属性如下: + +|属性名称|说明|默认值| +|---|---|---| +|period|输入时间序列的周期性,多少个数据点表示一个完整的周期。如果没有周期性,设置为 0 即可| 0| +|start_ts|预测结果的开始时间| 0| +|time_step|预测结果的两个数据点之间时间间隔|0 | +|fc_rows|预测结果的数量| 0 | +|return_conf|预测结果中是否包含置信区间范围,如果不包含置信区间,那么上界和下界与自身相同| 1| +|conf|置信区间分位数|95| + + + +### 示例代码 +下面我们开发一个示例预测算法,对于任何输入的时间序列数据,固定返回值 1 作为预测结果。 + +```python +import numpy as np +from service import AbstractForecastService + +# 算法实现类名称 需要以下划线 "_" 开始,并以 Service 结束 +class _MyForecastService(AbstractForecastService): + """ 定义类,从 AbstractForecastService 继承并实现其定义的抽象方法 execute """ + + # 定义算法调用关键词,全小写ASCII码 + name = 'myfc' + + # 该算法的描述信息(建议添加) + desc = """return the forecast time series data""" + + def __init__(self): + """类初始化方法""" + super().__init__() + + def execute(self): + """ 算法逻辑的核心实现""" + res = [] + + """这个预测算法固定返回 1 作为预测值,预测值的数量是用户通过 self.fc_rows 指定""" + ts_list = [self.start_ts + i * self.time_step for i in range(self.fc_rows)] + res.app(ts_list) # 设置预测结果时间戳列 + + """生成全部为 1 的预测结果 """ + res_list = [1] * self.fc_rows + res.append(res_list) + + """检查用户输入,是否要求返回预测置信区间上下界""" + if self.return_conf: + """对于没有计算预测置信区间上下界的算法,直接返回预测值作为上下界即可""" + bound_list = [1] * self.fc_rows + res.append(bound_list) # 预测结果置信区间下界 + res.append(bound_list) # 预测结果执行区间上界 + + """返回结果""" + return { "res": res, "mse": 0} + + + def set_params(self, params): + """该算法无需任何输入参数,直接重载父类该函数,不处理算法参数设置逻辑""" + pass +``` + +将该文件保存在 `./taosanalytics/algo/fc/` 目录下,然后重启 taosanode 服务。在 TDengine 命令行接口中执行 `SHOW ANODES FULL` 能够看到新加入的算法。应用就可以通过 SQL 语句调用该预测算法。 + +```SQL +--- 对 col 列进行异常检测,通过指定 algo 参数为 myfc 来调用新添加的预测类 +SELECT _flow, _fhigh, _frowts, FORECAST(col_name, "algo=myfc") +FROM foo; +``` + +如果是第一次启动该 Anode, 请按照 [TDgpt 安装部署](../../management/) 里的步骤先将该 Anode 添加到 TDengine 系统中。 + +### 单元测试 + +在测试目录`taosanalytics/test`中的 forecast_test.py 中增加单元测试用例或添加新的测试文件。单元测试依赖 Python Unit test 包。 + +```python +def test_myfc(self): + """ 测试 myfc 类 """ + s = loader.get_service("myfc") + + # 设置用于预测分析的数据 + s.set_input_list(self.get_input_list()) + # 检查预测结果应该全部为 1 + r = s.set_params( + {"fc_rows": 10, "start_ts": 171000000, "time_step": 86400 * 30, "start_p": 0} + ) + r = s.execute() + + expected_list = [1] * 10 + self.assertEqlist(r["res"][0], expected_list) +``` diff --git a/docs/zh/06-advanced/06-TDgpt/06-dev/03-ad.md b/docs/zh/06-advanced/06-TDgpt/06-dev/03-ad.md new file mode 100644 index 0000000000..dc0a534706 --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/06-dev/03-ad.md @@ -0,0 +1,76 @@ +--- +title: "异常检测" +sidebar_label: "异常检测" +--- + +### 输入约定 +`execute` 是算法处理的核心方法。框架调用该方法之前,在对象属性参数 `self.list` 中已经设置完毕用于异常检测的时间序列数据。 + +### 输出约定 +`execute` 方法执行完成后的返回值是长度与 `self.list` 相同的数组,数组位置 -1 的标识异常值点。 +> 例如:对于输入测量值序列 $[2, 2, 2, 2, 100]$, 假设 100 是异常点,那么方法返回的结果数组则为 $[1, 1, 1, 1, -1]$。 + + +### 示例代码 +下面我们开发一个示例异常检测算法,在异常检测中,将输入时间序列值的最后一个值设置为异常值,并返回结果。 + +```python +import numpy as np +from service import AbstractAnomalyDetectionService + +# 算法实现类名称 需要以下划线 "_" 开始,并以 Service 结束 +class _MyAnomalyDetectionService(AbstractAnomalyDetectionService): + """ 定义类,从 AbstractAnomalyDetectionService 继承,并实现 AbstractAnomalyDetectionService 类的抽象方法 """ + + # 定义算法调用关键词,全小写ASCII码 + name = 'myad' + + # 该算法的描述信息(建议添加) + desc = """return the last value as the anomaly data""" + + def __init__(self): + """类初始化方法""" + super().__init__() + + def execute(self): + """ 算法逻辑的核心实现""" + + """创建一个长度为 len(self.list),全部值为 1 的结果数组,然后将最后一个值设置为 -1,表示最后一个值是异常值""" + res = [1] * len(self.list) + res[-1] = -1 + + """返回结果数组""" + return res + + + def set_params(self, params): + """该算法无需任何输入参数,直接重载父类该函数,不处理算法参数设置逻辑""" + pass +``` + +将该文件保存在 `./taosanalytics/algo/ad/` 目录下,然后重启 taosanode 服务。在 TDengine 命令行接口 taos 中执行 `SHOW ANODES FULL` 就能够看到新加入的算法,然后应用就可以通过 SQL 语句调用该检测算法。 + +```SQL +--- 对 col 列进行异常检测,通过指定 algo 参数为 myad 来调用新添加的异常检测类 +SELECT COUNT(*) FROM foo ANOMALY_DETECTION(col, 'algo=myad') +``` +如果是第一次启动该 Anode, 请按照 [TDgpt 安装部署](../../management/) 里的步骤先将该 Anode 添加到 TDengine 系统中。 + +### 单元测试 + +在测试目录`taosanalytics/test`中的 anomaly_test.py 中增加单元测试用例或添加新的测试文件。框架中使用了 Python Unit test 包。 + +```python +def test_myad(self): + """ 测试 _IqrService 类 """ + s = loader.get_service("myad") + + # 设置需要进行检测的输入数据 + s.set_input_list(AnomalyDetectionTest.input_list) + + r = s.execute() + + # 最后一个点是异常点 + self.assertEqual(r[-1], -1) + self.assertEqual(len(r), len(AnomalyDetectionTest.input_list)) +``` diff --git a/docs/zh/06-advanced/06-TDgpt/06-dev/index.md b/docs/zh/06-advanced/06-TDgpt/06-dev/index.md new file mode 100644 index 0000000000..b7f048cefc --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/06-dev/index.md @@ -0,0 +1,82 @@ +--- +title: "算法开发者指南" +sidebar_label: "算法开发者指南" +--- +TDgpt 是一个可扩展的时序数据高级分析平台,用户遵循简易的步骤就能将自己开发的分析算法添加到分析平台, 各种应用就可以通过SQL语句直接调用, 让高级分析算法的使用门槛降到几乎为零。目前 TDpgt 平台只支持使用 Python 语言开发的分析算法。 +Anode 采用类动态加载模式,在启动的时候扫描特定目录内满足约定条件的所有代码文件,并将其加载到系统中。因此,开发者只需要遵循以下几步就能完成新算法的添加工作: +1. 开发完成符合要求的分析算法类 +2. 将代码文件放入对应目录,然后重启 Anode +3. 使用SQL命令"CREATE ANODE",将 Anode 添加到 TDengine + +此时就完成了新算法的添加工作,之后应用就可以直接使用SQL语句调用新算法。得益于 TDgpt 与 TDengine主进程 `taosd` 的松散耦合,Anode算法升级对 `taosd` 完全没有影响。应用系统只需要调整对应的SQL语句调用新(升级的)算法,就能够快速完成分析功能和分析算法的升级。 + +这种方式能够按需扩展分析算法,极大地拓展 TDgpt 的适应范围,用户可以按需将更契合业务场景的、更准确的(预测、异常检测)分析算法动态嵌入到 TDgpt,并通过 SQL 语句进行调用。在基本不用更改应用系统代码的前提下,就能够快速完成分析功能的平滑升级。 + +以下内容将说明如何将分析算法添加到 Anode 中并能够通过SQL语句进行调用。 + +## 目录结构 +Anode的主要目录结构如下图所示 + +```bash +. +├── cfg +├── model +│   └── ac_detection +├── release +├── script +└── taosanalytics + ├── algo + │   ├── ad + │   └── fc + ├── misc + └── test + +``` + +|目录|说明| +|---|---| +|taosanalytics| 源代码目录,其下包含了算法具体保存目录 algo,放置杂项目录 misc,单元测试和集成测试目录 test。 algo 目录下 ad 保存异常检测算法代码,fc 目录保存预测算法代码| +|script|是安装脚本和发布脚本放置目录| +|model|放置针对数据集完成的训练模型| +|cfg|配置文件目录| + +## 约定与限制 + +- 异常检测算法的 Python 代码文件需放在 `./taos/algo/ad` 目录中 +- 预测算法 Python 代码文件需要放在 `./taos/algo/fc` 目录中 + + +### 类命名规范 + +Anode采用算法自动加载模式,因此只识别符合命名约定的 Python 类。需要加载的算法类名称需要以下划线 `_` 开始并以 `Service` 结尾。例如:`_KsigmaService` 是 KSigma 异常检测算法类。 + +### 类继承约定 + +- 异常检测算法需要从 `AbstractAnomalyDetectionService` 继承,并实现其核心抽象方法 `execute` +- 预测算法需要从 `AbstractForecastService` 继承,同样需要实现其核心抽象方法 `execute` + +### 类属性初始化 +实现的类需要初始化以下两个类属性: + +- `name`:识别该算法的关键词,全小写英文字母。通过 `SHOW` 命令查看可用算法显示的名称即为该名称。 +- `desc`:算法的基础描述信息 + +```SQL +--- algo 后面的参数 name 即为类属性 `name` +SELECT COUNT(*) FROM foo ANOMALY_DETECTION(col_name, 'algo=name') +``` + +## 添加具有模型的分析算法 + +基于统计学的分析算法可以直接针对输入时间序列数据进行分析,但是某些深度学习算法对于输入数据需要较长的时间训练,并且生成相应的模型。这种情况下,同一个分析算法对应不同的输入数据集有不同的分析模型。 +将具有模型的分析算法添加到 Anode 中,首先需要在 `model` 目录中建立该算法对应的目录(目录名称可自拟),将采用该算法针对不同的输入时间序列数据生成的训练模型均需要保存在该目录下,同时目录名称要在分析算法中确定,以便能够固定加载该目录下的分析模型。为了确保模型能够正常读取加载,存储的模型使用`joblib`库进行序列化保存。 + +下面以自编码器(Autoencoder)为例,说明如何添加要预先训练的模型进行异常检测。 +首先我们在`model`目录中创建一个目录 -- `ad_detection`,该目录将用来保存所有使用自编码器训练的模型。然后,我们使用自编码器对 foo 表的时间序列数据进行训练,得到模型 ad_autoencoder_foo,使用 `joblib`序列化以后保存在`ad_detection` 目录中。 + +使用 SQL 调用已经保存的模型,需要在调用参数中指定模型名称``model=ad_autoencoder_foo`,而 `algo=encoder` 是确定调用的自编码器生成的模型(这里的`encoder`说明调用的是自编码器算法模型,该名称是添加算法的时候在代码中定义)以便能够调用该模型。 + +```SQL +--- 在 options 中增加 model 的名称,ad_autoencoder_foo, 针对 foo 数据集(表)训练的采用自编码器的异常检测模型进行异常检测 +SELECT COUNT(*), _WSTART FROM foo ANOMALY_DETECTION(col1, 'algo=encoder, model=ad_autoencoder_foo'); +``` diff --git a/docs/zh/06-advanced/06-TDgpt/index.md b/docs/zh/06-advanced/06-TDgpt/index.md new file mode 100644 index 0000000000..3f650b196b --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/index.md @@ -0,0 +1,25 @@ +--- +sidebar_label: TDgpt +title: TDgpt +--- + +import TDgpt from './pic/data-analysis.png'; + + +TDgpt 是 TDengine Enterprise 中针对时序数据提供高级分析功能的企业级组件,通过内置接口向 TDengine 提供运行时动态扩展的时序数据分析服务。TDgpt 能够独立于 TDengine 主进程部署和运行,因此可避免消耗占用 TDengine 集群的主进程资源。 +TDgpt 具有服务无状态、功能易扩展、快速弹性部署、应用轻量化、高安全性等优势。 +TDgpt 运行在集群中的 AI Node (Anode)中,集群中可以部署若干个 Anode 节点,不同的 Anode 节点之间无同步依赖或协同的要求。Anode 注册到 TDengine 集群以后,立即就可以提供服务。TDgpt 提供的高级时序数据分析服务可分为时序数据异常检测和时序数据预测分析两大类。 + +下图是部署 TDgpt 的 TDengine 集群示意图。 +TDgpt架构图 + +在查询处理过程中,Vnode中运行的查询引擎会根据查询处理物理执行计划,按需向 Anode 请求高级时序数据分析服务。因此用户可通过 SQL 语句与 Anode 节点交互并使用其提供的全部分析服务。需要注意的是 Anode 不直接接受用户的数据分析请求。同时 Anode 具备分析算法动态注册机制,其算法扩展过程完全不影响 TDengine 集群的服务,仅在非常小的(秒级)时间窗口内影响涉及高级分析的查询服务。 + +目前 TDgpt 提供如下的高级分析服务: +- 时序数据异常检测。TDengine 中定义了新的时间窗口——异常(状态)窗口——来提供异常检测服务。异常窗口可以视为一种特殊的**事件窗口(Event Window)**,即异常检测算法确定的连续异常时间序列数据所在的时间窗口。与普通事件窗口区别在于——时间窗口的起始时间和结束时间均是分析算法确定,不是用户指定的表达式判定。异常窗口使用方式与其他类型的时间窗口(例如状态窗口、会话窗口等)类似。因此时间窗口内可使用的查询操作均可应用在异常窗口上。 +- 时序数据预测。定义了一个新函数`FORECAST`,基于输入的(历史)时间序列数据调用指定(或默认)预测算法给出输入时序数据后续时间序列的**预测**数据。 + +TDgpt 还为算法开发者提供了一 SDK。任何开发者只需要按照[算法开发者指南](./dev)的步骤,就可以将自己独有的时序数据预测或时序数据异常检测算法无缝集成到 TDgpt, 这样 TDengine 用户就可以通过一条 SQL 获得时序数据预测结果或是异常窗口了, 大幅降低了用户使用新的时序数据分析算法的门槛,而且让 TDengine 成为一开放的系统。 + + + diff --git a/docs/zh/06-advanced/06-TDgpt/pic/activity.png b/docs/zh/06-advanced/06-TDgpt/pic/activity.png new file mode 100644 index 0000000000000000000000000000000000000000..2d2a403de520b7f12f515af31cd6ce9c28a0cfa0 GIT binary patch literal 46913 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 yRAItT*iQk!)D0;v3qGnE~uA|_IB7E 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ⅅ`9e)LdCO3rBL8i@vi!av{HG^P{xy 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

M8WE3g^LF2cOBIowv=RzgR9J;ObQKCpV;R}X13 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 LyO^tC9rAwwto73; literal 0 HcmV?d00001 diff --git a/docs/zh/06-advanced/06-TDgpt/pic/white-noise-data.png b/docs/zh/06-advanced/06-TDgpt/pic/white-noise-data.png new file mode 100644 index 0000000000000000000000000000000000000000..754c6f3d388ec25a5d975cbe0de86e4fa5b62a9f GIT binary patch literal 88637 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 NOE)CcH(N;|IaBAVyM0)~`7K9Tg7#Lv8s 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 literal 0 HcmV?d00001 From 2d13ce510572cd73b785faac0ae3b6449324a998 Mon Sep 17 00:00:00 2001 From: Shengliang Guan Date: Fri, 22 Nov 2024 15:33:55 +0800 Subject: [PATCH 84/88] refact: rename taos lib name --- cmake/cmake.define | 7 +++-- source/client/CMakeLists.txt | 22 ++++++------- source/client/test/CMakeLists.txt | 10 +++--- source/dnode/mgmt/node_util/CMakeLists.txt | 2 +- source/libs/catalog/test/CMakeLists.txt | 2 +- source/libs/executor/test/CMakeLists.txt | 2 +- source/libs/scheduler/test/CMakeLists.txt | 4 +-- tests/taosc_test/CMakeLists.txt | 2 +- tools/CMakeLists.txt | 12 ++++---- tools/shell/CMakeLists.txt | 6 ++-- utils/test/c/CMakeLists.txt | 36 +++++++++++----------- 11 files changed, 54 insertions(+), 51 deletions(-) diff --git a/cmake/cmake.define b/cmake/cmake.define index ff582261b3..5c766de142 100644 --- a/cmake/cmake.define +++ b/cmake/cmake.define @@ -97,10 +97,13 @@ ELSE() SET(TD_TAOS_TOOLS TRUE) ENDIF() +SET(TAOS_LIB taos) +SET(TAOS_LIB_STATIC taos_static) + IF(${TD_WINDOWS}) - SET(TAOS_LIB taos_static) + SET(TAOS_LIB_LINK taos_static) ELSE() - SET(TAOS_LIB taos) + SET(TAOS_LIB_LINK taos) ENDIF() # build TSZ by default diff --git a/source/client/CMakeLists.txt b/source/client/CMakeLists.txt index 6d5f006517..2113aa7921 100644 --- a/source/client/CMakeLists.txt +++ b/source/client/CMakeLists.txt @@ -5,24 +5,24 @@ if(TD_ENTERPRISE) endif() if(TD_WINDOWS) - add_library(taos SHARED ${CLIENT_SRC} ${CMAKE_CURRENT_SOURCE_DIR}/src/taos.rc.in) + add_library(${TAOS_LIB} SHARED ${CLIENT_SRC} ${CMAKE_CURRENT_SOURCE_DIR}/src/taos.rc.in) else() - add_library(taos SHARED ${CLIENT_SRC}) + add_library(${TAOS_LIB} SHARED ${CLIENT_SRC}) endif() if(${TD_DARWIN}) - target_compile_options(taos PRIVATE -Wno-error=deprecated-non-prototype) + target_compile_options(${TAOS_LIB} PRIVATE -Wno-error=deprecated-non-prototype) endif() INCLUDE_DIRECTORIES(jni) target_include_directories( - taos + ${TAOS_LIB} PUBLIC "${TD_SOURCE_DIR}/include/client" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/inc" ) target_link_libraries( - taos + ${TAOS_LIB} INTERFACE api PRIVATE os util common transport monitor nodes parser command planner catalog scheduler function qcom geometry ) @@ -36,32 +36,32 @@ else() endif() set_target_properties( - taos + ${TAOS_LIB} PROPERTIES CLEAN_DIRECT_OUTPUT 1 ) set_target_properties( - taos + ${TAOS_LIB} PROPERTIES VERSION ${TD_VER_NUMBER} SOVERSION 1 ) -add_library(taos_static STATIC ${CLIENT_SRC}) +add_library(${TAOS_LIB_STATIC} STATIC ${CLIENT_SRC}) if(${TD_DARWIN}) - target_compile_options(taos_static PRIVATE -Wno-error=deprecated-non-prototype) + target_compile_options(${TAOS_LIB_STATIC} PRIVATE -Wno-error=deprecated-non-prototype) endif() target_include_directories( - taos_static + ${TAOS_LIB_STATIC} PUBLIC "${TD_SOURCE_DIR}/include/client" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/inc" ) target_link_libraries( - taos_static + ${TAOS_LIB_STATIC} INTERFACE api PRIVATE os util common transport monitor nodes parser command planner catalog scheduler function qcom geometry ) diff --git a/source/client/test/CMakeLists.txt b/source/client/test/CMakeLists.txt index 7ca3086871..bfe9d7b11f 100644 --- a/source/client/test/CMakeLists.txt +++ b/source/client/test/CMakeLists.txt @@ -8,31 +8,31 @@ AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} SOURCE_LIST) ADD_EXECUTABLE(clientTest clientTests.cpp) TARGET_LINK_LIBRARIES( clientTest - os util common transport parser catalog scheduler gtest taos_static qcom executor function + os util common transport parser catalog scheduler gtest ${TAOS_LIB_LINK} qcom executor function ) ADD_EXECUTABLE(tmqTest tmqTest.cpp) TARGET_LINK_LIBRARIES( tmqTest - PUBLIC os util common transport parser catalog scheduler function gtest taos_static qcom + PUBLIC os util common transport parser catalog scheduler function gtest ${TAOS_LIB_LINK} qcom ) ADD_EXECUTABLE(smlTest smlTest.cpp) TARGET_LINK_LIBRARIES( smlTest - PUBLIC os util common transport parser catalog scheduler function gtest taos_static qcom geometry + PUBLIC os util common transport parser catalog scheduler function gtest ${TAOS_LIB_LINK} qcom geometry ) #ADD_EXECUTABLE(clientMonitorTest clientMonitorTests.cpp) #TARGET_LINK_LIBRARIES( # clientMonitorTest -# PUBLIC os util common transport monitor parser catalog scheduler function gtest taos_static qcom executor +# PUBLIC os util common transport monitor parser catalog scheduler function gtest ${TAOS_LIB_LINK} qcom executor #) ADD_EXECUTABLE(userOperTest ../../../tests/script/api/passwdTest.c) TARGET_LINK_LIBRARIES( userOperTest - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} ) TARGET_INCLUDE_DIRECTORIES( diff --git a/source/dnode/mgmt/node_util/CMakeLists.txt b/source/dnode/mgmt/node_util/CMakeLists.txt index d882d784de..320da45065 100644 --- a/source/dnode/mgmt/node_util/CMakeLists.txt +++ b/source/dnode/mgmt/node_util/CMakeLists.txt @@ -6,5 +6,5 @@ target_include_directories( PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/inc" ) target_link_libraries( - node_util cjson mnode vnode qnode snode wal sync taos_static tfs monitor monitorfw + node_util cjson mnode vnode qnode snode wal sync ${TAOS_LIB_STATIC} tfs monitor monitorfw ) \ No newline at end of file diff --git a/source/libs/catalog/test/CMakeLists.txt b/source/libs/catalog/test/CMakeLists.txt index de4d08835c..97c088e381 100644 --- a/source/libs/catalog/test/CMakeLists.txt +++ b/source/libs/catalog/test/CMakeLists.txt @@ -9,7 +9,7 @@ IF(NOT TD_DARWIN) ADD_EXECUTABLE(catalogTest ${SOURCE_LIST}) TARGET_LINK_LIBRARIES( catalogTest - PUBLIC os util common nodes catalog transport gtest qcom taos_static + PUBLIC os util common nodes catalog transport gtest qcom ${TAOS_LIB_LINK} ) TARGET_INCLUDE_DIRECTORIES( diff --git a/source/libs/executor/test/CMakeLists.txt b/source/libs/executor/test/CMakeLists.txt index c75de23c32..da6aafc0f7 100644 --- a/source/libs/executor/test/CMakeLists.txt +++ b/source/libs/executor/test/CMakeLists.txt @@ -9,7 +9,7 @@ MESSAGE(STATUS "build parser unit test") # ADD_EXECUTABLE(executorTest ${SOURCE_LIST}) # TARGET_LINK_LIBRARIES( # executorTest -# PRIVATE os util common transport gtest taos_static qcom executor function planner scalar nodes vnode +# PRIVATE os util common transport gtest ${TAOS_LIB_LINK} qcom executor function planner scalar nodes vnode # ) # # TARGET_INCLUDE_DIRECTORIES( diff --git a/source/libs/scheduler/test/CMakeLists.txt b/source/libs/scheduler/test/CMakeLists.txt index 9605cc7a1c..63e5367475 100644 --- a/source/libs/scheduler/test/CMakeLists.txt +++ b/source/libs/scheduler/test/CMakeLists.txt @@ -11,12 +11,12 @@ IF(NOT TD_DARWIN) IF (TD_GRANT) TARGET_LINK_LIBRARIES( schedulerTest - PUBLIC os util common catalog transport gtest qcom taos_static planner scheduler grant + PUBLIC os util common catalog transport gtest qcom ${TAOS_LIB_LINK} planner scheduler grant ) ELSE () TARGET_LINK_LIBRARIES( schedulerTest - PUBLIC os util common catalog transport gtest qcom taos_static planner scheduler + PUBLIC os util common catalog transport gtest qcom ${TAOS_LIB_LINK} planner scheduler ) ENDIF() diff --git a/tests/taosc_test/CMakeLists.txt b/tests/taosc_test/CMakeLists.txt index c16fe59271..e78d23f3fe 100644 --- a/tests/taosc_test/CMakeLists.txt +++ b/tests/taosc_test/CMakeLists.txt @@ -16,7 +16,7 @@ aux_source_directory(src OS_SRC) # taoscTest add_executable(taoscTest "taoscTest.cpp") -target_link_libraries(taoscTest taos os gtest_main) +target_link_libraries(taoscTest ${TAOS_LIB_LINK} os gtest_main) target_include_directories( taoscTest PUBLIC "${TD_SOURCE_DIR}/include/os" diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 5e93be695d..d058d7a52f 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -13,7 +13,7 @@ IF(TD_WEBSOCKET) PREFIX "taosws-rs" SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/taosws-rs BUILD_ALWAYS off - DEPENDS taos + DEPENDS ${TAOS_LIB} BUILD_IN_SOURCE 1 CONFIGURE_COMMAND cmake -E echo "taosws-rs no need cmake to config" PATCH_COMMAND @@ -32,7 +32,7 @@ IF(TD_WEBSOCKET) PREFIX "taosws-rs" SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/taosws-rs BUILD_ALWAYS off - DEPENDS taos + DEPENDS ${TAOS_LIB} BUILD_IN_SOURCE 1 CONFIGURE_COMMAND cmake -E echo "taosws-rs no need cmake to config" PATCH_COMMAND @@ -52,7 +52,7 @@ IF(TD_WEBSOCKET) PREFIX "taosws-rs" SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/taosws-rs BUILD_ALWAYS off - DEPENDS taos + DEPENDS ${TAOS_LIB} BUILD_IN_SOURCE 1 CONFIGURE_COMMAND cmake -E echo "taosws-rs no need cmake to config" PATCH_COMMAND @@ -139,7 +139,7 @@ ELSE() PREFIX "taosadapter" SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/taosadapter BUILD_ALWAYS off - DEPENDS taos + DEPENDS ${TAOS_LIB} BUILD_IN_SOURCE 1 CONFIGURE_COMMAND cmake -E echo "taosadapter no need cmake to config" PATCH_COMMAND @@ -168,7 +168,7 @@ ELSE() PREFIX "taosadapter" SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/taosadapter BUILD_ALWAYS off - DEPENDS taos + DEPENDS ${TAOS_LIB} BUILD_IN_SOURCE 1 CONFIGURE_COMMAND cmake -E echo "taosadapter no need cmake to config" PATCH_COMMAND @@ -193,7 +193,7 @@ ELSE() PREFIX "taosadapter" SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/taosadapter BUILD_ALWAYS off - DEPENDS taos + DEPENDS ${TAOS_LIB} BUILD_IN_SOURCE 1 CONFIGURE_COMMAND cmake -E echo "taosadapter no need cmake to config" PATCH_COMMAND diff --git a/tools/shell/CMakeLists.txt b/tools/shell/CMakeLists.txt index 4a8e0b9d34..2301f33803 100644 --- a/tools/shell/CMakeLists.txt +++ b/tools/shell/CMakeLists.txt @@ -33,9 +33,9 @@ ELSE() ENDIF() if(TD_WINDOWS) - target_link_libraries(shell PUBLIC taos_static ${LINK_WEBSOCKET}) + target_link_libraries(shell PUBLIC ${TAOS_LIB_STATIC} ${LINK_WEBSOCKET}) else() - target_link_libraries(shell PUBLIC taos ${LINK_WEBSOCKET} ${LINK_JEMALLOC} ${LINK_ARGP}) + target_link_libraries(shell PUBLIC ${TAOS_LIB} ${LINK_WEBSOCKET} ${LINK_JEMALLOC} ${LINK_ARGP}) endif() target_link_libraries( @@ -63,7 +63,7 @@ IF(TD_LINUX) IF(TD_WEBSOCKET) ADD_DEPENDENCIES(shell_ut taosws-rs) ENDIF() - target_link_libraries(shell_ut PUBLIC taos ${LINK_WEBSOCKET} ${LINK_JEMALLOC} ${LINK_ARGP}) + target_link_libraries(shell_ut PUBLIC ${TAOS_LIB} ${LINK_WEBSOCKET} ${LINK_JEMALLOC} ${LINK_ARGP}) target_link_libraries(shell_ut PRIVATE os common transport geometry util) # util depends diff --git a/utils/test/c/CMakeLists.txt b/utils/test/c/CMakeLists.txt index 7589d11840..1593b528ed 100644 --- a/utils/test/c/CMakeLists.txt +++ b/utils/test/c/CMakeLists.txt @@ -1,5 +1,5 @@ add_executable(tmq_demo tmqDemo.c) -add_dependencies(tmq_demo taos) +add_dependencies(tmq_demo ${TAOS_LIB_LINK}) add_executable(tmq_sim tmqSim.c) add_executable(create_table createTable.c) add_executable(tmq_taosx_ci tmq_taosx_ci.c) @@ -22,7 +22,7 @@ endif(${TD_LINUX}) target_link_libraries( tmq_offset - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os @@ -30,7 +30,7 @@ target_link_libraries( target_link_libraries( tmq_multi_thread_test - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os @@ -38,56 +38,56 @@ target_link_libraries( target_link_libraries( create_table - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_demo - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_sim - PUBLIC ${TAOS_LIB} + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_ts5466 - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_td32187 - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_td32526 - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_taosx_ci - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_offset_test - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os @@ -95,7 +95,7 @@ target_link_libraries( target_link_libraries( replay_test - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os @@ -103,7 +103,7 @@ target_link_libraries( target_link_libraries( write_raw_block_test - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os @@ -111,7 +111,7 @@ target_link_libraries( target_link_libraries( tmq_write_raw_test - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os @@ -119,7 +119,7 @@ target_link_libraries( target_link_libraries( sml_test - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os @@ -128,7 +128,7 @@ target_link_libraries( target_link_libraries( get_db_name_test - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os @@ -136,7 +136,7 @@ target_link_libraries( target_link_libraries( varbinary_test - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os @@ -145,7 +145,7 @@ target_link_libraries( if(${TD_LINUX}) target_link_libraries( tsz_test - PUBLIC taos + PUBLIC ${TAOS_LIB_LINK} PUBLIC util PUBLIC common PUBLIC os From 5e80e89cb3c87fce528482ae8bd85103c3ca96e4 Mon Sep 17 00:00:00 2001 From: Shengliang Guan Date: Fri, 22 Nov 2024 16:23:16 +0800 Subject: [PATCH 85/88] refact: rename taos lib name --- examples/c/CMakeLists.txt | 71 ++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index 07fc2fd71b..7427c482c4 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(TDengine) -IF (TD_LINUX) - INCLUDE_DIRECTORIES(. ${TD_SOURCE_DIR}/src/inc ${TD_SOURCE_DIR}/src/client/inc ${TD_SOURCE_DIR}/inc) +IF(TD_LINUX) + INCLUDE_DIRECTORIES(. ${TD_SOURCE_DIR}/src/inc ${TD_SOURCE_DIR}/src/client/inc ${TD_SOURCE_DIR}/inc) AUX_SOURCE_DIRECTORY(. SRC) add_executable(tmq "") @@ -12,58 +12,58 @@ IF (TD_LINUX) add_executable(asyncdemo "") target_sources(tmq - PRIVATE - "tmq.c" - ) + PRIVATE + "tmq.c" + ) target_sources(stream_demo - PRIVATE - "stream_demo.c" - ) + PRIVATE + "stream_demo.c" + ) target_sources(schemaless - PRIVATE - "schemaless.c" - ) + PRIVATE + "schemaless.c" + ) target_sources(prepare - PRIVATE + PRIVATE "prepare.c" - ) - + ) + target_sources(demo - PRIVATE + PRIVATE "demo.c" - ) + ) target_sources(asyncdemo - PRIVATE + PRIVATE "asyncdemo.c" - ) + ) target_link_libraries(tmq - taos - ) + ${TAOS_LIB_LINK} + ) target_link_libraries(stream_demo - taos - ) + ${TAOS_LIB_LINK} + ) target_link_libraries(schemaless - taos - ) + ${TAOS_LIB_LINK} + ) target_link_libraries(prepare - taos - ) - + ${TAOS_LIB_LINK} + ) + target_link_libraries(demo - taos - ) + ${TAOS_LIB_LINK} + ) target_link_libraries(asyncdemo - taos - ) + ${TAOS_LIB_LINK} + ) SET_TARGET_PROPERTIES(tmq PROPERTIES OUTPUT_NAME tmq) SET_TARGET_PROPERTIES(stream_demo PROPERTIES OUTPUT_NAME stream_demo) @@ -71,8 +71,9 @@ IF (TD_LINUX) SET_TARGET_PROPERTIES(prepare PROPERTIES OUTPUT_NAME prepare) SET_TARGET_PROPERTIES(demo PROPERTIES OUTPUT_NAME demo) SET_TARGET_PROPERTIES(asyncdemo PROPERTIES OUTPUT_NAME asyncdemo) -ENDIF () -IF (TD_DARWIN) - INCLUDE_DIRECTORIES(. ${TD_SOURCE_DIR}/src/inc ${TD_SOURCE_DIR}/src/client/inc ${TD_SOURCE_DIR}/inc) +ENDIF() + +IF(TD_DARWIN) + INCLUDE_DIRECTORIES(. ${TD_SOURCE_DIR}/src/inc ${TD_SOURCE_DIR}/src/client/inc ${TD_SOURCE_DIR}/inc) AUX_SOURCE_DIRECTORY(. SRC) -ENDIF () +ENDIF() From 09a9298de7414a27ae77e3b1f492f964e59b8080 Mon Sep 17 00:00:00 2001 From: Pengrongkun Date: Thu, 21 Nov 2024 19:35:44 +0800 Subject: [PATCH 86/88] TD-32797:fix array realloc problem --- source/libs/parser/src/parInsertStmt.c | 21 +- tests/script/api/stmt2-nchar.c | 273 +++++++++++++++++++++++++ tests/script/api/stmt2.c | 19 +- 3 files changed, 299 insertions(+), 14 deletions(-) create mode 100644 tests/script/api/stmt2-nchar.c diff --git a/source/libs/parser/src/parInsertStmt.c b/source/libs/parser/src/parInsertStmt.c index bedd4fa540..c6951d229d 100644 --- a/source/libs/parser/src/parInsertStmt.c +++ b/source/libs/parser/src/parInsertStmt.c @@ -681,11 +681,25 @@ int32_t qBindStmtStbColsValue2(void* pBlock, SArray* pCols, TAOS_STMT2_BIND* bin int32_t code = 0; int16_t lastColId = -1; bool colInOrder = true; + int ncharColNums = 0; if (NULL == *pTSchema) { *pTSchema = tBuildTSchema(pSchema, pDataBlock->pMeta->tableInfo.numOfColumns, pDataBlock->pMeta->sversion); } + for (int c = 0; c < boundInfo->numOfBound; ++c) { + if (TSDB_DATA_TYPE_NCHAR == pSchema[boundInfo->pColIndex[c]].type) { + ncharColNums++; + } + } + if (ncharColNums > 0) { + ncharBinds = taosArrayInit(ncharColNums, sizeof(ncharBind)); + if (!ncharBinds) { + code = terrno; + goto _return; + } + } + for (int c = 0; c < boundInfo->numOfBound; ++c) { SSchema* pColSchema = &pSchema[boundInfo->pColIndex[c]]; if (pColSchema->colId <= lastColId) { @@ -710,13 +724,6 @@ int32_t qBindStmtStbColsValue2(void* pBlock, SArray* pCols, TAOS_STMT2_BIND* bin if (code) { goto _return; } - if (!ncharBinds) { - ncharBinds = taosArrayInit(1, sizeof(ncharBind)); - if (!ncharBinds) { - code = terrno; - goto _return; - } - } if (!taosArrayPush(ncharBinds, &ncharBind)) { code = terrno; goto _return; diff --git a/tests/script/api/stmt2-nchar.c b/tests/script/api/stmt2-nchar.c new file mode 100644 index 0000000000..3952bd5898 --- /dev/null +++ b/tests/script/api/stmt2-nchar.c @@ -0,0 +1,273 @@ +// sample code to verify all TDengine API +// to compile: gcc -o apitest apitest.c -ltaos + +#include +#include +#include +#include +#include "taos.h" +static int64_t count = 10000; + +int64_t genReqid() { + count += 100; + return count; +} + +void stmtAsyncQueryCb(void* param, TAOS_RES* pRes, int code) { + int affected_rows = taos_affected_rows(pRes); + return; + /* + SSP_CB_PARAM* qParam = (SSP_CB_PARAM*)param; + if (code == 0 && pRes) { + if (qParam->fetch) { + taos_fetch_rows_a(pRes, sqAsyncFetchCb, param); + } else { + if (qParam->free) { + taos_free_result(pRes); + } + *qParam->end = 1; + } + } else { + sqError("select", taos_errstr(pRes)); + *qParam->end = 1; + taos_free_result(pRes); + } + */ +} + +void veriry_stmt(TAOS* taos) { + TAOS_RES* result = taos_query(taos, "drop database if exists test;"); + taos_free_result(result); + usleep(100000); + result = taos_query(taos, "create database test;"); + + int code = taos_errno(result); + if (code != 0) { + printf("\033[31mfailed to create database, reason:%s\033[0m\n", taos_errstr(result)); + taos_free_result(result); + return; + } + taos_free_result(result); + + usleep(100000); + taos_select_db(taos, "test"); + + // create table + /* + const char* sql = + "create table m1 (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin " + "binary(40), blob nchar(10))"; + */ + const char* sql = + "create table m1 (ts timestamp, blob2 nchar(10), blob nchar(10),blob3 nchar(10),blob4 nchar(10),blob5 " + "nchar(10))"; + result = taos_query(taos, sql); + code = taos_errno(result); + if (code != 0) { + printf("\033[31mfailed to create table, reason:%s\033[0m\n", taos_errstr(result)); + taos_free_result(result); + return; + } + taos_free_result(result); + + // insert 10 records + struct { + int64_t ts[10]; + char blob[10][1]; + char blob2[10][1]; + char blob3[10][1]; + char blob4[10][1]; + char blob5[10][1]; + + } v; + + int32_t* t64_len = malloc(sizeof(int32_t) * 10); + int32_t* blob_len = malloc(sizeof(int32_t) * 10); + int32_t* blob_len2 = malloc(sizeof(int32_t) * 10); + int32_t* blob_len3 = malloc(sizeof(int32_t) * 10); + int32_t* blob_len4 = malloc(sizeof(int32_t) * 10); + int32_t* blob_len5 = malloc(sizeof(int32_t) * 10); + +#include "time.h" + clock_t start, end; + TAOS_STMT2_OPTION option = {0, true, true, stmtAsyncQueryCb, NULL}; + + start = clock(); + TAOS_STMT2* stmt = taos_stmt2_init(taos, &option); + end = clock(); + printf("init time:%f\n", (double)(end - start) / CLOCKS_PER_SEC); + // TAOS_MULTI_BIND params[10]; + TAOS_STMT2_BIND params[10]; + char is_null[10] = {0}; + + params[0].buffer_type = TSDB_DATA_TYPE_TIMESTAMP; + // params[0].buffer_length = sizeof(v.ts[0]); + params[0].buffer = v.ts; + params[0].length = t64_len; + params[0].is_null = is_null; + params[0].num = 10; + + params[1].buffer_type = TSDB_DATA_TYPE_NCHAR; + // params[8].buffer_length = sizeof(v.blob2[0]); + params[1].buffer = v.blob2; + params[1].length = blob_len2; + params[1].is_null = is_null; + params[1].num = 10; + + params[2].buffer_type = TSDB_DATA_TYPE_NCHAR; + // params[9].buffer_length = sizeof(v.blob[0]); + params[2].buffer = v.blob3; + params[2].length = blob_len; + params[2].is_null = is_null; + params[2].num = 10; + + params[3].buffer_type = TSDB_DATA_TYPE_NCHAR; + // params[9].buffer_length = sizeof(v.blob[0]); + params[3].buffer = v.blob4; + params[3].length = blob_len; + params[3].is_null = is_null; + params[3].num = 10; + + params[4].buffer_type = TSDB_DATA_TYPE_NCHAR; + // params[9].buffer_length = sizeof(v.blob[0]); + params[4].buffer = v.blob; + params[4].length = blob_len; + params[4].is_null = is_null; + params[4].num = 10; + + params[5].buffer_type = TSDB_DATA_TYPE_NCHAR; + // params[9].buffer_length = sizeof(v.blob[0]); + params[5].buffer = v.blob5; + params[5].length = blob_len; + params[5].is_null = is_null; + params[5].num = 10; + + sql = "insert into ? (ts, blob2, blob, blob3, blob4, blob5) values(?,?,?,?,?,?)"; + start = clock(); + code = taos_stmt2_prepare(stmt, sql, 0); + end = clock(); + printf("prepare time:%f\n", (double)(end - start) / CLOCKS_PER_SEC); + if (code != 0) { + printf("\033[31mfailed to execute taos_stmt_prepare. error:%s\033[0m\n", taos_stmt_errstr(stmt)); + taos_stmt_close(stmt); + return; + } + /* + code = taos_stmt_set_tbname(stmt, "m1"); + if (code != 0) { + printf("\033[31mfailed to execute taos_stmt_prepare. error:%s\033[0m\n", taos_stmt_errstr(stmt)); + taos_stmt_close(stmt); + return; + } + */ + + int64_t ts = 1591060628000; + for (int i = 0; i < 10; ++i) { + is_null[i] = 0; + + v.ts[i] = ts++; + + v.blob[i][0] = 'a' + i; + v.blob2[i][0] = 'f' + i; + v.blob3[i][0] = 't' + i; + v.blob4[i][0] = 'A' + i; + v.blob5[i][0] = 'G' + i; + + // v.blob2[i] = malloc(strlen("一二三四五六七十九八")); + // v.blob[i] = malloc(strlen("十九八七六五四三二一")); + + // strcpy(v.blob2[i], "一二三四五六七十九八"); + // strcpy(v.blob[i], "十九八七六五四三二一"); + + blob_len[i] = sizeof(char); + blob_len2[i] = sizeof(char); + blob_len3[i] = sizeof(char); + blob_len4[i] = sizeof(char); + blob_len5[i] = sizeof(char); + } + + char* tbname = "m1"; + TAOS_STMT2_BIND* bind_cols[1] = {¶ms[0]}; + TAOS_STMT2_BINDV bindv = {1, &tbname, NULL, &bind_cols[0]}; + start = clock(); + // taos_stmt2_bind_param(stmt, "m1", NULL, params, -1); + taos_stmt2_bind_param(stmt, &bindv, -1); + end = clock(); + printf("bind time:%f\n", (double)(end - start) / CLOCKS_PER_SEC); + // taos_stmt_bind_param_batch(stmt, params); + // taos_stmt_add_batch(stmt); + /* + int param_count = -1; + code = taos_stmt2_param_count(stmt, ¶m_count); + if (code != 0) { + printf("\033[31mfailed to execute taos_stmt_param_count. error:%s\033[0m\n", taos_stmt_errstr(stmt)); + taos_stmt_close(stmt); + return; + } + printf("param_count: %d\n", param_count); + */ + TAOS_FIELD_E* fields = NULL; + int field_count = -1; + start = clock(); + code = taos_stmt2_get_fields(stmt, TAOS_FIELD_COL, &field_count, NULL); + end = clock(); + printf("get fields time:%f\n", (double)(end - start) / CLOCKS_PER_SEC); + if (code != 0) { + printf("\033[31mfailed to execute taos_stmt_param_count. error:%s\033[0m\n", taos_stmt_errstr(stmt)); + taos_stmt_close(stmt); + return; + } + printf("col field_count: %d\n", field_count); + start = clock(); + taos_stmt2_free_fields(stmt, fields); + end = clock(); + printf("free time:%f\n", (double)(end - start) / CLOCKS_PER_SEC); + /* + code = taos_stmt2_get_fields(stmt, TAOS_FIELD_TAG, &field_count, &fields); + if (code != 0) { + printf("\033[31mfailed to execute taos_stmt_param_count. error:%s\033[0m\n", taos_stmt_errstr(stmt)); + taos_stmt_close(stmt); + return; + } + printf("tag field_count: %d\n", field_count); + taos_stmt2_free_fields(stmt, fields); + */ + // if (taos_stmt_execute(stmt) != 0) { + start = clock(); + // if (taos_stmt2_exec(stmt, NULL, stmtAsyncQueryCb, NULL) != 0) { + if (taos_stmt2_exec(stmt, NULL) != 0) { + printf("\033[31mfailed to execute insert statement.error:%s\033[0m\n", taos_stmt_errstr(stmt)); + taos_stmt2_close(stmt); + return; + } + end = clock(); + printf("exec time:%f\n", (double)(end - start) / CLOCKS_PER_SEC); + + taos_stmt2_close(stmt); + + free(blob_len); + free(blob_len2); + free(blob_len5); + free(blob_len3); + free(blob_len4); +} + +int main(int argc, char* argv[]) { + const char* host = "127.0.0.1"; + const char* user = "root"; + const char* passwd = "taosdata"; + + taos_options(TSDB_OPTION_TIMEZONE, "GMT-8"); + TAOS* taos = taos_connect(host, user, passwd, "", 0); + if (taos == NULL) { + printf("\033[31mfailed to connect to db, reason:%s\033[0m\n", taos_errstr(taos)); + exit(1); + } + + printf("********* verify stmt query **********\n"); + veriry_stmt(taos); + + printf("done\n"); + taos_close(taos); + taos_cleanup(); +} diff --git a/tests/script/api/stmt2.c b/tests/script/api/stmt2.c index 82537905dc..5b9f41baff 100644 --- a/tests/script/api/stmt2.c +++ b/tests/script/api/stmt2.c @@ -81,8 +81,8 @@ void veriry_stmt(TAOS* taos) { float f4[10]; double f8[10]; char bin[10][40]; - char blob[10][80]; - char blob2[10][80]; + char blob[10][1]; + char blob2[10][1]; } v; int32_t* t8_len = malloc(sizeof(int32_t) * 10); @@ -218,8 +218,14 @@ void veriry_stmt(TAOS* taos) { for (int j = 0; j < sizeof(v.bin[0]); ++j) { v.bin[i][j] = (char)(i + '0'); } - strcpy(v.blob2[i], "一二三四五六七十九八"); - strcpy(v.blob[i], "一二三四五六七八九十"); + v.blob[i][0] = 'a' + i; + v.blob2[i][0] = 'A' + i; + + // v.blob2[i] = malloc(strlen("一二三四五六七十九八")); + // v.blob[i] = malloc(strlen("十九八七六五四三二一")); + + // strcpy(v.blob2[i], "一二三四五六七十九八"); + // strcpy(v.blob[i], "十九八七六五四三二一"); t8_len[i] = sizeof(int8_t); t16_len[i] = sizeof(int16_t); @@ -228,10 +234,9 @@ void veriry_stmt(TAOS* taos) { float_len[i] = sizeof(float); double_len[i] = sizeof(double); bin_len[i] = sizeof(v.bin[0]); - blob_len[i] = (int32_t)strlen(v.blob[i]); - blob_len2[i] = (int32_t)strlen(v.blob2[i]); + blob_len[i] = sizeof(char); + blob_len2[i] = sizeof(char); } - char* tbname = "m1"; TAOS_STMT2_BIND* bind_cols[1] = {¶ms[0]}; TAOS_STMT2_BINDV bindv = {1, &tbname, NULL, &bind_cols[0]}; From 6a2ab2fa8373aad391c2a1a78a50061a01d56e68 Mon Sep 17 00:00:00 2001 From: Shengliang Guan Date: Fri, 22 Nov 2024 19:30:23 +0800 Subject: [PATCH 87/88] fix: compile errors --- source/client/test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/client/test/CMakeLists.txt b/source/client/test/CMakeLists.txt index bfe9d7b11f..94d36c549e 100644 --- a/source/client/test/CMakeLists.txt +++ b/source/client/test/CMakeLists.txt @@ -32,7 +32,7 @@ TARGET_LINK_LIBRARIES( ADD_EXECUTABLE(userOperTest ../../../tests/script/api/passwdTest.c) TARGET_LINK_LIBRARIES( userOperTest - PUBLIC ${TAOS_LIB_LINK} + PUBLIC os util common transport parser catalog scheduler function gtest ${TAOS_LIB_LINK} qcom geometry ) TARGET_INCLUDE_DIRECTORIES( From 877dbd8e90ecd76164fc805aa3523fd8d26da22a Mon Sep 17 00:00:00 2001 From: Shengliang Guan Date: Fri, 22 Nov 2024 22:19:42 +0800 Subject: [PATCH 88/88] fix: complie errors --- cmake/cmake.define | 4 +-- examples/c/CMakeLists.txt | 12 ++++---- source/client/test/CMakeLists.txt | 10 +++---- source/libs/catalog/test/CMakeLists.txt | 2 +- source/libs/executor/test/CMakeLists.txt | 2 +- source/libs/scheduler/test/CMakeLists.txt | 4 +-- tests/taosc_test/CMakeLists.txt | 2 +- utils/test/c/CMakeLists.txt | 36 +++++++++++------------ 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/cmake/cmake.define b/cmake/cmake.define index 5c766de142..eb95feaf82 100644 --- a/cmake/cmake.define +++ b/cmake/cmake.define @@ -101,9 +101,9 @@ SET(TAOS_LIB taos) SET(TAOS_LIB_STATIC taos_static) IF(${TD_WINDOWS}) - SET(TAOS_LIB_LINK taos_static) + SET(TAOS_LIB_PLATFORM_SPEC taos_static) ELSE() - SET(TAOS_LIB_LINK taos) + SET(TAOS_LIB_PLATFORM_SPEC taos) ENDIF() # build TSZ by default diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index 7427c482c4..e3c992f53f 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -42,27 +42,27 @@ IF(TD_LINUX) ) target_link_libraries(tmq - ${TAOS_LIB_LINK} + ${TAOS_LIB} ) target_link_libraries(stream_demo - ${TAOS_LIB_LINK} + ${TAOS_LIB} ) target_link_libraries(schemaless - ${TAOS_LIB_LINK} + ${TAOS_LIB} ) target_link_libraries(prepare - ${TAOS_LIB_LINK} + ${TAOS_LIB} ) target_link_libraries(demo - ${TAOS_LIB_LINK} + ${TAOS_LIB} ) target_link_libraries(asyncdemo - ${TAOS_LIB_LINK} + ${TAOS_LIB} ) SET_TARGET_PROPERTIES(tmq PROPERTIES OUTPUT_NAME tmq) diff --git a/source/client/test/CMakeLists.txt b/source/client/test/CMakeLists.txt index 94d36c549e..9393bfc449 100644 --- a/source/client/test/CMakeLists.txt +++ b/source/client/test/CMakeLists.txt @@ -8,31 +8,31 @@ AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} SOURCE_LIST) ADD_EXECUTABLE(clientTest clientTests.cpp) TARGET_LINK_LIBRARIES( clientTest - os util common transport parser catalog scheduler gtest ${TAOS_LIB_LINK} qcom executor function + os util common transport parser catalog scheduler gtest ${TAOS_LIB_STATIC} qcom executor function ) ADD_EXECUTABLE(tmqTest tmqTest.cpp) TARGET_LINK_LIBRARIES( tmqTest - PUBLIC os util common transport parser catalog scheduler function gtest ${TAOS_LIB_LINK} qcom + PUBLIC os util common transport parser catalog scheduler function gtest ${TAOS_LIB_STATIC} qcom ) ADD_EXECUTABLE(smlTest smlTest.cpp) TARGET_LINK_LIBRARIES( smlTest - PUBLIC os util common transport parser catalog scheduler function gtest ${TAOS_LIB_LINK} qcom geometry + PUBLIC os util common transport parser catalog scheduler function gtest ${TAOS_LIB_STATIC} qcom geometry ) #ADD_EXECUTABLE(clientMonitorTest clientMonitorTests.cpp) #TARGET_LINK_LIBRARIES( # clientMonitorTest -# PUBLIC os util common transport monitor parser catalog scheduler function gtest ${TAOS_LIB_LINK} qcom executor +# PUBLIC os util common transport monitor parser catalog scheduler function gtest ${TAOS_LIB_STATIC} qcom executor #) ADD_EXECUTABLE(userOperTest ../../../tests/script/api/passwdTest.c) TARGET_LINK_LIBRARIES( userOperTest - PUBLIC os util common transport parser catalog scheduler function gtest ${TAOS_LIB_LINK} qcom geometry + PUBLIC ${TAOS_LIB} ) TARGET_INCLUDE_DIRECTORIES( diff --git a/source/libs/catalog/test/CMakeLists.txt b/source/libs/catalog/test/CMakeLists.txt index 97c088e381..f23a6beaee 100644 --- a/source/libs/catalog/test/CMakeLists.txt +++ b/source/libs/catalog/test/CMakeLists.txt @@ -9,7 +9,7 @@ IF(NOT TD_DARWIN) ADD_EXECUTABLE(catalogTest ${SOURCE_LIST}) TARGET_LINK_LIBRARIES( catalogTest - PUBLIC os util common nodes catalog transport gtest qcom ${TAOS_LIB_LINK} + PUBLIC os util common nodes catalog transport gtest qcom ${TAOS_LIB_STATIC} ) TARGET_INCLUDE_DIRECTORIES( diff --git a/source/libs/executor/test/CMakeLists.txt b/source/libs/executor/test/CMakeLists.txt index da6aafc0f7..cb1f951c94 100644 --- a/source/libs/executor/test/CMakeLists.txt +++ b/source/libs/executor/test/CMakeLists.txt @@ -9,7 +9,7 @@ MESSAGE(STATUS "build parser unit test") # ADD_EXECUTABLE(executorTest ${SOURCE_LIST}) # TARGET_LINK_LIBRARIES( # executorTest -# PRIVATE os util common transport gtest ${TAOS_LIB_LINK} qcom executor function planner scalar nodes vnode +# PRIVATE os util common transport gtest ${TAOS_LIB_STATIC} qcom executor function planner scalar nodes vnode # ) # # TARGET_INCLUDE_DIRECTORIES( diff --git a/source/libs/scheduler/test/CMakeLists.txt b/source/libs/scheduler/test/CMakeLists.txt index 63e5367475..d9572e8dec 100644 --- a/source/libs/scheduler/test/CMakeLists.txt +++ b/source/libs/scheduler/test/CMakeLists.txt @@ -11,12 +11,12 @@ IF(NOT TD_DARWIN) IF (TD_GRANT) TARGET_LINK_LIBRARIES( schedulerTest - PUBLIC os util common catalog transport gtest qcom ${TAOS_LIB_LINK} planner scheduler grant + PUBLIC os util common catalog transport gtest qcom ${TAOS_LIB_STATIC} planner scheduler grant ) ELSE () TARGET_LINK_LIBRARIES( schedulerTest - PUBLIC os util common catalog transport gtest qcom ${TAOS_LIB_LINK} planner scheduler + PUBLIC os util common catalog transport gtest qcom ${TAOS_LIB_STATIC} planner scheduler ) ENDIF() diff --git a/tests/taosc_test/CMakeLists.txt b/tests/taosc_test/CMakeLists.txt index e78d23f3fe..45c14f84bf 100644 --- a/tests/taosc_test/CMakeLists.txt +++ b/tests/taosc_test/CMakeLists.txt @@ -16,7 +16,7 @@ aux_source_directory(src OS_SRC) # taoscTest add_executable(taoscTest "taoscTest.cpp") -target_link_libraries(taoscTest ${TAOS_LIB_LINK} os gtest_main) +target_link_libraries(taoscTest ${TAOS_LIB} os gtest_main) target_include_directories( taoscTest PUBLIC "${TD_SOURCE_DIR}/include/os" diff --git a/utils/test/c/CMakeLists.txt b/utils/test/c/CMakeLists.txt index 1593b528ed..7054eb218f 100644 --- a/utils/test/c/CMakeLists.txt +++ b/utils/test/c/CMakeLists.txt @@ -1,5 +1,5 @@ add_executable(tmq_demo tmqDemo.c) -add_dependencies(tmq_demo ${TAOS_LIB_LINK}) +add_dependencies(tmq_demo ${TAOS_LIB}) add_executable(tmq_sim tmqSim.c) add_executable(create_table createTable.c) add_executable(tmq_taosx_ci tmq_taosx_ci.c) @@ -22,7 +22,7 @@ endif(${TD_LINUX}) target_link_libraries( tmq_offset - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os @@ -30,7 +30,7 @@ target_link_libraries( target_link_libraries( tmq_multi_thread_test - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os @@ -38,56 +38,56 @@ target_link_libraries( target_link_libraries( create_table - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_demo - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_sim - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB_PLATFORM_SPEC} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_ts5466 - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_td32187 - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_td32526 - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_taosx_ci - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os ) target_link_libraries( tmq_offset_test - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os @@ -95,7 +95,7 @@ target_link_libraries( target_link_libraries( replay_test - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os @@ -103,7 +103,7 @@ target_link_libraries( target_link_libraries( write_raw_block_test - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os @@ -111,7 +111,7 @@ target_link_libraries( target_link_libraries( tmq_write_raw_test - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os @@ -119,7 +119,7 @@ target_link_libraries( target_link_libraries( sml_test - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os @@ -128,7 +128,7 @@ target_link_libraries( target_link_libraries( get_db_name_test - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os @@ -136,7 +136,7 @@ target_link_libraries( target_link_libraries( varbinary_test - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os @@ -145,7 +145,7 @@ target_link_libraries( if(${TD_LINUX}) target_link_libraries( tsz_test - PUBLIC ${TAOS_LIB_LINK} + PUBLIC ${TAOS_LIB} PUBLIC util PUBLIC common PUBLIC os