From 89ff78d99c615c6bbd7129f3f5802b661e17274d Mon Sep 17 00:00:00 2001 From: Ganlin Zhao Date: Wed, 16 Aug 2023 17:32:08 +0800 Subject: [PATCH 01/33] fix: fix non-root users cannot create log files even if they have write permission of the directory --- source/os/src/osDir.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/os/src/osDir.c b/source/os/src/osDir.c index 3d63da7ba3..7e4058d508 100644 --- a/source/os/src/osDir.c +++ b/source/os/src/osDir.c @@ -206,7 +206,11 @@ int32_t taosMulModeMkDir(const char *dirname, int mode) { #endif if (taosDirExist(temp)) { - return chmod(temp, mode); + if (taosCheckAccessFile(temp, TD_FILE_ACCESS_WRITE_OK)) { + return code; + } else { + return chmod(temp, mode); + } } if (strncmp(temp, TD_DIRSEP, 1) == 0) { From 8fc8aad1b5b8b5ced999d94f42c8b7a5db564e69 Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Wed, 23 Aug 2023 16:44:29 +0800 Subject: [PATCH 02/33] fix:[TD-258900]modify tmq trans conflict to db level --- source/dnode/mnode/impl/src/mndConsumer.c | 56 +------- source/dnode/mnode/impl/src/mndSubscribe.c | 17 +-- source/dnode/mnode/impl/src/mndTopic.c | 152 +++++++++++---------- 3 files changed, 87 insertions(+), 138 deletions(-) diff --git a/source/dnode/mnode/impl/src/mndConsumer.c b/source/dnode/mnode/impl/src/mndConsumer.c index 82492f930e..14344b52e7 100644 --- a/source/dnode/mnode/impl/src/mndConsumer.c +++ b/source/dnode/mnode/impl/src/mndConsumer.c @@ -37,7 +37,6 @@ static const char *mndConsumerStatusName(int status); static int32_t mndConsumerActionInsert(SSdb *pSdb, SMqConsumerObj *pConsumer); static int32_t mndConsumerActionDelete(SSdb *pSdb, SMqConsumerObj *pConsumer); static int32_t mndConsumerActionUpdate(SSdb *pSdb, SMqConsumerObj *pOldConsumer, SMqConsumerObj *pNewConsumer); -static int32_t mndProcessConsumerMetaMsg(SRpcMsg *pMsg); static int32_t mndRetrieveConsumer(SRpcMsg *pReq, SShowObj *pShow, SSDataBlock *pBlock, int32_t rows); static void mndCancelGetNextConsumer(SMnode *pMnode, void *pIter); @@ -45,7 +44,6 @@ static int32_t mndProcessSubscribeReq(SRpcMsg *pMsg); static int32_t mndProcessAskEpReq(SRpcMsg *pMsg); static int32_t mndProcessMqHbReq(SRpcMsg *pMsg); static int32_t mndProcessMqTimerMsg(SRpcMsg *pMsg); -static int32_t mndProcessConsumerLostMsg(SRpcMsg *pMsg); static int32_t mndProcessConsumerClearMsg(SRpcMsg *pMsg); static int32_t mndProcessConsumerRecoverMsg(SRpcMsg *pMsg); @@ -64,7 +62,6 @@ int32_t mndInitConsumer(SMnode *pMnode) { mndSetMsgHandle(pMnode, TDMT_MND_TMQ_HB, mndProcessMqHbReq); mndSetMsgHandle(pMnode, TDMT_MND_TMQ_ASK_EP, mndProcessAskEpReq); mndSetMsgHandle(pMnode, TDMT_MND_TMQ_TIMER, mndProcessMqTimerMsg); -// mndSetMsgHandle(pMnode, TDMT_MND_TMQ_CONSUMER_LOST, mndProcessConsumerLostMsg); mndSetMsgHandle(pMnode, TDMT_MND_TMQ_CONSUMER_RECOVER, mndProcessConsumerRecoverMsg); mndSetMsgHandle(pMnode, TDMT_MND_TMQ_LOST_CONSUMER_CLEAR, mndProcessConsumerClearMsg); @@ -122,49 +119,6 @@ void mndRebCntDec() { } } -//static int32_t mndProcessConsumerLostMsg(SRpcMsg *pMsg) { -// SMnode *pMnode = pMsg->info.node; -// SMqConsumerLostMsg *pLostMsg = pMsg->pCont; -// SMqConsumerObj *pConsumer = mndAcquireConsumer(pMnode, pLostMsg->consumerId); -// if (pConsumer == NULL) { -// return 0; -// } -// -// mInfo("process consumer lost msg, consumer:0x%" PRIx64 " status:%d(%s)", pLostMsg->consumerId, pConsumer->status, -// mndConsumerStatusName(pConsumer->status)); -// -// if (pConsumer->status != MQ_CONSUMER_STATUS_READY) { -// mndReleaseConsumer(pMnode, pConsumer); -// return -1; -// } -// -// SMqConsumerObj *pConsumerNew = tNewSMqConsumerObj(pConsumer->consumerId, pConsumer->cgroup); -// pConsumerNew->updateType = CONSUMER_UPDATE_TIMER_LOST; -// -// mndReleaseConsumer(pMnode, pConsumer); -// -// STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_NOTHING, pMsg, "lost-csm"); -// if (pTrans == NULL) { -// goto FAIL; -// } -// -// if (mndSetConsumerCommitLogs(pMnode, pTrans, pConsumerNew) != 0) { -// goto FAIL; -// } -// -// if (mndTransPrepare(pMnode, pTrans) != 0) { -// goto FAIL; -// } -// -// tDeleteSMqConsumerObj(pConsumerNew, true); -// mndTransDrop(pTrans); -// return 0; -//FAIL: -// tDeleteSMqConsumerObj(pConsumerNew, true); -// mndTransDrop(pTrans); -// return -1; -//} - static int32_t mndProcessConsumerRecoverMsg(SRpcMsg *pMsg) { SMnode *pMnode = pMsg->info.node; SMqConsumerRecoverMsg *pRecoverMsg = pMsg->pCont; @@ -221,13 +175,7 @@ static int32_t mndProcessConsumerClearMsg(SRpcMsg *pMsg) { mInfo("consumer:0x%" PRIx64 " needs to be cleared, status %s", pClearMsg->consumerId, mndConsumerStatusName(pConsumer->status)); -// if (pConsumer->status != MQ_CONSUMER_STATUS_LOST) { -// mndReleaseConsumer(pMnode, pConsumer); -// return -1; -// } - SMqConsumerObj *pConsumerNew = tNewSMqConsumerObj(pConsumer->consumerId, pConsumer->cgroup); -// pConsumerNew->updateType = CONSUMER_UPDATE_TIMER_LOST; mndReleaseConsumer(pMnode, pConsumer); @@ -629,7 +577,7 @@ int32_t mndSetConsumerCommitLogs(SMnode *pMnode, STrans *pTrans, SMqConsumerObj return 0; } -static int32_t validateTopics(const SArray *pTopicList, SMnode *pMnode, const char *pUser) { +static int32_t validateTopics(STrans *pTrans, const SArray *pTopicList, SMnode *pMnode, const char *pUser) { int32_t numOfTopics = taosArrayGetSize(pTopicList); for (int32_t i = 0; i < numOfTopics; i++) { @@ -722,7 +670,6 @@ int32_t mndProcessSubscribeReq(SRpcMsg *pMsg) { if (mndSetConsumerCommitLogs(pMnode, pTrans, pConsumerNew) != 0) goto _over; if (mndTransPrepare(pMnode, pTrans) != 0) goto _over; - } else { int32_t status = atomic_load_32(&pExistedConsumer->status); @@ -802,7 +749,6 @@ _over: tDeleteSMqConsumerObj(pConsumerNew, true); - // TODO: replace with destroy subscribe msg taosArrayDestroyP(subscribe.topicNames, (FDelete)taosMemoryFree); return code; } diff --git a/source/dnode/mnode/impl/src/mndSubscribe.c b/source/dnode/mnode/impl/src/mndSubscribe.c index 53f22f6e60..9579a18fc4 100644 --- a/source/dnode/mnode/impl/src/mndSubscribe.c +++ b/source/dnode/mnode/impl/src/mndSubscribe.c @@ -553,7 +553,7 @@ static int32_t mndPersistRebResult(SMnode *pMnode, SRpcMsg *pMsg, const SMqRebOu } } - STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB_INSIDE, pMsg, "tmq-reb"); + STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB, pMsg, "tmq-reb"); if (pTrans == NULL) { nodesDestroyNode((SNode*)pPlan); return -1; @@ -1019,8 +1019,8 @@ int32_t mndGetGroupNumByTopic(SMnode *pMnode, const char *topicName) { if (pIter == NULL) break; - char topic[TSDB_TOPIC_FNAME_LEN]; - char cgroup[TSDB_CGROUP_LEN]; + char topic[TSDB_TOPIC_FNAME_LEN] = {0}; + char cgroup[TSDB_CGROUP_LEN] = {0}; mndSplitSubscribeKey(pSub->key, topic, cgroup, true); if (strcmp(topic, topicName) != 0) { sdbRelease(pSdb, pSub); @@ -1084,7 +1084,6 @@ int32_t mndDropSubByDB(SMnode *pMnode, STrans *pTrans, SDbObj *pDb) { } int32_t mndDropSubByTopic(SMnode *pMnode, STrans *pTrans, const char *topicName) { - int32_t code = -1; SSdb *pSdb = pMnode->pSdb; void *pIter = NULL; @@ -1093,8 +1092,8 @@ int32_t mndDropSubByTopic(SMnode *pMnode, STrans *pTrans, const char *topicName) pIter = sdbFetch(pSdb, SDB_SUBSCRIBE, pIter, (void **)&pSub); if (pIter == NULL) break; - char topic[TSDB_TOPIC_FNAME_LEN]; - char cgroup[TSDB_CGROUP_LEN]; + char topic[TSDB_TOPIC_FNAME_LEN] = {0}; + char cgroup[TSDB_CGROUP_LEN] = {0}; mndSplitSubscribeKey(pSub->key, topic, cgroup, true); if (strcmp(topic, topicName) != 0) { sdbRelease(pSdb, pSub); @@ -1132,15 +1131,13 @@ int32_t mndDropSubByTopic(SMnode *pMnode, STrans *pTrans, const char *topicName) if (mndSetDropSubRedoLogs(pMnode, pTrans, pSub) < 0) { sdbRelease(pSdb, pSub); sdbCancelFetch(pSdb, pIter); - goto END; + return -1; } sdbRelease(pSdb, pSub); } - code = 0; -END: - return code; + return 0; } static int32_t buildResult(SSDataBlock *pBlock, int32_t* numOfRows, int64_t consumerId, const char* topic, const char* cgroup, SArray* vgs, SArray *offsetRows){ diff --git a/source/dnode/mnode/impl/src/mndTopic.c b/source/dnode/mnode/impl/src/mndTopic.c index 621a80338d..e7e631fcae 100644 --- a/source/dnode/mnode/impl/src/mndTopic.c +++ b/source/dnode/mnode/impl/src/mndTopic.c @@ -381,14 +381,26 @@ static int32_t mndCreateTopic(SMnode *pMnode, SRpcMsg *pReq, SCMCreateTopicReq * int32_t code = -1; SNode *pAst = NULL; SQueryPlan *pPlan = NULL; - SMqTopicObj topicObj = {0}; + + pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB, pReq, "create-topic"); + if (pTrans == NULL) { + mError("topic:%s, failed to create since %s", pCreate->name, terrstr()); + goto _OUT; + } + + mndTransSetDbName(pTrans, pDb->name, NULL); + if (mndTransCheckConflict(pMnode, pTrans) != 0) { + goto _OUT; + } + mInfo("trans:%d to create topic:%s", pTrans->id, pCreate->name); + tstrncpy(topicObj.name, pCreate->name, TSDB_TOPIC_FNAME_LEN); tstrncpy(topicObj.db, pDb->name, TSDB_DB_FNAME_LEN); tstrncpy(topicObj.createUser, userName, TSDB_USER_LEN); if (mndCheckTopicPrivilege(pMnode, pReq->info.conn.user, MND_OPER_CREATE_TOPIC, &topicObj) != 0) { - return -1; + goto _OUT; } topicObj.createTime = taosGetTimestampMs(); @@ -469,18 +481,6 @@ static int32_t mndCreateTopic(SMnode *pMnode, SRpcMsg *pReq, SCMCreateTopicReq * /*topicObj.withTbName = 1;*/ /*topicObj.withSchema = 1;*/ - pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB_INSIDE, pReq, "create-topic"); - if (pTrans == NULL) { - mError("topic:%s, failed to create since %s", pCreate->name, terrstr()); - goto _OUT; - } - - mndTransSetDbName(pTrans, pDb->name, NULL); - if (mndTransCheckConflict(pMnode, pTrans) != 0) { - goto _OUT; - } - mInfo("trans:%d to create topic:%s", pTrans->id, pCreate->name); - SSdbRaw *pCommitRaw = mndTopicActionEncode(&topicObj); if (pCommitRaw == NULL || mndTransAppendCommitlog(pTrans, pCommitRaw) != 0) { mError("trans:%d, failed to append commit log since %s", pTrans->id, terrstr()); @@ -654,30 +654,55 @@ _OVER: } static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { - SMnode *pMnode = pReq->info.node; - SSdb *pSdb = pMnode->pSdb; + SMnode *pMnode = pReq->info.node; + SSdb *pSdb = pMnode->pSdb; SMDropTopicReq dropReq = {0}; + int32_t code = 0; + SMqTopicObj *pTopic = NULL; + STrans *pTrans = NULL; if (tDeserializeSMDropTopicReq(pReq->pCont, pReq->contLen, &dropReq) != 0) { terrno = TSDB_CODE_INVALID_MSG; - return -1; + code = -1; + goto end; } - SMqTopicObj *pTopic = mndAcquireTopic(pMnode, dropReq.name); + pTopic = mndAcquireTopic(pMnode, dropReq.name); if (pTopic == NULL) { if (dropReq.igNotExists) { mInfo("topic:%s, not exist, ignore not exist is set", dropReq.name); - return 0; + goto end; } else { terrno = TSDB_CODE_MND_TOPIC_NOT_EXIST; mError("topic:%s, failed to drop since %s", dropReq.name, terrstr()); - return -1; + code = -1; + goto end; } } - if (mndCheckTopicPrivilege(pMnode, pReq->info.conn.user, MND_OPER_DROP_TOPIC, pTopic) != 0) { - mndReleaseTopic(pMnode, pTopic); - return -1; + pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB, pReq, "drop-topic"); + if (pTrans == NULL) { + mError("topic:%s, failed to drop since %s", pTopic->name, terrstr()); + code = -1; + goto end; + } + + mndTransSetDbName(pTrans, pTopic->db, NULL); + code = mndTransCheckConflict(pMnode, pTrans); + if (code != 0) { + goto end; + } + + mInfo("trans:%d, used to drop topic:%s", pTrans->id, pTopic->name); + + code = mndCheckTopicPrivilege(pMnode, pReq->info.conn.user, MND_OPER_DROP_TOPIC, pTopic); + if (code != 0) { + goto end; + } + + code = mndCheckDbPrivilegeByName(pMnode, pReq->info.conn.user, MND_OPER_READ_DB, pTopic->db); + if (code != 0) { + goto end; } void *pIter = NULL; @@ -688,37 +713,41 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { break; } - if (pConsumer->status == MQ_CONSUMER_STATUS_LOST){ - mndDropConsumerFromSdb(pMnode, pConsumer->consumerId); - mndReleaseConsumer(pMnode, pConsumer); - continue; - } - + bool found = false; int32_t sz = taosArrayGetSize(pConsumer->assignedTopics); for (int32_t i = 0; i < sz; i++) { char *name = taosArrayGetP(pConsumer->assignedTopics, i); if (strcmp(name, pTopic->name) == 0) { - mndReleaseConsumer(pMnode, pConsumer); - mndReleaseTopic(pMnode, pTopic); - sdbCancelFetch(pSdb, pIter); - terrno = TSDB_CODE_MND_TOPIC_SUBSCRIBED; - mError("topic:%s, failed to drop since subscribed by consumer:0x%" PRIx64 ", in consumer group %s", - dropReq.name, pConsumer->consumerId, pConsumer->cgroup); - return -1; + found = true; + break; } } + if (found){ + if (pConsumer->status == MQ_CONSUMER_STATUS_LOST) { + mndDropConsumerFromSdb(pMnode, pConsumer->consumerId); + continue; + } + + mndReleaseConsumer(pMnode, pConsumer); + sdbCancelFetch(pSdb, pIter); + terrno = TSDB_CODE_MND_TOPIC_SUBSCRIBED; + mError("topic:%s, failed to drop since subscribed by consumer:0x%" PRIx64 ", in consumer group %s", + dropReq.name, pConsumer->consumerId, pConsumer->cgroup); + code = -1; + goto end; + } sz = taosArrayGetSize(pConsumer->rebNewTopics); for (int32_t i = 0; i < sz; i++) { char *name = taosArrayGetP(pConsumer->rebNewTopics, i); if (strcmp(name, pTopic->name) == 0) { mndReleaseConsumer(pMnode, pConsumer); - mndReleaseTopic(pMnode, pTopic); sdbCancelFetch(pSdb, pIter); terrno = TSDB_CODE_MND_TOPIC_SUBSCRIBED; mError("topic:%s, failed to drop since subscribed by consumer:%" PRId64 ", in consumer group %s (reb new)", dropReq.name, pConsumer->consumerId, pConsumer->cgroup); - return -1; + code = -1; + goto end; } } @@ -727,45 +756,22 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { char *name = taosArrayGetP(pConsumer->rebRemovedTopics, i); if (strcmp(name, pTopic->name) == 0) { mndReleaseConsumer(pMnode, pConsumer); - mndReleaseTopic(pMnode, pTopic); sdbCancelFetch(pSdb, pIter); terrno = TSDB_CODE_MND_TOPIC_SUBSCRIBED; mError("topic:%s, failed to drop since subscribed by consumer:%" PRId64 ", in consumer group %s (reb remove)", dropReq.name, pConsumer->consumerId, pConsumer->cgroup); - return -1; + code = -1; + goto end; } } sdbRelease(pSdb, pConsumer); } - if (mndCheckDbPrivilegeByName(pMnode, pReq->info.conn.user, MND_OPER_READ_DB, pTopic->db) != 0) { - mndReleaseTopic(pMnode, pTopic); - return -1; - } - - STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB_INSIDE, pReq, "drop-topic"); - if (pTrans == NULL) { + code = mndDropSubByTopic(pMnode, pTrans, dropReq.name); + if ( code < 0) { mError("topic:%s, failed to drop since %s", pTopic->name, terrstr()); - mndReleaseTopic(pMnode, pTopic); - return -1; - } - - mndTransSetDbName(pTrans, pTopic->db, NULL); - if (mndTransCheckConflict(pMnode, pTrans) != 0) { - mndReleaseTopic(pMnode, pTopic); - mndTransDrop(pTrans); - return -1; - } - - mInfo("trans:%d, used to drop topic:%s", pTrans->id, pTopic->name); - - // TODO check if rebalancing - if (mndDropSubByTopic(pMnode, pTrans, dropReq.name) < 0) { - mError("topic:%s, failed to drop since %s", pTopic->name, terrstr()); - mndTransDrop(pTrans); - mndReleaseTopic(pMnode, pTopic); - return -1; + goto end; } if (pTopic->ntbUid != 0) { @@ -791,25 +797,25 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { action.pCont = buf; action.contLen = sizeof(SMsgHead) + TSDB_TOPIC_FNAME_LEN; action.msgType = TDMT_VND_TMQ_DEL_CHECKINFO; - if (mndTransAppendRedoAction(pTrans, &action) != 0) { + code = mndTransAppendRedoAction(pTrans, &action); + if (code != 0) { taosMemoryFree(buf); sdbRelease(pSdb, pVgroup); - mndReleaseTopic(pMnode, pTopic); sdbCancelFetch(pSdb, pIter); - mndTransDrop(pTrans); - return -1; + goto end; } sdbRelease(pSdb, pVgroup); } } - int32_t code = mndDropTopic(pMnode, pTrans, pReq, pTopic); + code = mndDropTopic(pMnode, pTrans, pReq, pTopic); + +end: mndReleaseTopic(pMnode, pTopic); mndTransDrop(pTrans); - if (code != 0) { mError("topic:%s, failed to drop since %s", dropReq.name, terrstr()); - return -1; + return code; } return TSDB_CODE_ACTION_IN_PROGRESS; From f19c1ea3fe1f9bfd711eabddb009ad6a1fed9103 Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Wed, 23 Aug 2023 16:46:33 +0800 Subject: [PATCH 03/33] fix:[TD-258900]modify tmq trans conflict to db level --- source/dnode/mnode/impl/src/mndConsumer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/dnode/mnode/impl/src/mndConsumer.c b/source/dnode/mnode/impl/src/mndConsumer.c index 14344b52e7..d5b7342768 100644 --- a/source/dnode/mnode/impl/src/mndConsumer.c +++ b/source/dnode/mnode/impl/src/mndConsumer.c @@ -577,7 +577,7 @@ int32_t mndSetConsumerCommitLogs(SMnode *pMnode, STrans *pTrans, SMqConsumerObj return 0; } -static int32_t validateTopics(STrans *pTrans, const SArray *pTopicList, SMnode *pMnode, const char *pUser) { +static int32_t validateTopics(const SArray *pTopicList, SMnode *pMnode, const char *pUser) { int32_t numOfTopics = taosArrayGetSize(pTopicList); for (int32_t i = 0; i < numOfTopics; i++) { From 4591f2474617f12cd2c6d6f63eef7f08fd13ca6f Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Wed, 23 Aug 2023 19:41:02 +0800 Subject: [PATCH 04/33] fix:memory leak --- source/dnode/mnode/impl/src/mndTopic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/dnode/mnode/impl/src/mndTopic.c b/source/dnode/mnode/impl/src/mndTopic.c index e7e631fcae..cb18c0bc65 100644 --- a/source/dnode/mnode/impl/src/mndTopic.c +++ b/source/dnode/mnode/impl/src/mndTopic.c @@ -725,6 +725,7 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { if (found){ if (pConsumer->status == MQ_CONSUMER_STATUS_LOST) { mndDropConsumerFromSdb(pMnode, pConsumer->consumerId); + mndReleaseConsumer(pMnode, pConsumer); continue; } From 962a0cd403ea08223dc10fefcb8f3a712296be0d Mon Sep 17 00:00:00 2001 From: Ganlin Zhao Date: Wed, 23 Aug 2023 23:34:02 +0800 Subject: [PATCH 05/33] fix --- include/os/osDir.h | 2 +- source/common/src/tglobal.c | 2 +- source/libs/stream/src/streamMeta.c | 4 ++-- source/libs/stream/src/streamState.c | 2 +- source/libs/tdb/src/db/tdbDb.c | 2 +- source/os/src/osDir.c | 7 +++++-- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/include/os/osDir.h b/include/os/osDir.h index 55c7a15764..2542f9830f 100644 --- a/include/os/osDir.h +++ b/include/os/osDir.h @@ -83,7 +83,7 @@ void taosRemoveDir(const char *dirname); bool taosDirExist(const char *dirname); int32_t taosMkDir(const char *dirname); int32_t taosMulMkDir(const char *dirname); -int32_t taosMulModeMkDir(const char *dirname, int mode); +int32_t taosMulModeMkDir(const char *dirname, int mode, bool createLogFile); void taosRemoveOldFiles(const char *dirname, int32_t keepDays); int32_t taosExpandDir(const char *dirname, char *outname, int32_t maxlen); int32_t taosRealPath(char *dirname, char *realPath, int32_t maxlen); diff --git a/source/common/src/tglobal.c b/source/common/src/tglobal.c index 5eeece9890..3edb70e63f 100644 --- a/source/common/src/tglobal.c +++ b/source/common/src/tglobal.c @@ -1434,7 +1434,7 @@ int32_t taosCreateLog(const char *logname, int32_t logFileNum, const char *cfgDi taosSetAllDebugFlag(cfgGetItem(pCfg, "debugFlag")->i32, false); - if (taosMulModeMkDir(tsLogDir, 0777) != 0) { + if (taosMulModeMkDir(tsLogDir, 0777, true) != 0) { terrno = TAOS_SYSTEM_ERROR(errno); printf("failed to create dir:%s since %s", tsLogDir, terrstr()); cfgCleanup(pCfg); diff --git a/source/libs/stream/src/streamMeta.c b/source/libs/stream/src/streamMeta.c index fe455c0190..61da055b25 100644 --- a/source/libs/stream/src/streamMeta.c +++ b/source/libs/stream/src/streamMeta.c @@ -52,7 +52,7 @@ SStreamMeta* streamMetaOpen(const char* path, void* ahandle, FTaskExpand expandF memset(streamPath, 0, len); sprintf(streamPath, "%s/%s", pMeta->path, "checkpoints"); - code = taosMulModeMkDir(streamPath, 0755); + code = taosMulModeMkDir(streamPath, 0755, false); if (code != 0) { terrno = TAOS_SYSTEM_ERROR(code); goto _err; @@ -90,7 +90,7 @@ SStreamMeta* streamMetaOpen(const char* path, void* ahandle, FTaskExpand expandF memset(streamPath, 0, len); sprintf(streamPath, "%s/%s", pMeta->path, "state"); - code = taosMulModeMkDir(streamPath, 0755); + code = taosMulModeMkDir(streamPath, 0755, false); if (code != 0) { terrno = TAOS_SYSTEM_ERROR(code); goto _err; diff --git a/source/libs/stream/src/streamState.c b/source/libs/stream/src/streamState.c index 5b42be182c..8694e5cf4c 100644 --- a/source/libs/stream/src/streamState.c +++ b/source/libs/stream/src/streamState.c @@ -169,7 +169,7 @@ SStreamState* streamStateOpen(char* path, void* pTask, bool specPath, int32_t sz sscanf(cfg, "%d\n%d\n", &szPage, &pages); } } else { - int32_t code = taosMulModeMkDir(statePath, 0755); + int32_t code = taosMulModeMkDir(statePath, 0755, false); if (code == 0) { pCfgFile = taosOpenFile(cfgPath, TD_FILE_WRITE | TD_FILE_CREATE); sprintf(cfg, "%d\n%d\n", szPage, pages); diff --git a/source/libs/tdb/src/db/tdbDb.c b/source/libs/tdb/src/db/tdbDb.c index 4f595d8d4a..81b306e65d 100644 --- a/source/libs/tdb/src/db/tdbDb.c +++ b/source/libs/tdb/src/db/tdbDb.c @@ -62,7 +62,7 @@ int32_t tdbOpen(const char *dbname, int32_t szPage, int32_t pages, TDB **ppDb, i } memset(pDb->pgrHash, 0, tsize); - ret = taosMulModeMkDir(dbname, 0755); + ret = taosMulModeMkDir(dbname, 0755, false); if (ret < 0) { return -1; } diff --git a/source/os/src/osDir.c b/source/os/src/osDir.c index 7e4058d508..e9f8c7f7e6 100644 --- a/source/os/src/osDir.c +++ b/source/os/src/osDir.c @@ -193,7 +193,7 @@ int32_t taosMulMkDir(const char *dirname) { return code; } -int32_t taosMulModeMkDir(const char *dirname, int mode) { +int32_t taosMulModeMkDir(const char *dirname, int mode, bool createLogFile) { if (dirname == NULL || strlen(dirname) >= TDDIRMAXLEN) return -1; char temp[TDDIRMAXLEN]; char *pos = temp; @@ -206,7 +206,10 @@ int32_t taosMulModeMkDir(const char *dirname, int mode) { #endif if (taosDirExist(temp)) { - if (taosCheckAccessFile(temp, TD_FILE_ACCESS_WRITE_OK)) { + if (createLogFile) { + if (!taosCheckAccessFile(temp, TD_FILE_ACCESS_EXIST_OK | TD_FILE_ACCESS_READ_OK | TD_FILE_ACCESS_WRITE_OK)) { + code = -1; + } return code; } else { return chmod(temp, mode); From 8f3956cc673ffca957616055b6929f69ff54c4d3 Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Wed, 23 Aug 2023 16:44:29 +0800 Subject: [PATCH 06/33] fix:[TD-258900]modify tmq trans conflict to db level --- source/dnode/mnode/impl/src/mndConsumer.c | 56 +------- source/dnode/mnode/impl/src/mndSubscribe.c | 17 +-- source/dnode/mnode/impl/src/mndTopic.c | 152 +++++++++++---------- 3 files changed, 87 insertions(+), 138 deletions(-) diff --git a/source/dnode/mnode/impl/src/mndConsumer.c b/source/dnode/mnode/impl/src/mndConsumer.c index 82492f930e..14344b52e7 100644 --- a/source/dnode/mnode/impl/src/mndConsumer.c +++ b/source/dnode/mnode/impl/src/mndConsumer.c @@ -37,7 +37,6 @@ static const char *mndConsumerStatusName(int status); static int32_t mndConsumerActionInsert(SSdb *pSdb, SMqConsumerObj *pConsumer); static int32_t mndConsumerActionDelete(SSdb *pSdb, SMqConsumerObj *pConsumer); static int32_t mndConsumerActionUpdate(SSdb *pSdb, SMqConsumerObj *pOldConsumer, SMqConsumerObj *pNewConsumer); -static int32_t mndProcessConsumerMetaMsg(SRpcMsg *pMsg); static int32_t mndRetrieveConsumer(SRpcMsg *pReq, SShowObj *pShow, SSDataBlock *pBlock, int32_t rows); static void mndCancelGetNextConsumer(SMnode *pMnode, void *pIter); @@ -45,7 +44,6 @@ static int32_t mndProcessSubscribeReq(SRpcMsg *pMsg); static int32_t mndProcessAskEpReq(SRpcMsg *pMsg); static int32_t mndProcessMqHbReq(SRpcMsg *pMsg); static int32_t mndProcessMqTimerMsg(SRpcMsg *pMsg); -static int32_t mndProcessConsumerLostMsg(SRpcMsg *pMsg); static int32_t mndProcessConsumerClearMsg(SRpcMsg *pMsg); static int32_t mndProcessConsumerRecoverMsg(SRpcMsg *pMsg); @@ -64,7 +62,6 @@ int32_t mndInitConsumer(SMnode *pMnode) { mndSetMsgHandle(pMnode, TDMT_MND_TMQ_HB, mndProcessMqHbReq); mndSetMsgHandle(pMnode, TDMT_MND_TMQ_ASK_EP, mndProcessAskEpReq); mndSetMsgHandle(pMnode, TDMT_MND_TMQ_TIMER, mndProcessMqTimerMsg); -// mndSetMsgHandle(pMnode, TDMT_MND_TMQ_CONSUMER_LOST, mndProcessConsumerLostMsg); mndSetMsgHandle(pMnode, TDMT_MND_TMQ_CONSUMER_RECOVER, mndProcessConsumerRecoverMsg); mndSetMsgHandle(pMnode, TDMT_MND_TMQ_LOST_CONSUMER_CLEAR, mndProcessConsumerClearMsg); @@ -122,49 +119,6 @@ void mndRebCntDec() { } } -//static int32_t mndProcessConsumerLostMsg(SRpcMsg *pMsg) { -// SMnode *pMnode = pMsg->info.node; -// SMqConsumerLostMsg *pLostMsg = pMsg->pCont; -// SMqConsumerObj *pConsumer = mndAcquireConsumer(pMnode, pLostMsg->consumerId); -// if (pConsumer == NULL) { -// return 0; -// } -// -// mInfo("process consumer lost msg, consumer:0x%" PRIx64 " status:%d(%s)", pLostMsg->consumerId, pConsumer->status, -// mndConsumerStatusName(pConsumer->status)); -// -// if (pConsumer->status != MQ_CONSUMER_STATUS_READY) { -// mndReleaseConsumer(pMnode, pConsumer); -// return -1; -// } -// -// SMqConsumerObj *pConsumerNew = tNewSMqConsumerObj(pConsumer->consumerId, pConsumer->cgroup); -// pConsumerNew->updateType = CONSUMER_UPDATE_TIMER_LOST; -// -// mndReleaseConsumer(pMnode, pConsumer); -// -// STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_NOTHING, pMsg, "lost-csm"); -// if (pTrans == NULL) { -// goto FAIL; -// } -// -// if (mndSetConsumerCommitLogs(pMnode, pTrans, pConsumerNew) != 0) { -// goto FAIL; -// } -// -// if (mndTransPrepare(pMnode, pTrans) != 0) { -// goto FAIL; -// } -// -// tDeleteSMqConsumerObj(pConsumerNew, true); -// mndTransDrop(pTrans); -// return 0; -//FAIL: -// tDeleteSMqConsumerObj(pConsumerNew, true); -// mndTransDrop(pTrans); -// return -1; -//} - static int32_t mndProcessConsumerRecoverMsg(SRpcMsg *pMsg) { SMnode *pMnode = pMsg->info.node; SMqConsumerRecoverMsg *pRecoverMsg = pMsg->pCont; @@ -221,13 +175,7 @@ static int32_t mndProcessConsumerClearMsg(SRpcMsg *pMsg) { mInfo("consumer:0x%" PRIx64 " needs to be cleared, status %s", pClearMsg->consumerId, mndConsumerStatusName(pConsumer->status)); -// if (pConsumer->status != MQ_CONSUMER_STATUS_LOST) { -// mndReleaseConsumer(pMnode, pConsumer); -// return -1; -// } - SMqConsumerObj *pConsumerNew = tNewSMqConsumerObj(pConsumer->consumerId, pConsumer->cgroup); -// pConsumerNew->updateType = CONSUMER_UPDATE_TIMER_LOST; mndReleaseConsumer(pMnode, pConsumer); @@ -629,7 +577,7 @@ int32_t mndSetConsumerCommitLogs(SMnode *pMnode, STrans *pTrans, SMqConsumerObj return 0; } -static int32_t validateTopics(const SArray *pTopicList, SMnode *pMnode, const char *pUser) { +static int32_t validateTopics(STrans *pTrans, const SArray *pTopicList, SMnode *pMnode, const char *pUser) { int32_t numOfTopics = taosArrayGetSize(pTopicList); for (int32_t i = 0; i < numOfTopics; i++) { @@ -722,7 +670,6 @@ int32_t mndProcessSubscribeReq(SRpcMsg *pMsg) { if (mndSetConsumerCommitLogs(pMnode, pTrans, pConsumerNew) != 0) goto _over; if (mndTransPrepare(pMnode, pTrans) != 0) goto _over; - } else { int32_t status = atomic_load_32(&pExistedConsumer->status); @@ -802,7 +749,6 @@ _over: tDeleteSMqConsumerObj(pConsumerNew, true); - // TODO: replace with destroy subscribe msg taosArrayDestroyP(subscribe.topicNames, (FDelete)taosMemoryFree); return code; } diff --git a/source/dnode/mnode/impl/src/mndSubscribe.c b/source/dnode/mnode/impl/src/mndSubscribe.c index 53f22f6e60..9579a18fc4 100644 --- a/source/dnode/mnode/impl/src/mndSubscribe.c +++ b/source/dnode/mnode/impl/src/mndSubscribe.c @@ -553,7 +553,7 @@ static int32_t mndPersistRebResult(SMnode *pMnode, SRpcMsg *pMsg, const SMqRebOu } } - STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB_INSIDE, pMsg, "tmq-reb"); + STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB, pMsg, "tmq-reb"); if (pTrans == NULL) { nodesDestroyNode((SNode*)pPlan); return -1; @@ -1019,8 +1019,8 @@ int32_t mndGetGroupNumByTopic(SMnode *pMnode, const char *topicName) { if (pIter == NULL) break; - char topic[TSDB_TOPIC_FNAME_LEN]; - char cgroup[TSDB_CGROUP_LEN]; + char topic[TSDB_TOPIC_FNAME_LEN] = {0}; + char cgroup[TSDB_CGROUP_LEN] = {0}; mndSplitSubscribeKey(pSub->key, topic, cgroup, true); if (strcmp(topic, topicName) != 0) { sdbRelease(pSdb, pSub); @@ -1084,7 +1084,6 @@ int32_t mndDropSubByDB(SMnode *pMnode, STrans *pTrans, SDbObj *pDb) { } int32_t mndDropSubByTopic(SMnode *pMnode, STrans *pTrans, const char *topicName) { - int32_t code = -1; SSdb *pSdb = pMnode->pSdb; void *pIter = NULL; @@ -1093,8 +1092,8 @@ int32_t mndDropSubByTopic(SMnode *pMnode, STrans *pTrans, const char *topicName) pIter = sdbFetch(pSdb, SDB_SUBSCRIBE, pIter, (void **)&pSub); if (pIter == NULL) break; - char topic[TSDB_TOPIC_FNAME_LEN]; - char cgroup[TSDB_CGROUP_LEN]; + char topic[TSDB_TOPIC_FNAME_LEN] = {0}; + char cgroup[TSDB_CGROUP_LEN] = {0}; mndSplitSubscribeKey(pSub->key, topic, cgroup, true); if (strcmp(topic, topicName) != 0) { sdbRelease(pSdb, pSub); @@ -1132,15 +1131,13 @@ int32_t mndDropSubByTopic(SMnode *pMnode, STrans *pTrans, const char *topicName) if (mndSetDropSubRedoLogs(pMnode, pTrans, pSub) < 0) { sdbRelease(pSdb, pSub); sdbCancelFetch(pSdb, pIter); - goto END; + return -1; } sdbRelease(pSdb, pSub); } - code = 0; -END: - return code; + return 0; } static int32_t buildResult(SSDataBlock *pBlock, int32_t* numOfRows, int64_t consumerId, const char* topic, const char* cgroup, SArray* vgs, SArray *offsetRows){ diff --git a/source/dnode/mnode/impl/src/mndTopic.c b/source/dnode/mnode/impl/src/mndTopic.c index 621a80338d..e7e631fcae 100644 --- a/source/dnode/mnode/impl/src/mndTopic.c +++ b/source/dnode/mnode/impl/src/mndTopic.c @@ -381,14 +381,26 @@ static int32_t mndCreateTopic(SMnode *pMnode, SRpcMsg *pReq, SCMCreateTopicReq * int32_t code = -1; SNode *pAst = NULL; SQueryPlan *pPlan = NULL; - SMqTopicObj topicObj = {0}; + + pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB, pReq, "create-topic"); + if (pTrans == NULL) { + mError("topic:%s, failed to create since %s", pCreate->name, terrstr()); + goto _OUT; + } + + mndTransSetDbName(pTrans, pDb->name, NULL); + if (mndTransCheckConflict(pMnode, pTrans) != 0) { + goto _OUT; + } + mInfo("trans:%d to create topic:%s", pTrans->id, pCreate->name); + tstrncpy(topicObj.name, pCreate->name, TSDB_TOPIC_FNAME_LEN); tstrncpy(topicObj.db, pDb->name, TSDB_DB_FNAME_LEN); tstrncpy(topicObj.createUser, userName, TSDB_USER_LEN); if (mndCheckTopicPrivilege(pMnode, pReq->info.conn.user, MND_OPER_CREATE_TOPIC, &topicObj) != 0) { - return -1; + goto _OUT; } topicObj.createTime = taosGetTimestampMs(); @@ -469,18 +481,6 @@ static int32_t mndCreateTopic(SMnode *pMnode, SRpcMsg *pReq, SCMCreateTopicReq * /*topicObj.withTbName = 1;*/ /*topicObj.withSchema = 1;*/ - pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB_INSIDE, pReq, "create-topic"); - if (pTrans == NULL) { - mError("topic:%s, failed to create since %s", pCreate->name, terrstr()); - goto _OUT; - } - - mndTransSetDbName(pTrans, pDb->name, NULL); - if (mndTransCheckConflict(pMnode, pTrans) != 0) { - goto _OUT; - } - mInfo("trans:%d to create topic:%s", pTrans->id, pCreate->name); - SSdbRaw *pCommitRaw = mndTopicActionEncode(&topicObj); if (pCommitRaw == NULL || mndTransAppendCommitlog(pTrans, pCommitRaw) != 0) { mError("trans:%d, failed to append commit log since %s", pTrans->id, terrstr()); @@ -654,30 +654,55 @@ _OVER: } static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { - SMnode *pMnode = pReq->info.node; - SSdb *pSdb = pMnode->pSdb; + SMnode *pMnode = pReq->info.node; + SSdb *pSdb = pMnode->pSdb; SMDropTopicReq dropReq = {0}; + int32_t code = 0; + SMqTopicObj *pTopic = NULL; + STrans *pTrans = NULL; if (tDeserializeSMDropTopicReq(pReq->pCont, pReq->contLen, &dropReq) != 0) { terrno = TSDB_CODE_INVALID_MSG; - return -1; + code = -1; + goto end; } - SMqTopicObj *pTopic = mndAcquireTopic(pMnode, dropReq.name); + pTopic = mndAcquireTopic(pMnode, dropReq.name); if (pTopic == NULL) { if (dropReq.igNotExists) { mInfo("topic:%s, not exist, ignore not exist is set", dropReq.name); - return 0; + goto end; } else { terrno = TSDB_CODE_MND_TOPIC_NOT_EXIST; mError("topic:%s, failed to drop since %s", dropReq.name, terrstr()); - return -1; + code = -1; + goto end; } } - if (mndCheckTopicPrivilege(pMnode, pReq->info.conn.user, MND_OPER_DROP_TOPIC, pTopic) != 0) { - mndReleaseTopic(pMnode, pTopic); - return -1; + pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB, pReq, "drop-topic"); + if (pTrans == NULL) { + mError("topic:%s, failed to drop since %s", pTopic->name, terrstr()); + code = -1; + goto end; + } + + mndTransSetDbName(pTrans, pTopic->db, NULL); + code = mndTransCheckConflict(pMnode, pTrans); + if (code != 0) { + goto end; + } + + mInfo("trans:%d, used to drop topic:%s", pTrans->id, pTopic->name); + + code = mndCheckTopicPrivilege(pMnode, pReq->info.conn.user, MND_OPER_DROP_TOPIC, pTopic); + if (code != 0) { + goto end; + } + + code = mndCheckDbPrivilegeByName(pMnode, pReq->info.conn.user, MND_OPER_READ_DB, pTopic->db); + if (code != 0) { + goto end; } void *pIter = NULL; @@ -688,37 +713,41 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { break; } - if (pConsumer->status == MQ_CONSUMER_STATUS_LOST){ - mndDropConsumerFromSdb(pMnode, pConsumer->consumerId); - mndReleaseConsumer(pMnode, pConsumer); - continue; - } - + bool found = false; int32_t sz = taosArrayGetSize(pConsumer->assignedTopics); for (int32_t i = 0; i < sz; i++) { char *name = taosArrayGetP(pConsumer->assignedTopics, i); if (strcmp(name, pTopic->name) == 0) { - mndReleaseConsumer(pMnode, pConsumer); - mndReleaseTopic(pMnode, pTopic); - sdbCancelFetch(pSdb, pIter); - terrno = TSDB_CODE_MND_TOPIC_SUBSCRIBED; - mError("topic:%s, failed to drop since subscribed by consumer:0x%" PRIx64 ", in consumer group %s", - dropReq.name, pConsumer->consumerId, pConsumer->cgroup); - return -1; + found = true; + break; } } + if (found){ + if (pConsumer->status == MQ_CONSUMER_STATUS_LOST) { + mndDropConsumerFromSdb(pMnode, pConsumer->consumerId); + continue; + } + + mndReleaseConsumer(pMnode, pConsumer); + sdbCancelFetch(pSdb, pIter); + terrno = TSDB_CODE_MND_TOPIC_SUBSCRIBED; + mError("topic:%s, failed to drop since subscribed by consumer:0x%" PRIx64 ", in consumer group %s", + dropReq.name, pConsumer->consumerId, pConsumer->cgroup); + code = -1; + goto end; + } sz = taosArrayGetSize(pConsumer->rebNewTopics); for (int32_t i = 0; i < sz; i++) { char *name = taosArrayGetP(pConsumer->rebNewTopics, i); if (strcmp(name, pTopic->name) == 0) { mndReleaseConsumer(pMnode, pConsumer); - mndReleaseTopic(pMnode, pTopic); sdbCancelFetch(pSdb, pIter); terrno = TSDB_CODE_MND_TOPIC_SUBSCRIBED; mError("topic:%s, failed to drop since subscribed by consumer:%" PRId64 ", in consumer group %s (reb new)", dropReq.name, pConsumer->consumerId, pConsumer->cgroup); - return -1; + code = -1; + goto end; } } @@ -727,45 +756,22 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { char *name = taosArrayGetP(pConsumer->rebRemovedTopics, i); if (strcmp(name, pTopic->name) == 0) { mndReleaseConsumer(pMnode, pConsumer); - mndReleaseTopic(pMnode, pTopic); sdbCancelFetch(pSdb, pIter); terrno = TSDB_CODE_MND_TOPIC_SUBSCRIBED; mError("topic:%s, failed to drop since subscribed by consumer:%" PRId64 ", in consumer group %s (reb remove)", dropReq.name, pConsumer->consumerId, pConsumer->cgroup); - return -1; + code = -1; + goto end; } } sdbRelease(pSdb, pConsumer); } - if (mndCheckDbPrivilegeByName(pMnode, pReq->info.conn.user, MND_OPER_READ_DB, pTopic->db) != 0) { - mndReleaseTopic(pMnode, pTopic); - return -1; - } - - STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB_INSIDE, pReq, "drop-topic"); - if (pTrans == NULL) { + code = mndDropSubByTopic(pMnode, pTrans, dropReq.name); + if ( code < 0) { mError("topic:%s, failed to drop since %s", pTopic->name, terrstr()); - mndReleaseTopic(pMnode, pTopic); - return -1; - } - - mndTransSetDbName(pTrans, pTopic->db, NULL); - if (mndTransCheckConflict(pMnode, pTrans) != 0) { - mndReleaseTopic(pMnode, pTopic); - mndTransDrop(pTrans); - return -1; - } - - mInfo("trans:%d, used to drop topic:%s", pTrans->id, pTopic->name); - - // TODO check if rebalancing - if (mndDropSubByTopic(pMnode, pTrans, dropReq.name) < 0) { - mError("topic:%s, failed to drop since %s", pTopic->name, terrstr()); - mndTransDrop(pTrans); - mndReleaseTopic(pMnode, pTopic); - return -1; + goto end; } if (pTopic->ntbUid != 0) { @@ -791,25 +797,25 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { action.pCont = buf; action.contLen = sizeof(SMsgHead) + TSDB_TOPIC_FNAME_LEN; action.msgType = TDMT_VND_TMQ_DEL_CHECKINFO; - if (mndTransAppendRedoAction(pTrans, &action) != 0) { + code = mndTransAppendRedoAction(pTrans, &action); + if (code != 0) { taosMemoryFree(buf); sdbRelease(pSdb, pVgroup); - mndReleaseTopic(pMnode, pTopic); sdbCancelFetch(pSdb, pIter); - mndTransDrop(pTrans); - return -1; + goto end; } sdbRelease(pSdb, pVgroup); } } - int32_t code = mndDropTopic(pMnode, pTrans, pReq, pTopic); + code = mndDropTopic(pMnode, pTrans, pReq, pTopic); + +end: mndReleaseTopic(pMnode, pTopic); mndTransDrop(pTrans); - if (code != 0) { mError("topic:%s, failed to drop since %s", dropReq.name, terrstr()); - return -1; + return code; } return TSDB_CODE_ACTION_IN_PROGRESS; From 775f0668c5c57f5497a068b3d8e357096ee371cf Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Wed, 23 Aug 2023 16:46:33 +0800 Subject: [PATCH 07/33] fix:[TD-258900]modify tmq trans conflict to db level --- source/dnode/mnode/impl/src/mndConsumer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/dnode/mnode/impl/src/mndConsumer.c b/source/dnode/mnode/impl/src/mndConsumer.c index 14344b52e7..d5b7342768 100644 --- a/source/dnode/mnode/impl/src/mndConsumer.c +++ b/source/dnode/mnode/impl/src/mndConsumer.c @@ -577,7 +577,7 @@ int32_t mndSetConsumerCommitLogs(SMnode *pMnode, STrans *pTrans, SMqConsumerObj return 0; } -static int32_t validateTopics(STrans *pTrans, const SArray *pTopicList, SMnode *pMnode, const char *pUser) { +static int32_t validateTopics(const SArray *pTopicList, SMnode *pMnode, const char *pUser) { int32_t numOfTopics = taosArrayGetSize(pTopicList); for (int32_t i = 0; i < numOfTopics; i++) { From be74370e988fa625f56dacf469e0e408ac3d0a64 Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Wed, 23 Aug 2023 19:41:02 +0800 Subject: [PATCH 08/33] fix:memory leak --- source/dnode/mnode/impl/src/mndTopic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/dnode/mnode/impl/src/mndTopic.c b/source/dnode/mnode/impl/src/mndTopic.c index e7e631fcae..cb18c0bc65 100644 --- a/source/dnode/mnode/impl/src/mndTopic.c +++ b/source/dnode/mnode/impl/src/mndTopic.c @@ -725,6 +725,7 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { if (found){ if (pConsumer->status == MQ_CONSUMER_STATUS_LOST) { mndDropConsumerFromSdb(pMnode, pConsumer->consumerId); + mndReleaseConsumer(pMnode, pConsumer); continue; } From 13d2d72bb61a0d504cb7738241dc802a1597275d Mon Sep 17 00:00:00 2001 From: Ganlin Zhao Date: Thu, 24 Aug 2023 14:56:14 +0800 Subject: [PATCH 09/33] add seperate function for createLogs --- include/os/osDir.h | 3 +- source/common/src/tglobal.c | 2 +- source/libs/stream/src/streamMeta.c | 4 +- source/libs/stream/src/streamState.c | 2 +- source/libs/tdb/src/db/tdbDb.c | 2 +- source/os/src/osDir.c | 71 +++++++++++++++++++++++++--- 6 files changed, 71 insertions(+), 13 deletions(-) diff --git a/include/os/osDir.h b/include/os/osDir.h index 2542f9830f..e722adcdcc 100644 --- a/include/os/osDir.h +++ b/include/os/osDir.h @@ -83,7 +83,8 @@ void taosRemoveDir(const char *dirname); bool taosDirExist(const char *dirname); int32_t taosMkDir(const char *dirname); int32_t taosMulMkDir(const char *dirname); -int32_t taosMulModeMkDir(const char *dirname, int mode, bool createLogFile); +int32_t taosMulModeMkDir(const char *dirname, int mode); +int32_t taosMulModeMkLogDir(const char *dirname, int mode); void taosRemoveOldFiles(const char *dirname, int32_t keepDays); int32_t taosExpandDir(const char *dirname, char *outname, int32_t maxlen); int32_t taosRealPath(char *dirname, char *realPath, int32_t maxlen); diff --git a/source/common/src/tglobal.c b/source/common/src/tglobal.c index 3edb70e63f..611b88bc9d 100644 --- a/source/common/src/tglobal.c +++ b/source/common/src/tglobal.c @@ -1434,7 +1434,7 @@ int32_t taosCreateLog(const char *logname, int32_t logFileNum, const char *cfgDi taosSetAllDebugFlag(cfgGetItem(pCfg, "debugFlag")->i32, false); - if (taosMulModeMkDir(tsLogDir, 0777, true) != 0) { + if (taosMulModeMkLogDir(tsLogDir, 0777) != 0) { terrno = TAOS_SYSTEM_ERROR(errno); printf("failed to create dir:%s since %s", tsLogDir, terrstr()); cfgCleanup(pCfg); diff --git a/source/libs/stream/src/streamMeta.c b/source/libs/stream/src/streamMeta.c index 61da055b25..fe455c0190 100644 --- a/source/libs/stream/src/streamMeta.c +++ b/source/libs/stream/src/streamMeta.c @@ -52,7 +52,7 @@ SStreamMeta* streamMetaOpen(const char* path, void* ahandle, FTaskExpand expandF memset(streamPath, 0, len); sprintf(streamPath, "%s/%s", pMeta->path, "checkpoints"); - code = taosMulModeMkDir(streamPath, 0755, false); + code = taosMulModeMkDir(streamPath, 0755); if (code != 0) { terrno = TAOS_SYSTEM_ERROR(code); goto _err; @@ -90,7 +90,7 @@ SStreamMeta* streamMetaOpen(const char* path, void* ahandle, FTaskExpand expandF memset(streamPath, 0, len); sprintf(streamPath, "%s/%s", pMeta->path, "state"); - code = taosMulModeMkDir(streamPath, 0755, false); + code = taosMulModeMkDir(streamPath, 0755); if (code != 0) { terrno = TAOS_SYSTEM_ERROR(code); goto _err; diff --git a/source/libs/stream/src/streamState.c b/source/libs/stream/src/streamState.c index 8694e5cf4c..5b42be182c 100644 --- a/source/libs/stream/src/streamState.c +++ b/source/libs/stream/src/streamState.c @@ -169,7 +169,7 @@ SStreamState* streamStateOpen(char* path, void* pTask, bool specPath, int32_t sz sscanf(cfg, "%d\n%d\n", &szPage, &pages); } } else { - int32_t code = taosMulModeMkDir(statePath, 0755, false); + int32_t code = taosMulModeMkDir(statePath, 0755); if (code == 0) { pCfgFile = taosOpenFile(cfgPath, TD_FILE_WRITE | TD_FILE_CREATE); sprintf(cfg, "%d\n%d\n", szPage, pages); diff --git a/source/libs/tdb/src/db/tdbDb.c b/source/libs/tdb/src/db/tdbDb.c index 81b306e65d..4f595d8d4a 100644 --- a/source/libs/tdb/src/db/tdbDb.c +++ b/source/libs/tdb/src/db/tdbDb.c @@ -62,7 +62,7 @@ int32_t tdbOpen(const char *dbname, int32_t szPage, int32_t pages, TDB **ppDb, i } memset(pDb->pgrHash, 0, tsize); - ret = taosMulModeMkDir(dbname, 0755, false); + ret = taosMulModeMkDir(dbname, 0755); if (ret < 0) { return -1; } diff --git a/source/os/src/osDir.c b/source/os/src/osDir.c index e9f8c7f7e6..dff0cf9886 100644 --- a/source/os/src/osDir.c +++ b/source/os/src/osDir.c @@ -193,7 +193,7 @@ int32_t taosMulMkDir(const char *dirname) { return code; } -int32_t taosMulModeMkDir(const char *dirname, int mode, bool createLogFile) { +int32_t taosMulModeMkDir(const char *dirname, int mode) { if (dirname == NULL || strlen(dirname) >= TDDIRMAXLEN) return -1; char temp[TDDIRMAXLEN]; char *pos = temp; @@ -206,16 +206,73 @@ int32_t taosMulModeMkDir(const char *dirname, int mode, bool createLogFile) { #endif if (taosDirExist(temp)) { - if (createLogFile) { - if (!taosCheckAccessFile(temp, TD_FILE_ACCESS_EXIST_OK | TD_FILE_ACCESS_READ_OK | TD_FILE_ACCESS_WRITE_OK)) { - code = -1; + return chmod(temp, mode); + } + + if (strncmp(temp, TD_DIRSEP, 1) == 0) { + pos += 1; + } else if (strncmp(temp, "." TD_DIRSEP, 2) == 0) { + pos += 2; + } + + for (; *pos != '\0'; pos++) { + if (*pos == TD_DIRSEP[0]) { + *pos = '\0'; +#ifdef WINDOWS + code = _mkdir(temp, mode); +#elif defined(DARWIN) + code = mkdir(dirname, 0777); +#else + code = mkdir(temp, mode); +#endif + if (code < 0 && errno != EEXIST) { + // terrno = TAOS_SYSTEM_ERROR(errno); + return code; } - return code; - } else { - return chmod(temp, mode); + *pos = TD_DIRSEP[0]; } } + if (*(pos - 1) != TD_DIRSEP[0]) { +#ifdef WINDOWS + code = _mkdir(temp, mode); +#elif defined(DARWIN) + code = mkdir(dirname, 0777); +#else + code = mkdir(temp, mode); +#endif + if (code < 0 && errno != EEXIST) { + // terrno = TAOS_SYSTEM_ERROR(errno); + return code; + } + } + + if (code < 0 && errno == EEXIST) { + return chmod(temp, mode); + } + + return chmod(temp, mode); +} + +int32_t taosMulModeMkLogDir(const char *dirname, int mode) { + if (dirname == NULL || strlen(dirname) >= TDDIRMAXLEN) return -1; + char temp[TDDIRMAXLEN]; + char *pos = temp; + int32_t code = 0; +#ifdef WINDOWS + taosRealPath(dirname, temp, sizeof(temp)); + if (temp[1] == ':') pos += 3; +#else + strcpy(temp, dirname); +#endif + + if (taosDirExist(temp)) { + if (!taosCheckAccessFile(temp, TD_FILE_ACCESS_EXIST_OK | TD_FILE_ACCESS_READ_OK | TD_FILE_ACCESS_WRITE_OK)) { + code = -1; + } + return code; + } + if (strncmp(temp, TD_DIRSEP, 1) == 0) { pos += 1; } else if (strncmp(temp, "." TD_DIRSEP, 2) == 0) { From 49216ffa54f211adafc0567602aeb4a668a67d9a Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Thu, 24 Aug 2023 16:32:15 +0800 Subject: [PATCH 10/33] fix:transaction in tmq --- source/dnode/mnode/impl/inc/mndDef.h | 2 + source/dnode/mnode/impl/src/mndConsumer.c | 57 ++++++++++++---------- source/dnode/mnode/impl/src/mndSubscribe.c | 32 +++++++----- source/dnode/mnode/impl/src/mndTopic.c | 16 ++++-- source/dnode/mnode/impl/src/mndTrans.c | 16 ++++++ source/dnode/vnode/src/tq/tq.c | 15 +----- 6 files changed, 80 insertions(+), 58 deletions(-) diff --git a/source/dnode/mnode/impl/inc/mndDef.h b/source/dnode/mnode/impl/inc/mndDef.h index c83a40c25d..4656e0555f 100644 --- a/source/dnode/mnode/impl/inc/mndDef.h +++ b/source/dnode/mnode/impl/inc/mndDef.h @@ -99,6 +99,8 @@ typedef enum { TRN_CONFLICT_GLOBAL = 1, TRN_CONFLICT_DB = 2, TRN_CONFLICT_DB_INSIDE = 3, + TRN_CONFLICT_TOPIC = 4, + TRN_CONFLICT_TOPIC_INSIDE = 5, } ETrnConflct; typedef enum { diff --git a/source/dnode/mnode/impl/src/mndConsumer.c b/source/dnode/mnode/impl/src/mndConsumer.c index d5b7342768..2e78d03884 100644 --- a/source/dnode/mnode/impl/src/mndConsumer.c +++ b/source/dnode/mnode/impl/src/mndConsumer.c @@ -119,6 +119,27 @@ void mndRebCntDec() { } } +static int32_t validateTopics(STrans *pTrans, const SArray *pTopicList, SMnode *pMnode) { + int32_t numOfTopics = taosArrayGetSize(pTopicList); + + for (int32_t i = 0; i < numOfTopics; i++) { + char *pOneTopic = taosArrayGetP(pTopicList, i); + SMqTopicObj *pTopic = mndAcquireTopic(pMnode, pOneTopic); + if (pTopic == NULL) { // terrno has been set by callee function + return -1; + } + + mndTransSetDbName(pTrans, pOneTopic, NULL); + if(mndTransCheckConflict(pMnode, pTrans) != 0){ + mndReleaseTopic(pMnode, pTopic); + return -1; + } + mndReleaseTopic(pMnode, pTopic); + } + + return 0; +} + static int32_t mndProcessConsumerRecoverMsg(SRpcMsg *pMsg) { SMnode *pMnode = pMsg->info.node; SMqConsumerRecoverMsg *pRecoverMsg = pMsg->pCont; @@ -142,10 +163,13 @@ static int32_t mndProcessConsumerRecoverMsg(SRpcMsg *pMsg) { mndReleaseConsumer(pMnode, pConsumer); - STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_RETRY, TRN_CONFLICT_NOTHING, pMsg, "recover-csm"); + STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_RETRY, TRN_CONFLICT_TOPIC, pMsg, "recover-csm"); if (pTrans == NULL) { goto FAIL; } + if(validateTopics(pTrans, pConsumer->assignedTopics, pMnode) != 0){ + goto FAIL; + } if (mndSetConsumerCommitLogs(pMnode, pTrans, pConsumerNew) != 0) goto FAIL; if (mndTransPrepare(pMnode, pTrans) != 0) goto FAIL; @@ -179,9 +203,11 @@ static int32_t mndProcessConsumerClearMsg(SRpcMsg *pMsg) { mndReleaseConsumer(pMnode, pConsumer); - STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_NOTHING, pMsg, "clear-csm"); + STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_TOPIC, pMsg, "clear-csm"); if (pTrans == NULL) goto FAIL; - + if (validateTopics(pTrans, pConsumer->assignedTopics, pMnode) != 0){ + goto FAIL; + } // this is the drop action, not the update action if (mndSetConsumerDropLogs(pMnode, pTrans, pConsumerNew) != 0) goto FAIL; if (mndTransPrepare(pMnode, pTrans) != 0) goto FAIL; @@ -577,27 +603,6 @@ int32_t mndSetConsumerCommitLogs(SMnode *pMnode, STrans *pTrans, SMqConsumerObj return 0; } -static int32_t validateTopics(const SArray *pTopicList, SMnode *pMnode, const char *pUser) { - int32_t numOfTopics = taosArrayGetSize(pTopicList); - - for (int32_t i = 0; i < numOfTopics; i++) { - char *pOneTopic = taosArrayGetP(pTopicList, i); - SMqTopicObj *pTopic = mndAcquireTopic(pMnode, pOneTopic); - if (pTopic == NULL) { // terrno has been set by callee function - return -1; - } - - if (mndCheckTopicPrivilege(pMnode, pUser, MND_OPER_SUBSCRIBE, pTopic) != 0) { - mndReleaseTopic(pMnode, pTopic); - return -1; - } - - mndReleaseTopic(pMnode, pTopic); - } - - return 0; -} - static void *topicNameDup(void *p) { return taosStrdup((char *)p); } static void freeItem(void *param) { @@ -636,12 +641,12 @@ int32_t mndProcessSubscribeReq(SRpcMsg *pMsg) { } // check topic existence - pTrans = mndTransCreate(pMnode, TRN_POLICY_RETRY, TRN_CONFLICT_NOTHING, pMsg, "subscribe"); + pTrans = mndTransCreate(pMnode, TRN_POLICY_RETRY, TRN_CONFLICT_TOPIC, pMsg, "subscribe"); if (pTrans == NULL) { goto _over; } - code = validateTopics(pTopicList, pMnode, pMsg->info.conn.user); + code = validateTopics(pTrans, pTopicList, pMnode); if (code != TSDB_CODE_SUCCESS) { goto _over; } diff --git a/source/dnode/mnode/impl/src/mndSubscribe.c b/source/dnode/mnode/impl/src/mndSubscribe.c index 9579a18fc4..b4145ae8d0 100644 --- a/source/dnode/mnode/impl/src/mndSubscribe.c +++ b/source/dnode/mnode/impl/src/mndSubscribe.c @@ -553,13 +553,17 @@ static int32_t mndPersistRebResult(SMnode *pMnode, SRpcMsg *pMsg, const SMqRebOu } } - STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB, pMsg, "tmq-reb"); + char topic[TSDB_TOPIC_FNAME_LEN] = {0}; + char cgroup[TSDB_CGROUP_LEN] = {0}; + mndSplitSubscribeKey(pOutput->pSub->key, topic, cgroup, true); + + STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_TOPIC_INSIDE, pMsg, "tmq-reb"); if (pTrans == NULL) { nodesDestroyNode((SNode*)pPlan); return -1; } - mndTransSetDbName(pTrans, pOutput->pSub->dbName, NULL); + mndTransSetDbName(pTrans, topic, cgroup); if (mndTransCheckConflict(pMnode, pTrans) != 0) { mndTransDrop(pTrans); nodesDestroyNode((SNode*)pPlan); @@ -587,10 +591,6 @@ static int32_t mndPersistRebResult(SMnode *pMnode, SRpcMsg *pMsg, const SMqRebOu return -1; } - char topic[TSDB_TOPIC_FNAME_LEN] = {0}; - char cgroup[TSDB_CGROUP_LEN] = {0}; - mndSplitSubscribeKey(pOutput->pSub->key, topic, cgroup, true); - // 3. commit log: consumer to update status and epoch // 3.1 set touched consumer int32_t consumerNum = taosArrayGetSize(pOutput->modifyConsumers); @@ -802,6 +802,19 @@ static int32_t mndProcessDropCgroupReq(SRpcMsg *pMsg) { goto end; } + pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_TOPIC_INSIDE, pMsg, "drop-cgroup"); + if (pTrans == NULL) { + mError("cgroup: %s on topic:%s, failed to drop since %s", dropReq.cgroup, dropReq.topic, terrstr()); + code = -1; + goto end; + } + + mndTransSetDbName(pTrans, dropReq.topic, dropReq.cgroup); + code = mndTransCheckConflict(pMnode, pTrans); + if (code != 0) { + goto end; + } + void *pIter = NULL; SMqConsumerObj *pConsumer; while (1) { @@ -816,13 +829,6 @@ static int32_t mndProcessDropCgroupReq(SRpcMsg *pMsg) { sdbRelease(pMnode->pSdb, pConsumer); } - pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_NOTHING, pMsg, "drop-cgroup"); - if (pTrans == NULL) { - mError("cgroup: %s on topic:%s, failed to drop since %s", dropReq.cgroup, dropReq.topic, terrstr()); - code = -1; - goto end; - } - mInfo("trans:%d, used to drop cgroup:%s on topic %s", pTrans->id, dropReq.cgroup, dropReq.topic); if (mndSetDropSubCommitLogs(pMnode, pTrans, pSub) < 0) { diff --git a/source/dnode/mnode/impl/src/mndTopic.c b/source/dnode/mnode/impl/src/mndTopic.c index cb18c0bc65..13c9b7d176 100644 --- a/source/dnode/mnode/impl/src/mndTopic.c +++ b/source/dnode/mnode/impl/src/mndTopic.c @@ -383,14 +383,15 @@ static int32_t mndCreateTopic(SMnode *pMnode, SRpcMsg *pReq, SCMCreateTopicReq * SQueryPlan *pPlan = NULL; SMqTopicObj topicObj = {0}; - pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB, pReq, "create-topic"); + pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_TOPIC, pReq, "create-topic"); if (pTrans == NULL) { mError("topic:%s, failed to create since %s", pCreate->name, terrstr()); goto _OUT; } - mndTransSetDbName(pTrans, pDb->name, NULL); - if (mndTransCheckConflict(pMnode, pTrans) != 0) { + mndTransSetDbName(pTrans, pCreate->name, NULL); + code = mndTransCheckConflict(pMnode, pTrans); + if (code != 0) { goto _OUT; } mInfo("trans:%d to create topic:%s", pTrans->id, pCreate->name); @@ -661,6 +662,11 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { SMqTopicObj *pTopic = NULL; STrans *pTrans = NULL; + if (!mndRebTryStart()) { + mInfo("mq rebalance already in progress, do nothing"); + return 0; + } + if (tDeserializeSMDropTopicReq(pReq->pCont, pReq->contLen, &dropReq) != 0) { terrno = TSDB_CODE_INVALID_MSG; code = -1; @@ -680,14 +686,14 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { } } - pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_DB, pReq, "drop-topic"); + pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_TOPIC, pReq, "drop-topic"); if (pTrans == NULL) { mError("topic:%s, failed to drop since %s", pTopic->name, terrstr()); code = -1; goto end; } - mndTransSetDbName(pTrans, pTopic->db, NULL); + mndTransSetDbName(pTrans, pTopic->name, NULL); code = mndTransCheckConflict(pMnode, pTrans); if (code != 0) { goto end; diff --git a/source/dnode/mnode/impl/src/mndTrans.c b/source/dnode/mnode/impl/src/mndTrans.c index 7ebaf6dda5..849b14255c 100644 --- a/source/dnode/mnode/impl/src/mndTrans.c +++ b/source/dnode/mnode/impl/src/mndTrans.c @@ -792,6 +792,22 @@ static bool mndCheckTransConflict(SMnode *pMnode, STrans *pNew) { } } + if (pNew->conflict == TRN_CONFLICT_TOPIC) { + if (pTrans->conflict == TRN_CONFLICT_GLOBAL) conflict = true; + if (pTrans->conflict == TRN_CONFLICT_TOPIC || pTrans->conflict == TRN_CONFLICT_TOPIC_INSIDE) { + if (strcasecmp(pNew->dbname, pTrans->dbname) == 0 ) conflict = true; + } + } + if (pNew->conflict == TRN_CONFLICT_TOPIC_INSIDE) { + if (pTrans->conflict == TRN_CONFLICT_GLOBAL) conflict = true; + if (pTrans->conflict == TRN_CONFLICT_TOPIC ) { + if (strcasecmp(pNew->dbname, pTrans->dbname) == 0 ) conflict = true; + } + if (pTrans->conflict == TRN_CONFLICT_TOPIC_INSIDE) { + if (strcasecmp(pNew->dbname, pTrans->dbname) == 0 && strcasecmp(pNew->stbname, pTrans->stbname) == 0) conflict = true; + } + } + if (conflict) { mError("trans:%d, db:%s stb:%s type:%d, can't execute since conflict with trans:%d db:%s stb:%s type:%d", pNew->id, pNew->dbname, pNew->stbname, pNew->conflict, pTrans->id, pTrans->dbname, pTrans->stbname, diff --git a/source/dnode/vnode/src/tq/tq.c b/source/dnode/vnode/src/tq/tq.c index 815e9647b5..3396803f08 100644 --- a/source/dnode/vnode/src/tq/tq.c +++ b/source/dnode/vnode/src/tq/tq.c @@ -879,20 +879,7 @@ int32_t tqProcessSubscribeReq(STQ* pTq, int64_t sversion, char* msg, int32_t msg } else { tqInfo("vgId:%d switch consumer from Id:0x%" PRIx64 " to Id:0x%" PRIx64, req.vgId, pHandle->consumerId, req.newConsumerId); atomic_store_64(&pHandle->consumerId, req.newConsumerId); - // atomic_add_fetch_32(&pHandle->epoch, 1); - - // kill executing task - // if(tqIsHandleExec(pHandle)) { - // qTaskInfo_t pTaskInfo = pHandle->execHandle.task; - // if (pTaskInfo != NULL) { - // qKillTask(pTaskInfo, TSDB_CODE_SUCCESS); - // } - - // if (pHandle->execHandle.subType == TOPIC_SUB_TYPE__COLUMN) { - // qStreamCloseTsdbReader(pTaskInfo); - // } - // } - // remove if it has been register in the push manager, and return one empty block to consumer + atomic_store_32(&pHandle->epoch, 0); tqUnregisterPushHandle(pTq, pHandle); ret = tqMetaSaveHandle(pTq, req.subKey, pHandle); } From cca00406a55965c86aea61d2d3061b0a58265e67 Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Thu, 24 Aug 2023 18:16:57 +0800 Subject: [PATCH 11/33] fix:drop topic error if topic not exist --- source/dnode/mnode/impl/src/mndTopic.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/dnode/mnode/impl/src/mndTopic.c b/source/dnode/mnode/impl/src/mndTopic.c index a3f1a100d0..eacdaa1665 100644 --- a/source/dnode/mnode/impl/src/mndTopic.c +++ b/source/dnode/mnode/impl/src/mndTopic.c @@ -664,20 +664,18 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { if (tDeserializeSMDropTopicReq(pReq->pCont, pReq->contLen, &dropReq) != 0) { terrno = TSDB_CODE_INVALID_MSG; - code = -1; - goto end; + return -1; } pTopic = mndAcquireTopic(pMnode, dropReq.name); if (pTopic == NULL) { if (dropReq.igNotExists) { mInfo("topic:%s, not exist, ignore not exist is set", dropReq.name); - goto end; + return 0; } else { terrno = TSDB_CODE_MND_TOPIC_NOT_EXIST; mError("topic:%s, failed to drop since %s", dropReq.name, terrstr()); - code = -1; - goto end; + return -1; } } From af130cc92f6ace15646fb36b45a5df650a8aebc3 Mon Sep 17 00:00:00 2001 From: Ganlin Zhao Date: Fri, 25 Aug 2023 09:55:50 +0800 Subject: [PATCH 12/33] Revert "add seperate function for createLogs" This reverts commit 13d2d72bb61a0d504cb7738241dc802a1597275d. --- include/os/osDir.h | 3 +- source/common/src/tglobal.c | 2 +- source/libs/stream/src/streamMeta.c | 4 +- source/libs/stream/src/streamState.c | 2 +- source/libs/tdb/src/db/tdbDb.c | 2 +- source/os/src/osDir.c | 69 +++------------------------- 6 files changed, 12 insertions(+), 70 deletions(-) diff --git a/include/os/osDir.h b/include/os/osDir.h index e722adcdcc..2542f9830f 100644 --- a/include/os/osDir.h +++ b/include/os/osDir.h @@ -83,8 +83,7 @@ void taosRemoveDir(const char *dirname); bool taosDirExist(const char *dirname); int32_t taosMkDir(const char *dirname); int32_t taosMulMkDir(const char *dirname); -int32_t taosMulModeMkDir(const char *dirname, int mode); -int32_t taosMulModeMkLogDir(const char *dirname, int mode); +int32_t taosMulModeMkDir(const char *dirname, int mode, bool createLogFile); void taosRemoveOldFiles(const char *dirname, int32_t keepDays); int32_t taosExpandDir(const char *dirname, char *outname, int32_t maxlen); int32_t taosRealPath(char *dirname, char *realPath, int32_t maxlen); diff --git a/source/common/src/tglobal.c b/source/common/src/tglobal.c index 611b88bc9d..3edb70e63f 100644 --- a/source/common/src/tglobal.c +++ b/source/common/src/tglobal.c @@ -1434,7 +1434,7 @@ int32_t taosCreateLog(const char *logname, int32_t logFileNum, const char *cfgDi taosSetAllDebugFlag(cfgGetItem(pCfg, "debugFlag")->i32, false); - if (taosMulModeMkLogDir(tsLogDir, 0777) != 0) { + if (taosMulModeMkDir(tsLogDir, 0777, true) != 0) { terrno = TAOS_SYSTEM_ERROR(errno); printf("failed to create dir:%s since %s", tsLogDir, terrstr()); cfgCleanup(pCfg); diff --git a/source/libs/stream/src/streamMeta.c b/source/libs/stream/src/streamMeta.c index fe455c0190..61da055b25 100644 --- a/source/libs/stream/src/streamMeta.c +++ b/source/libs/stream/src/streamMeta.c @@ -52,7 +52,7 @@ SStreamMeta* streamMetaOpen(const char* path, void* ahandle, FTaskExpand expandF memset(streamPath, 0, len); sprintf(streamPath, "%s/%s", pMeta->path, "checkpoints"); - code = taosMulModeMkDir(streamPath, 0755); + code = taosMulModeMkDir(streamPath, 0755, false); if (code != 0) { terrno = TAOS_SYSTEM_ERROR(code); goto _err; @@ -90,7 +90,7 @@ SStreamMeta* streamMetaOpen(const char* path, void* ahandle, FTaskExpand expandF memset(streamPath, 0, len); sprintf(streamPath, "%s/%s", pMeta->path, "state"); - code = taosMulModeMkDir(streamPath, 0755); + code = taosMulModeMkDir(streamPath, 0755, false); if (code != 0) { terrno = TAOS_SYSTEM_ERROR(code); goto _err; diff --git a/source/libs/stream/src/streamState.c b/source/libs/stream/src/streamState.c index 5b42be182c..8694e5cf4c 100644 --- a/source/libs/stream/src/streamState.c +++ b/source/libs/stream/src/streamState.c @@ -169,7 +169,7 @@ SStreamState* streamStateOpen(char* path, void* pTask, bool specPath, int32_t sz sscanf(cfg, "%d\n%d\n", &szPage, &pages); } } else { - int32_t code = taosMulModeMkDir(statePath, 0755); + int32_t code = taosMulModeMkDir(statePath, 0755, false); if (code == 0) { pCfgFile = taosOpenFile(cfgPath, TD_FILE_WRITE | TD_FILE_CREATE); sprintf(cfg, "%d\n%d\n", szPage, pages); diff --git a/source/libs/tdb/src/db/tdbDb.c b/source/libs/tdb/src/db/tdbDb.c index 4f595d8d4a..81b306e65d 100644 --- a/source/libs/tdb/src/db/tdbDb.c +++ b/source/libs/tdb/src/db/tdbDb.c @@ -62,7 +62,7 @@ int32_t tdbOpen(const char *dbname, int32_t szPage, int32_t pages, TDB **ppDb, i } memset(pDb->pgrHash, 0, tsize); - ret = taosMulModeMkDir(dbname, 0755); + ret = taosMulModeMkDir(dbname, 0755, false); if (ret < 0) { return -1; } diff --git a/source/os/src/osDir.c b/source/os/src/osDir.c index dff0cf9886..e9f8c7f7e6 100644 --- a/source/os/src/osDir.c +++ b/source/os/src/osDir.c @@ -193,7 +193,7 @@ int32_t taosMulMkDir(const char *dirname) { return code; } -int32_t taosMulModeMkDir(const char *dirname, int mode) { +int32_t taosMulModeMkDir(const char *dirname, int mode, bool createLogFile) { if (dirname == NULL || strlen(dirname) >= TDDIRMAXLEN) return -1; char temp[TDDIRMAXLEN]; char *pos = temp; @@ -206,73 +206,16 @@ int32_t taosMulModeMkDir(const char *dirname, int mode) { #endif if (taosDirExist(temp)) { - return chmod(temp, mode); - } - - if (strncmp(temp, TD_DIRSEP, 1) == 0) { - pos += 1; - } else if (strncmp(temp, "." TD_DIRSEP, 2) == 0) { - pos += 2; - } - - for (; *pos != '\0'; pos++) { - if (*pos == TD_DIRSEP[0]) { - *pos = '\0'; -#ifdef WINDOWS - code = _mkdir(temp, mode); -#elif defined(DARWIN) - code = mkdir(dirname, 0777); -#else - code = mkdir(temp, mode); -#endif - if (code < 0 && errno != EEXIST) { - // terrno = TAOS_SYSTEM_ERROR(errno); - return code; + if (createLogFile) { + if (!taosCheckAccessFile(temp, TD_FILE_ACCESS_EXIST_OK | TD_FILE_ACCESS_READ_OK | TD_FILE_ACCESS_WRITE_OK)) { + code = -1; } - *pos = TD_DIRSEP[0]; - } - } - - if (*(pos - 1) != TD_DIRSEP[0]) { -#ifdef WINDOWS - code = _mkdir(temp, mode); -#elif defined(DARWIN) - code = mkdir(dirname, 0777); -#else - code = mkdir(temp, mode); -#endif - if (code < 0 && errno != EEXIST) { - // terrno = TAOS_SYSTEM_ERROR(errno); return code; + } else { + return chmod(temp, mode); } } - if (code < 0 && errno == EEXIST) { - return chmod(temp, mode); - } - - return chmod(temp, mode); -} - -int32_t taosMulModeMkLogDir(const char *dirname, int mode) { - if (dirname == NULL || strlen(dirname) >= TDDIRMAXLEN) return -1; - char temp[TDDIRMAXLEN]; - char *pos = temp; - int32_t code = 0; -#ifdef WINDOWS - taosRealPath(dirname, temp, sizeof(temp)); - if (temp[1] == ':') pos += 3; -#else - strcpy(temp, dirname); -#endif - - if (taosDirExist(temp)) { - if (!taosCheckAccessFile(temp, TD_FILE_ACCESS_EXIST_OK | TD_FILE_ACCESS_READ_OK | TD_FILE_ACCESS_WRITE_OK)) { - code = -1; - } - return code; - } - if (strncmp(temp, TD_DIRSEP, 1) == 0) { pos += 1; } else if (strncmp(temp, "." TD_DIRSEP, 2) == 0) { From c8c30e0a00c508faf6a8428846516386124b9f47 Mon Sep 17 00:00:00 2001 From: Ganlin Zhao Date: Fri, 25 Aug 2023 10:15:52 +0800 Subject: [PATCH 13/33] fix --- include/os/osDir.h | 2 +- source/os/src/osDir.c | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/include/os/osDir.h b/include/os/osDir.h index 2542f9830f..533ac8e4a4 100644 --- a/include/os/osDir.h +++ b/include/os/osDir.h @@ -83,7 +83,7 @@ void taosRemoveDir(const char *dirname); bool taosDirExist(const char *dirname); int32_t taosMkDir(const char *dirname); int32_t taosMulMkDir(const char *dirname); -int32_t taosMulModeMkDir(const char *dirname, int mode, bool createLogFile); +int32_t taosMulModeMkDir(const char *dirname, int mode, bool checkAccess); void taosRemoveOldFiles(const char *dirname, int32_t keepDays); int32_t taosExpandDir(const char *dirname, char *outname, int32_t maxlen); int32_t taosRealPath(char *dirname, char *realPath, int32_t maxlen); diff --git a/source/os/src/osDir.c b/source/os/src/osDir.c index e9f8c7f7e6..d0fb7ee919 100644 --- a/source/os/src/osDir.c +++ b/source/os/src/osDir.c @@ -193,7 +193,7 @@ int32_t taosMulMkDir(const char *dirname) { return code; } -int32_t taosMulModeMkDir(const char *dirname, int mode, bool createLogFile) { +int32_t taosMulModeMkDir(const char *dirname, int mode, bool checkAccess) { if (dirname == NULL || strlen(dirname) >= TDDIRMAXLEN) return -1; char temp[TDDIRMAXLEN]; char *pos = temp; @@ -206,14 +206,10 @@ int32_t taosMulModeMkDir(const char *dirname, int mode, bool createLogFile) { #endif if (taosDirExist(temp)) { - if (createLogFile) { - if (!taosCheckAccessFile(temp, TD_FILE_ACCESS_EXIST_OK | TD_FILE_ACCESS_READ_OK | TD_FILE_ACCESS_WRITE_OK)) { - code = -1; - } + if (checkAccess && taosCheckAccessFile(temp, TD_FILE_ACCESS_EXIST_OK | TD_FILE_ACCESS_READ_OK | TD_FILE_ACCESS_WRITE_OK)) { return code; - } else { - return chmod(temp, mode); } + return chmod(temp, mode); } if (strncmp(temp, TD_DIRSEP, 1) == 0) { From 9fc75fb73d68f0506c6f624793954a06bbee143e Mon Sep 17 00:00:00 2001 From: shenglian zhou Date: Fri, 25 Aug 2023 14:59:39 +0800 Subject: [PATCH 14/33] fix: wrong start key for pFill when unit of sliding is y or n --- source/libs/executor/src/filloperator.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/source/libs/executor/src/filloperator.c b/source/libs/executor/src/filloperator.c index 9b0b43b6c4..dc6d7a791e 100644 --- a/source/libs/executor/src/filloperator.c +++ b/source/libs/executor/src/filloperator.c @@ -166,18 +166,20 @@ static void revisedFillStartKey(SFillOperatorInfo* pInfo, SSDataBlock* pBlock, i } else if (ekey < pInfo->pFillInfo->start) { int64_t t = ekey; SInterval* pInterval = &pInfo->pFillInfo->interval; - + int64_t prev = t; while(1) { - int64_t prev = taosTimeAdd(t, pInterval->sliding, pInterval->slidingUnit, pInterval->precision); - if (prev >= pInfo->pFillInfo->start) { - t = prev; + int64_t next = taosTimeAdd(t, pInterval->sliding, pInterval->slidingUnit, pInterval->precision); + if (next >= pInfo->pFillInfo->start) { + prev = t; + t = next; break; } - t = prev; + prev = t; + t = next; } - // todo time window chosen problem: t or prev value? - if (t > pInfo->pFillInfo->start) t -= pInterval->sliding; + // todo time window chosen problem: t or next value? + if (t > pInfo->pFillInfo->start) t = prev; taosFillUpdateStartTimestampInfo(pInfo->pFillInfo, t); } } From e0f88eb19c5bdf1b3adda0d41cda4c737f2d4cf8 Mon Sep 17 00:00:00 2001 From: Ping Xiao Date: Fri, 25 Aug 2023 17:18:02 +0800 Subject: [PATCH 15/33] build: release ver-3.1.0.3 --- cmake/cmake.version | 2 +- docs/en/28-releases/01-tdengine.md | 4 ++++ docs/zh/28-releases/01-tdengine.md | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cmake/cmake.version b/cmake/cmake.version index ee02ef8c07..a356d974f6 100644 --- a/cmake/cmake.version +++ b/cmake/cmake.version @@ -2,7 +2,7 @@ IF (DEFINED VERNUMBER) SET(TD_VER_NUMBER ${VERNUMBER}) ELSE () - SET(TD_VER_NUMBER "3.1.0.3.alpha") + SET(TD_VER_NUMBER "3.1.0.4.alpha") ENDIF () IF (DEFINED VERCOMPATIBLE) diff --git a/docs/en/28-releases/01-tdengine.md b/docs/en/28-releases/01-tdengine.md index ff6a36440f..c02b3227ca 100644 --- a/docs/en/28-releases/01-tdengine.md +++ b/docs/en/28-releases/01-tdengine.md @@ -10,6 +10,10 @@ For TDengine 2.x installation packages by version, please visit [here](https://t import Release from "/components/ReleaseV3"; +## 3.1.0.3 + + + ## 3.1.0.2 diff --git a/docs/zh/28-releases/01-tdengine.md b/docs/zh/28-releases/01-tdengine.md index d316b3ab68..d4e4b116b7 100644 --- a/docs/zh/28-releases/01-tdengine.md +++ b/docs/zh/28-releases/01-tdengine.md @@ -10,6 +10,10 @@ TDengine 2.x 各版本安装包请访问[这里](https://www.taosdata.com/all-do import Release from "/components/ReleaseV3"; +## 3.1.0.3 + + + ## 3.1.0.2 From 8b745de397575003717aa5bd109d6b408aabf076 Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Sat, 26 Aug 2023 00:43:47 +0800 Subject: [PATCH 16/33] fix:transaction error --- source/dnode/mnode/impl/src/mndConsumer.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/source/dnode/mnode/impl/src/mndConsumer.c b/source/dnode/mnode/impl/src/mndConsumer.c index e83235fd74..f25bd2cffb 100644 --- a/source/dnode/mnode/impl/src/mndConsumer.c +++ b/source/dnode/mnode/impl/src/mndConsumer.c @@ -212,11 +212,8 @@ static int32_t mndProcessConsumerClearMsg(SRpcMsg *pMsg) { mndReleaseConsumer(pMnode, pConsumer); - STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_TOPIC, pMsg, "clear-csm"); + STrans *pTrans = mndTransCreate(pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_NOTHING, pMsg, "clear-csm"); if (pTrans == NULL) goto FAIL; - if (validateTopics(pTrans, pConsumer->assignedTopics, pMnode, pMsg->info.conn.user) != 0){ - goto FAIL; - } // this is the drop action, not the update action if (mndSetConsumerDropLogs(pMnode, pTrans, pConsumerNew) != 0) goto FAIL; if (mndTransPrepare(pMnode, pTrans) != 0) goto FAIL; From c60ac2b8ae62f0a33bf1e71536ed0045d78f5e63 Mon Sep 17 00:00:00 2001 From: Ganlin Zhao Date: Mon, 28 Aug 2023 09:45:50 +0800 Subject: [PATCH 17/33] fix --- source/os/src/osDir.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/os/src/osDir.c b/source/os/src/osDir.c index d0fb7ee919..df4d03c118 100644 --- a/source/os/src/osDir.c +++ b/source/os/src/osDir.c @@ -207,7 +207,7 @@ int32_t taosMulModeMkDir(const char *dirname, int mode, bool checkAccess) { if (taosDirExist(temp)) { if (checkAccess && taosCheckAccessFile(temp, TD_FILE_ACCESS_EXIST_OK | TD_FILE_ACCESS_READ_OK | TD_FILE_ACCESS_WRITE_OK)) { - return code; + return 0; } return chmod(temp, mode); } @@ -251,9 +251,15 @@ int32_t taosMulModeMkDir(const char *dirname, int mode, bool checkAccess) { } if (code < 0 && errno == EEXIST) { + if (checkAccess && taosCheckAccessFile(temp, TD_FILE_ACCESS_EXIST_OK | TD_FILE_ACCESS_READ_OK | TD_FILE_ACCESS_WRITE_OK)) { + return 0; + } return chmod(temp, mode); } + if (checkAccess && taosCheckAccessFile(temp, TD_FILE_ACCESS_EXIST_OK | TD_FILE_ACCESS_READ_OK | TD_FILE_ACCESS_WRITE_OK)) { + return 0; + } return chmod(temp, mode); } From 2f46b2eefd35efa5a927acb4d742d7d2bda09e1a Mon Sep 17 00:00:00 2001 From: Ganlin Zhao Date: Mon, 28 Aug 2023 10:10:16 +0800 Subject: [PATCH 18/33] fix --- source/os/src/osDir.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/os/src/osDir.c b/source/os/src/osDir.c index df4d03c118..6e52c4ed27 100644 --- a/source/os/src/osDir.c +++ b/source/os/src/osDir.c @@ -257,9 +257,6 @@ int32_t taosMulModeMkDir(const char *dirname, int mode, bool checkAccess) { return chmod(temp, mode); } - if (checkAccess && taosCheckAccessFile(temp, TD_FILE_ACCESS_EXIST_OK | TD_FILE_ACCESS_READ_OK | TD_FILE_ACCESS_WRITE_OK)) { - return 0; - } return chmod(temp, mode); } From 193284d9baa033d528ea220c65733c33ffca4617 Mon Sep 17 00:00:00 2001 From: huolibo Date: Mon, 28 Aug 2023 10:11:47 +0800 Subject: [PATCH 19/33] release(driver): jdbc release 3.2.5 version --- docs/en/07-develop/07-tmq.mdx | 12 ++++++++++++ docs/en/14-reference/03-connector/04-java.mdx | 18 ++++++++++++++++++ docs/zh/07-develop/07-tmq.mdx | 12 ++++++++++++ docs/zh/08-connector/14-java.mdx | 18 ++++++++++++++++++ 4 files changed, 60 insertions(+) diff --git a/docs/en/07-develop/07-tmq.mdx b/docs/en/07-develop/07-tmq.mdx index e78855dad3..98ba100862 100644 --- a/docs/en/07-develop/07-tmq.mdx +++ b/docs/en/07-develop/07-tmq.mdx @@ -120,7 +120,19 @@ Set subscription() throws SQLException; ConsumerRecords poll(Duration timeout) throws SQLException; +Set assignment() throws SQLException; +long position(TopicPartition partition) throws SQLException; +Map position(String topic) throws SQLException; +Map beginningOffsets(String topic) throws SQLException; +Map endOffsets(String topic) throws SQLException; +Map committed(Set partitions) throws SQLException; + +void seek(TopicPartition partition, long offset) throws SQLException; +void seekToBeginning(Collection partitions) throws SQLException; +void seekToEnd(Collection partitions) throws SQLException; + void commitSync() throws SQLException; +void commitSync(Map offsets) throws SQLException; void close() throws SQLException; ``` diff --git a/docs/en/14-reference/03-connector/04-java.mdx b/docs/en/14-reference/03-connector/04-java.mdx index ff1f209788..996ad39035 100644 --- a/docs/en/14-reference/03-connector/04-java.mdx +++ b/docs/en/14-reference/03-connector/04-java.mdx @@ -36,6 +36,7 @@ REST connection supports all platforms that can run Java. | taos-jdbcdriver version | major changes | TDengine version | | :---------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------: | +| 3.2.5 | Subscription add committed() and assignment() method | 3.1.0.3 or later | | 3.2.4 | Subscription add the enable.auto.commit parameter and the unsubscribe() method in the WebSocket connection | - | | 3.2.3 | Fixed resultSet data parsing failure in some cases | - | | 3.2.2 | Subscription add seek function | 3.0.5.0 or later | @@ -1019,14 +1020,19 @@ while(true) { #### Assignment subscription Offset ```java +// get topicPartition +Set assignment() throws SQLException; // get offset long position(TopicPartition partition) throws SQLException; Map position(String topic) throws SQLException; Map beginningOffsets(String topic) throws SQLException; Map endOffsets(String topic) throws SQLException; +Map committed(Set partitions) throws SQLException; // Overrides the fetch offsets that the consumer will use on the next poll(timeout). void seek(TopicPartition partition, long offset) throws SQLException; +void seekToBeginning(Collection partitions) throws SQLException; +void seekToEnd(Collection partitions) throws SQLException; ``` Example usage is as follows. @@ -1052,6 +1058,18 @@ try (TaosConsumer consumer = new TaosConsumer<>(properties)) { } ``` +#### Commit offset + +If `enable.auto.commit` is false, offset can be submitted manually. + +```java +void commitSync() throws SQLException; +void commitSync(Map offsets) throws SQLException; +// async commit only support jni connection +void commitAsync(OffsetCommitCallback callback) throws SQLException; +void commitAsync(Map offsets, OffsetCommitCallback callback) throws SQLException; +``` + #### Close subscriptions ```java diff --git a/docs/zh/07-develop/07-tmq.mdx b/docs/zh/07-develop/07-tmq.mdx index 04c978679e..d87eb89f0f 100644 --- a/docs/zh/07-develop/07-tmq.mdx +++ b/docs/zh/07-develop/07-tmq.mdx @@ -120,7 +120,19 @@ Set subscription() throws SQLException; ConsumerRecords poll(Duration timeout) throws SQLException; +Set assignment() throws SQLException; +long position(TopicPartition partition) throws SQLException; +Map position(String topic) throws SQLException; +Map beginningOffsets(String topic) throws SQLException; +Map endOffsets(String topic) throws SQLException; +Map committed(Set partitions) throws SQLException; + +void seek(TopicPartition partition, long offset) throws SQLException; +void seekToBeginning(Collection partitions) throws SQLException; +void seekToEnd(Collection partitions) throws SQLException; + void commitSync() throws SQLException; +void commitSync(Map offsets) throws SQLException; void close() throws SQLException; ``` diff --git a/docs/zh/08-connector/14-java.mdx b/docs/zh/08-connector/14-java.mdx index 36eacd26a4..0ff00d1710 100644 --- a/docs/zh/08-connector/14-java.mdx +++ b/docs/zh/08-connector/14-java.mdx @@ -36,6 +36,7 @@ REST 连接支持所有能运行 Java 的平台。 | taos-jdbcdriver 版本 | 主要变化 | TDengine 版本 | | :------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------: | +| 3.2.5 | 数据订阅增加 committed()、assignment() 方法 | 3.1.0.3 及更高版本 | | 3.2.4 | 数据订阅在 WebSocket 连接下增加 enable.auto.commit 参数,以及 unsubscribe() 方法。 | - | | 3.2.3 | 修复 ResultSet 在一些情况数据解析失败 | - | | 3.2.2 | 新增功能:数据订阅支持 seek 功能。 | 3.0.5.0 及更高版本 | @@ -1022,14 +1023,19 @@ while(true) { #### 指定订阅 Offset ```java +// 获取订阅的 topicPartition +Set assignment() throws SQLException; // 获取 offset long position(TopicPartition partition) throws SQLException; Map position(String topic) throws SQLException; Map beginningOffsets(String topic) throws SQLException; Map endOffsets(String topic) throws SQLException; +Map committed(Set partitions) throws SQLException; // 指定下一次 poll 中使用的 offset void seek(TopicPartition partition, long offset) throws SQLException; +void seekToBeginning(Collection partitions) throws SQLException; +void seekToEnd(Collection partitions) throws SQLException; ``` 示例代码: @@ -1055,6 +1061,18 @@ try (TaosConsumer consumer = new TaosConsumer<>(properties)) { } ``` +#### 提交 Offset + +当`enable.auto.commit`为 false 时,可以手动提交 offset。 + +```java +void commitSync() throws SQLException; +void commitSync(Map offsets) throws SQLException; +// 异步提交仅在 native 连接下有效 +void commitAsync(OffsetCommitCallback callback) throws SQLException; +void commitAsync(Map offsets, OffsetCommitCallback callback) throws SQLException; +``` + #### 关闭订阅 ```java From 09463cb43ee807d432972e0eab8af1f67d6b92c2 Mon Sep 17 00:00:00 2001 From: shenglian zhou Date: Mon, 28 Aug 2023 11:25:16 +0800 Subject: [PATCH 20/33] enhance: fix buffer size overflow --- source/libs/scalar/src/sclfunc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/libs/scalar/src/sclfunc.c b/source/libs/scalar/src/sclfunc.c index b9af716929..7a19fda08e 100644 --- a/source/libs/scalar/src/sclfunc.c +++ b/source/libs/scalar/src/sclfunc.c @@ -654,8 +654,12 @@ int32_t substrFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOu SColumnInfoData *pInputData = pInput->columnData; SColumnInfoData *pOutputData = pOutput->columnData; - int32_t outputLen = pInputData->varmeta.length * pInput->numOfRows; + uint32_t outputLen = pInputData->varmeta.length; char *outputBuf = taosMemoryCalloc(outputLen, 1); + if (outputBuf == NULL) { + qError("memory allocation failure. size: %u", outputLen); + return TSDB_CODE_OUT_OF_MEMORY; + } char *output = outputBuf; for (int32_t i = 0; i < pInput->numOfRows; ++i) { From 322e8c66975201ea01de18dd0f0624eb43b89551 Mon Sep 17 00:00:00 2001 From: shenglian zhou Date: Mon, 28 Aug 2023 13:43:37 +0800 Subject: [PATCH 21/33] fix: use col cell size instead of total col data size --- source/libs/scalar/src/sclfunc.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/source/libs/scalar/src/sclfunc.c b/source/libs/scalar/src/sclfunc.c index 7a19fda08e..1b7559a207 100644 --- a/source/libs/scalar/src/sclfunc.c +++ b/source/libs/scalar/src/sclfunc.c @@ -654,13 +654,12 @@ int32_t substrFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOu SColumnInfoData *pInputData = pInput->columnData; SColumnInfoData *pOutputData = pOutput->columnData; - uint32_t outputLen = pInputData->varmeta.length; - char *outputBuf = taosMemoryCalloc(outputLen, 1); + int32_t outputLen = pInputData->info.bytes; + char *outputBuf = taosMemoryMalloc(outputLen); if (outputBuf == NULL) { - qError("memory allocation failure. size: %u", outputLen); + qError("substr function memory allocation failure. size: %d", outputLen); return TSDB_CODE_OUT_OF_MEMORY; } - char *output = outputBuf; for (int32_t i = 0; i < pInput->numOfRows; ++i) { if (colDataIsNull_s(pInputData, i)) { @@ -680,14 +679,16 @@ int32_t substrFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOu startPosBytes = TMAX(startPosBytes, 0); } + char *output = outputBuf; int32_t resLen = TMIN(subLen, len - startPosBytes); if (resLen > 0) { memcpy(varDataVal(output), varDataVal(input) + startPosBytes, resLen); + varDataSetLen(output, resLen); + } else { + varDataSetLen(output, 0); } - varDataSetLen(output, resLen); colDataSetVal(pOutputData, i, output, false); - output += varDataTLen(output); } pOutput->numOfRows = pInput->numOfRows; From 271502d3c7d6e6392d230218926063f74063e2b7 Mon Sep 17 00:00:00 2001 From: shenglian zhou Date: Mon, 28 Aug 2023 15:08:55 +0800 Subject: [PATCH 22/33] fix: add test case --- tests/script/tsim/query/interval.sim | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/script/tsim/query/interval.sim b/tests/script/tsim/query/interval.sim index e2b0d219cb..5fc6a867d7 100644 --- a/tests/script/tsim/query/interval.sim +++ b/tests/script/tsim/query/interval.sim @@ -206,6 +206,19 @@ if $desc_rows != $asc_rows then return -1 endi +print ================= step11 + +sql create database if not exists test0828 +sql use test0828 +sql create stable st (ts timestamp, c2 int) tags(tg int) +sql insert into ct1 using st tags(1) values('2021-08-01', 0) +sql insert into ct2 using st tags(2) values('2022-08-01', 1) +sql select _wstart, _wend, count(*) from st where ts>='2021-01-01' and ts < now interval(1n) fill(value, 0) order by _wstart desc +print $rows +if $rows != 32 then + return -1 +endi +sql drop database test0828 print =============== clear #sql drop database $db #sql select * from information_schema.ins_databases From 05bb1646759cc15d357b593d7a80d959ca0ec709 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Mon, 28 Aug 2023 15:56:02 +0800 Subject: [PATCH 23/33] fix(stream): release tasks. --- source/dnode/vnode/src/tq/tq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/dnode/vnode/src/tq/tq.c b/source/dnode/vnode/src/tq/tq.c index 3396803f08..c61fd78747 100644 --- a/source/dnode/vnode/src/tq/tq.c +++ b/source/dnode/vnode/src/tq/tq.c @@ -1196,6 +1196,7 @@ int32_t tqProcessTaskScanHistory(STQ* pTq, SRpcMsg* pMsg) { "s-task:%s failed to start scan-history in first stream time window since already started, unexpected " "sched-status:%d", id, schedStatus); + streamMetaReleaseTask(pMeta, pTask); return 0; } @@ -1209,6 +1210,7 @@ int32_t tqProcessTaskScanHistory(STQ* pTq, SRpcMsg* pMsg) { tqDebug("s-task:%s is paused in the step1, elapsed time:%.2fs, sched-status:%d", pTask->id.idStr, el, TASK_SCHED_STATUS__INACTIVE); atomic_store_8(&pTask->status.schedStatus, TASK_SCHED_STATUS__INACTIVE); + streamMetaReleaseTask(pMeta, pTask); return 0; } From 2e04d513a43f6a8ef0723a1e502f35502119ec3b Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Mon, 28 Aug 2023 16:41:28 +0800 Subject: [PATCH 24/33] fix:unregister push manager if delete subscribe to avoid tqProcessPollPush --- source/dnode/vnode/src/tq/tq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/dnode/vnode/src/tq/tq.c b/source/dnode/vnode/src/tq/tq.c index 3396803f08..5a7cdb46ed 100644 --- a/source/dnode/vnode/src/tq/tq.c +++ b/source/dnode/vnode/src/tq/tq.c @@ -779,6 +779,8 @@ int32_t tqProcessDeleteSubReq(STQ* pTq, int64_t sversion, char* msg, int32_t msg walCloseRef(pTq->pVnode->pWal, pHandle->pRef->refId); } + tqUnregisterPushHandle(pTq, pHandle); + code = taosHashRemove(pTq->pHandle, pReq->subKey, strlen(pReq->subKey)); if (code != 0) { tqError("cannot process tq delete req %s, since no such handle", pReq->subKey); From d8ce7a14676c07c69ad5c2b94c8f6396c110ad2c Mon Sep 17 00:00:00 2001 From: shenglian-zhou Date: Mon, 28 Aug 2023 16:46:05 +0800 Subject: [PATCH 25/33] Update interval.sim to now use now --- tests/script/tsim/query/interval.sim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/script/tsim/query/interval.sim b/tests/script/tsim/query/interval.sim index 5fc6a867d7..135fcc8591 100644 --- a/tests/script/tsim/query/interval.sim +++ b/tests/script/tsim/query/interval.sim @@ -213,7 +213,7 @@ sql use test0828 sql create stable st (ts timestamp, c2 int) tags(tg int) sql insert into ct1 using st tags(1) values('2021-08-01', 0) sql insert into ct2 using st tags(2) values('2022-08-01', 1) -sql select _wstart, _wend, count(*) from st where ts>='2021-01-01' and ts < now interval(1n) fill(value, 0) order by _wstart desc +sql select _wstart, _wend, count(*) from st where ts>='2021-01-01' and ts < '2023-08-28' interval(1n) fill(value, 0) order by _wstart desc print $rows if $rows != 32 then return -1 From 728c542bd0b8b654c94f9f65f0c897295360528d Mon Sep 17 00:00:00 2001 From: yihaoDeng Date: Mon, 28 Aug 2023 22:30:50 +0800 Subject: [PATCH 26/33] update doc --- docs/en/12-taos-sql/27-indexing.md | 3 +++ docs/zh/12-taos-sql/27-index.md | 2 ++ 2 files changed, 5 insertions(+) diff --git a/docs/en/12-taos-sql/27-indexing.md b/docs/en/12-taos-sql/27-indexing.md index a89c8929c1..1badd38d17 100644 --- a/docs/en/12-taos-sql/27-indexing.md +++ b/docs/en/12-taos-sql/27-indexing.md @@ -19,6 +19,9 @@ index_option: functions: function [, function] ... ``` +### tag Indexing + + [tag index](../tag-index) ### SMA Indexing diff --git a/docs/zh/12-taos-sql/27-index.md b/docs/zh/12-taos-sql/27-index.md index da8f38eb22..327d909616 100644 --- a/docs/zh/12-taos-sql/27-index.md +++ b/docs/zh/12-taos-sql/27-index.md @@ -20,6 +20,8 @@ index_option: functions: function [, function] ... ``` +### tag 索引 + [tag-index] (../tag-index) ### SMA 索引 From 381072f0c08041146687620101cae453ae4f490e Mon Sep 17 00:00:00 2001 From: yihaoDeng Date: Mon, 28 Aug 2023 22:32:19 +0800 Subject: [PATCH 27/33] update doc --- docs/zh/12-taos-sql/27-index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh/12-taos-sql/27-index.md b/docs/zh/12-taos-sql/27-index.md index 327d909616..f934672bd2 100644 --- a/docs/zh/12-taos-sql/27-index.md +++ b/docs/zh/12-taos-sql/27-index.md @@ -21,7 +21,7 @@ functions: function [, function] ... ``` ### tag 索引 - [tag-index] (../tag-index) + [tag 索引] (../tag-index) ### SMA 索引 From 27138b22aa4214ae7ac485385f5169358161e53d Mon Sep 17 00:00:00 2001 From: Shuduo Sang Date: Tue, 29 Aug 2023 11:45:42 +0800 Subject: [PATCH 28/33] docs: add seeq doc zh (#22601) --- docs/zh/20-third-party/70-seeq.md | 437 ++++++++++++++++++ .../20-third-party/seeq/seeq-demo-schema.webp | Bin 0 -> 13174 bytes .../seeq/seeq-demo-workbench.webp | Bin 0 -> 56926 bytes .../seeq/seeq-forecast-result.webp | Bin 0 -> 26598 bytes .../seeq-workbench-with-tdengine-cloud.webp | Bin 0 -> 48280 bytes 5 files changed, 437 insertions(+) create mode 100644 docs/zh/20-third-party/70-seeq.md create mode 100644 docs/zh/20-third-party/seeq/seeq-demo-schema.webp create mode 100644 docs/zh/20-third-party/seeq/seeq-demo-workbench.webp create mode 100644 docs/zh/20-third-party/seeq/seeq-forecast-result.webp create mode 100644 docs/zh/20-third-party/seeq/seeq-workbench-with-tdengine-cloud.webp diff --git a/docs/zh/20-third-party/70-seeq.md b/docs/zh/20-third-party/70-seeq.md new file mode 100644 index 0000000000..0bdf58955d --- /dev/null +++ b/docs/zh/20-third-party/70-seeq.md @@ -0,0 +1,437 @@ +--- +sidebar_label: Seeq +title: Seeq +description: 如何使用 Seeq 和 TDengine 进行时序数据分析 +--- + +# 如何使用 Seeq 和 TDengine 进行时序数据分析 + +## 方案介绍 + +Seeq 是制造业和工业互联网(IIOT)高级分析软件。Seeq 支持在工艺制造组织中使用机器学习创新的新功能。这些功能使组织能够将自己或第三方机器学习算法部署到前线流程工程师和主题专家使用的高级分析应用程序,从而使单个数据科学家的努力扩展到许多前线员工。 + +通过 TDengine Java connector, Seeq 可以轻松支持查询 TDengine 提供的时序数据,并提供数据展现、分析、预测等功能。 + +### Seeq 安装方法 + +从 (Seeq 官网)[https://www.seeq.com/customer-download]下载相关软件,例如 Seeq Server 和 Seeq Data Lab 等。 + +### Seeq Server 安装和启动 + +``` +tar xvzf seeq-server-xxx.tar.gz +cd seeq-server-installer +sudo ./install + +sudo seeq service enable +sudo seeq start +``` + +### Seeq Data Lab Server 安装和启动 + +Seeq Data Lab 需要安装在和 Seeq Server 不同的服务器上,并通过配置和 Seeq Server 互联。详细安装配置指令参见(Seeq 官方文档)[https://support.seeq.com/space/KB/1034059842]。 + +``` +tar xvf seeq-data-lab--64bit-linux.tar.gz +sudo seeq-data-lab-installer/install -f /opt/seeq/seeq-data-lab -g /var/opt/seeq -u seeq +sudo seeq config set Network/DataLab/Hostname localhost +sudo seeq config set Network/DataLab/Port 34231 # the port of the Data Lab server (usually 34231) +sudo seeq config set Network/Hostname # the host IP or URL of the main Seeq Server + +# If the main Seeq server is configured to listen over HTTPS +sudo seeq config set Network/Webserver/SecurePort 443 # the secure port of the main Seeq Server (usually 443) + +# If the main Seeq server is NOT configured to listen over HTTPS +sudo seeq config set Network/Webserver/Port + +#On the main Seeq server, open a Seeq Command Prompt and set the hostname of the Data Lab server: +sudo seeq config set Network/DataLab/Hostname # the host IP (not URL) of the Data Lab server +sudo seeq config set Network/DataLab/Port 34231 # the port of the Data Lab server (usually 34231 +``` + +## TDengine 本地实例安装方法 + +请参考(官网文档)[https://docs.taosdata.com/get-started/package/]。 + +## TDengine Cloud 访问方法 +如果使用 Seeq 连接 TDengine Cloud,请在 https://cloud.taosdata.com 申请帐号并登录查看如何访问 TDengine Cloud。 + +## 如何配置 Seeq 访问 TDengine + +1. 查看 data 存储位置 + +``` +sudo seeq config get Folders/Data +``` + +2. 从 maven.org 下载 TDengine Java connector 包,目前最新版本为(3.2.4)[https://repo1.maven.org/maven2/com/taosdata/jdbc/taos-jdbcdriver/3.2.4/taos-jdbcdriver-3.2.4-dist.jar],并拷贝至 data 存储位置的 plugins\lib 中。 + +3. 重新启动 seeq server + +``` +sudo seeq restart +``` + +4. 输入 License + +使用浏览器访问 ip:34216 并按照说明输入 license。 + +## 使用 Seeq 分析 TDengine 时序数据 + +本章节演示如何使用 Seeq 软件配合 TDengine 进行时序数据分析。 + +### 场景介绍 + +示例场景为一个电力系统,用户每天从电站仪表收集用电量数据,并将其存储在 TDengine 集群中。现在用户想要预测电力消耗将会如何发展,并购买更多设备来支持它。用户电力消耗随着每月订单变化而不同,另外考虑到季节变化,电力消耗量会有所不同。这个城市位于北半球,所以在夏天会使用更多的电力。我们模拟数据来反映这些假定。 + +### 数据 Schema + +``` +CREATE STABLE meters (ts TIMESTAMP, num INT, temperature FLOAT, goods INT) TAGS (device NCHAR(20)); +CREATE TABLE goods (ts1 TIMESTAMP, ts2 TIMESTAMP, goods FLOAT); +``` + +!(Seeq demo schema)[./seeq/seeq-demo-schema.webp] + +### 构造数据方法 + +``` +python mockdata.py +taos -s "insert into power.goods select _wstart, _wstart + 10d, avg(goods) from power.meters interval(10d);" +``` +源代码托管在(github 仓库)[https://github.com/sangshuduo/td-forecasting]。 + +### 使用 Seeq 进行数据分析 + +#### 配置数据源(Data Source) + +使用 Seeq 管理员角色的帐号登录,并新建数据源。 + +- Power + +``` +{ + "QueryDefinitions": [ + { + "Name": "PowerNum", + "Type": "SIGNAL", + "Sql": "SELECT ts, num FROM meters", + "Enabled": true, + "TestMode": false, + "TestQueriesDuringSync": true, + "InProgressCapsulesEnabled": false, + "Variables": null, + "Properties": [ + { + "Name": "Name", + "Value": "Num", + "Sql": null, + "Uom": "string" + }, + { + "Name": "Interpolation Method", + "Value": "linear", + "Sql": null, + "Uom": "string" + }, + { + "Name": "Maximum Interpolation", + "Value": "2day", + "Sql": null, + "Uom": "string" + } + ], + "CapsuleProperties": null + } + ], + "Type": "GENERIC", + "Hostname": null, + "Port": 0, + "DatabaseName": null, + "Username": "root", + "Password": "taosdata", + "InitialSql": null, + "TimeZone": null, + "PrintRows": false, + "UseWindowsAuth": false, + "SqlFetchBatchSize": 100000, + "UseSSL": false, + "JdbcProperties": null, + "GenericDatabaseConfig": { + "DatabaseJdbcUrl": "jdbc:TAOS-RS://127.0.0.1:6041/power?user=root&password=taosdata", + "SqlDriverClassName": "com.taosdata.jdbc.rs.RestfulDriver", + "ResolutionInNanoseconds": 1000, + "ZonedColumnTypes": [] + } +} +``` + +- Goods + +``` +{ + "QueryDefinitions": [ + { + "Name": "PowerGoods", + "Type": "CONDITION", + "Sql": "SELECT ts1, ts2, goods FROM power.goods", + "Enabled": true, + "TestMode": false, + "TestQueriesDuringSync": true, + "InProgressCapsulesEnabled": false, + "Variables": null, + "Properties": [ + { + "Name": "Name", + "Value": "Goods", + "Sql": null, + "Uom": "string" + }, + { + "Name": "Maximum Duration", + "Value": "10days", + "Sql": null, + "Uom": "string" + } + ], + "CapsuleProperties": [ + { + "Name": "goods", + "Value": "${columnResult}", + "Column": "goods", + "Uom": "string" + } + ] + } + ], + "Type": "GENERIC", + "Hostname": null, + "Port": 0, + "DatabaseName": null, + "Username": "root", + "Password": "taosdata", + "InitialSql": null, + "TimeZone": null, + "PrintRows": false, + "UseWindowsAuth": false, + "SqlFetchBatchSize": 100000, + "UseSSL": false, + "JdbcProperties": null, + "GenericDatabaseConfig": { + "DatabaseJdbcUrl": "jdbc:TAOS-RS://127.0.0.1:6041/power?user=root&password=taosdata", + "SqlDriverClassName": "com.taosdata.jdbc.rs.RestfulDriver", + "ResolutionInNanoseconds": 1000, + "ZonedColumnTypes": [] + } +} +``` + +- Temperature + +``` +{ + "QueryDefinitions": [ + { + "Name": "PowerNum", + "Type": "SIGNAL", + "Sql": "SELECT ts, temperature FROM meters", + "Enabled": true, + "TestMode": false, + "TestQueriesDuringSync": true, + "InProgressCapsulesEnabled": false, + "Variables": null, + "Properties": [ + { + "Name": "Name", + "Value": "Temperature", + "Sql": null, + "Uom": "string" + }, + { + "Name": "Interpolation Method", + "Value": "linear", + "Sql": null, + "Uom": "string" + }, + { + "Name": "Maximum Interpolation", + "Value": "2day", + "Sql": null, + "Uom": "string" + } + ], + "CapsuleProperties": null + } + ], + "Type": "GENERIC", + "Hostname": null, + "Port": 0, + "DatabaseName": null, + "Username": "root", + "Password": "taosdata", + "InitialSql": null, + "TimeZone": null, + "PrintRows": false, + "UseWindowsAuth": false, + "SqlFetchBatchSize": 100000, + "UseSSL": false, + "JdbcProperties": null, + "GenericDatabaseConfig": { + "DatabaseJdbcUrl": "jdbc:TAOS-RS://127.0.0.1:6041/power?user=root&password=taosdata", + "SqlDriverClassName": "com.taosdata.jdbc.rs.RestfulDriver", + "ResolutionInNanoseconds": 1000, + "ZonedColumnTypes": [] + } +} +``` + +#### 使用 Seeq Workbench + +登录 Seeq 服务页面并新建 Seeq Workbench,通过选择数据源搜索结果和根据需要选择不同的工具,可以进行数据展现或预测,详细使用方法参见(官方知识库)[https://support.seeq.com/space/KB/146440193/Seeq+Workbench]。 + +!(Seeq Workbench)[./seeq/seeq-demo-workbench.webp] + +#### 用 Seeq Data Lab Server 进行进一步的数据分析 + +登录 Seeq 服务页面并新建 Seeq Data Lab,可以进一步使用 Python 编程或其他机器学习工具进行更复杂的数据挖掘功能。 + +```Python +from seeq import spy +spy.options.compatibility = 189 +import pandas as pd +import matplotlib +import matplotlib.pyplot as plt +import mlforecast +import lightgbm as lgb +from mlforecast.target_transforms import Differences +from sklearn.linear_model import LinearRegression + +ds = spy.search({'ID': "8C91A9C7-B6C2-4E18-AAAF-XXXXXXXXX"}) +print(ds) + +sig = ds.loc[ds['Name'].isin(['Num'])] +print(sig) + +data = spy.pull(sig, start='2015-01-01', end='2022-12-31', grid=None) +print("data.info()") +data.info() +print(data) +#data.plot() + +print("data[Num].info()") +data['Num'].info() +da = data['Num'].index.tolist() +#print(da) + +li = data['Num'].tolist() +#print(li) + +data2 = pd.DataFrame() +data2['ds'] = da +print('1st data2 ds info()') +data2['ds'].info() + +#data2['ds'] = pd.to_datetime(data2['ds']).to_timestamp() +data2['ds'] = pd.to_datetime(data2['ds']).astype('int64') +data2['y'] = li +print('2nd data2 ds info()') +data2['ds'].info() +print(data2) + +data2.insert(0, column = "unique_id", value="unique_id") + +print("Forecasting ...") + +forecast = mlforecast.MLForecast( + models = lgb.LGBMRegressor(), + freq = 1, + lags=[365], + target_transforms=[Differences([365])], +) + +forecast.fit(data2) +predicts = forecast.predict(365) + +pd.concat([data2, predicts]).set_index("ds").plot(title = "current data with forecast") +plt.show() +``` + +运行程序输出结果: + +!(Seeq forecast result)[./seeq/seeq-forecast-result.webp] + +### 配置 Seeq 数据源连接 TDengine Cloud + +配置 Seeq 数据源连接 TDengine Cloud 和连接 TDengine 本地安装实例没有本质的不同,只要登录 TDengine Cloud 后选择“编程 - Java”并拷贝带 token 字符串的 JDBC 填写为 Seeq Data Source 的 DatabaseJdbcUrl 值。 +注意使用 TDengine Cloud 时 SQL 命令中需要指定数据库名称。 + +#### 用 TDengine Cloud 作为数据源的配置内容示例: + +``` +{ + "QueryDefinitions": [ + { + "Name": "CloudVoltage", + "Type": "SIGNAL", + "Sql": "SELECT ts, voltage FROM test.meters", + "Enabled": true, + "TestMode": false, + "TestQueriesDuringSync": true, + "InProgressCapsulesEnabled": false, + "Variables": null, + "Properties": [ + { + "Name": "Name", + "Value": "Voltage", + "Sql": null, + "Uom": "string" + }, + { + "Name": "Interpolation Method", + "Value": "linear", + "Sql": null, + "Uom": "string" + }, + { + "Name": "Maximum Interpolation", + "Value": "2day", + "Sql": null, + "Uom": "string" + } + ], + "CapsuleProperties": null + } + ], + "Type": "GENERIC", + "Hostname": null, + "Port": 0, + "DatabaseName": null, + "Username": "root", + "Password": "taosdata", + "InitialSql": null, + "TimeZone": null, + "PrintRows": false, + "UseWindowsAuth": false, + "SqlFetchBatchSize": 100000, + "UseSSL": false, + "JdbcProperties": null, + "GenericDatabaseConfig": { + "DatabaseJdbcUrl": "jdbc:TAOS-RS://gw.cloud.taosdata.com?useSSL=true&token=41ac9d61d641b6b334e8b76f45f5a8XXXXXXXXXX", + "SqlDriverClassName": "com.taosdata.jdbc.rs.RestfulDriver", + "ResolutionInNanoseconds": 1000, + "ZonedColumnTypes": [] + } +} +``` + +#### TDengine Cloud 作为数据源的 Seeq Workbench 界面示例 + +!(Seeq workbench with TDengine cloud)[./seeq/seeq-workbench-with-tdengine-cloud.webp] + +## 方案总结 + +通过集成Seeq和TDengine,可以充分利用TDengine高效的存储和查询性能,同时也可以受益于Seeq提供给用户的强大数据可视化和分析功能。 + +这种集成使用户能够充分利用TDengine的高性能时序数据存储和检索,确保高效处理大量数据。同时,Seeq提供高级分析功能,如数据可视化、异常检测、相关性分析和预测建模,使用户能够获得有价值的洞察并基于数据进行决策。 + +综合来看,Seeq和TDengine共同为制造业、工业物联网和电力系统等各行各业的时序数据分析提供了综合解决方案。高效数据存储和先进的分析相结合,赋予用户充分发挥时序数据潜力的能力,推动运营改进,并支持预测和规划分析应用。 diff --git a/docs/zh/20-third-party/seeq/seeq-demo-schema.webp b/docs/zh/20-third-party/seeq/seeq-demo-schema.webp new file mode 100644 index 0000000000000000000000000000000000000000..3f603749ad2ed4cd0e584e88a58a2410953b3e62 GIT binary patch literal 13174 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 Pq4}etDFJV(=>Px#frSQK literal 0 HcmV?d00001 diff --git a/docs/zh/20-third-party/seeq/seeq-forecast-result.webp b/docs/zh/20-third-party/seeq/seeq-forecast-result.webp new file mode 100644 index 0000000000000000000000000000000000000000..13144207eb4bebe6a4c5915de5c795db6b30aee9 GIT binary patch literal 26598 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 TX0&z2v8^TD2wVUF00000CWhTZ literal 0 HcmV?d00001 diff --git a/docs/zh/20-third-party/seeq/seeq-workbench-with-tdengine-cloud.webp b/docs/zh/20-third-party/seeq/seeq-workbench-with-tdengine-cloud.webp new file mode 100644 index 0000000000000000000000000000000000000000..f486c3ea29790c3b48dfb017a0295a281488d319 GIT binary patch literal 48280 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
  • H5PdZ)D}>wj%ZsaRQaX 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 T9Y6p80000fumAu600000zlH=d literal 0 HcmV?d00001 From 465a0561f9d42be7c3ef24f110aa6c4cb9851726 Mon Sep 17 00:00:00 2001 From: dingbo8128 Date: Tue, 29 Aug 2023 11:49:29 +0800 Subject: [PATCH 29/33] docs: fix duplicate name index.md --- docs/zh/12-taos-sql/{27-index.md => 27-indexing.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/zh/12-taos-sql/{27-index.md => 27-indexing.md} (100%) diff --git a/docs/zh/12-taos-sql/27-index.md b/docs/zh/12-taos-sql/27-indexing.md similarity index 100% rename from docs/zh/12-taos-sql/27-index.md rename to docs/zh/12-taos-sql/27-indexing.md From db2209189d1e2ebcadc52e7bcf4cba9237457b59 Mon Sep 17 00:00:00 2001 From: dingbo8128 Date: Tue, 29 Aug 2023 11:53:31 +0800 Subject: [PATCH 30/33] docs: remove space --- docs/zh/12-taos-sql/27-indexing.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/zh/12-taos-sql/27-indexing.md b/docs/zh/12-taos-sql/27-indexing.md index f934672bd2..cf8cac1bed 100644 --- a/docs/zh/12-taos-sql/27-indexing.md +++ b/docs/zh/12-taos-sql/27-indexing.md @@ -21,7 +21,8 @@ functions: function [, function] ... ``` ### tag 索引 - [tag 索引] (../tag-index) + + [tag 索引](../tag-index) ### SMA 索引 From 82fc1f41fb08cd4e4cfe76a67cd9ef88d4c5b15f Mon Sep 17 00:00:00 2001 From: Minglei Jin Date: Tue, 29 Aug 2023 16:15:00 +0800 Subject: [PATCH 31/33] fix(tsdb/cache read): remove all null row --- source/dnode/vnode/src/tsdb/tsdbCacheRead.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source/dnode/vnode/src/tsdb/tsdbCacheRead.c b/source/dnode/vnode/src/tsdb/tsdbCacheRead.c index 8ca2ccad1b..caf88f55fc 100644 --- a/source/dnode/vnode/src/tsdb/tsdbCacheRead.c +++ b/source/dnode/vnode/src/tsdb/tsdbCacheRead.c @@ -25,7 +25,7 @@ static int32_t saveOneRow(SArray* pRow, SSDataBlock* pBlock, SCacheRowsReader* pReader, const int32_t* slotIds, const int32_t* dstSlotIds, void** pRes, const char* idStr) { int32_t numOfRows = pBlock->info.rows; - bool allNullRow = true; + // bool allNullRow = true; if (HASTYPE(pReader->type, CACHESCAN_RETRIEVE_LAST)) { for (int32_t i = 0; i < pReader->numOfCols; ++i) { @@ -36,7 +36,7 @@ static int32_t saveOneRow(SArray* pRow, SSDataBlock* pBlock, SCacheRowsReader* p p->ts = pColVal->ts; p->isNull = !COL_VAL_IS_VALUE(&pColVal->colVal); - allNullRow = p->isNull & allNullRow; + // allNullRow = p->isNull & allNullRow; if (!p->isNull) { if (IS_VAR_DATA_TYPE(pColVal->colVal.type)) { @@ -56,7 +56,8 @@ static int32_t saveOneRow(SArray* pRow, SSDataBlock* pBlock, SCacheRowsReader* p colDataSetVal(pColInfoData, numOfRows, (const char*)pRes[i], false); } - pBlock->info.rows += allNullRow ? 0 : 1; + // pBlock->info.rows += allNullRow ? 0 : 1; + ++pBlock->info.rows; } else if (HASTYPE(pReader->type, CACHESCAN_RETRIEVE_LAST_ROW)) { for (int32_t i = 0; i < pReader->numOfCols; ++i) { SColumnInfoData* pColInfoData = taosArrayGet(pBlock->pDataBlock, dstSlotIds[i]); @@ -65,7 +66,7 @@ static int32_t saveOneRow(SArray* pRow, SSDataBlock* pBlock, SCacheRowsReader* p SLastCol* pColVal = (SLastCol*)taosArrayGet(pRow, i); SColVal* pVal = &pColVal->colVal; - allNullRow = false; + // allNullRow = false; if (IS_VAR_DATA_TYPE(pColVal->colVal.type)) { if (!COL_VAL_IS_VALUE(&pColVal->colVal)) { colDataSetNULL(pColInfoData, numOfRows); @@ -80,7 +81,8 @@ static int32_t saveOneRow(SArray* pRow, SSDataBlock* pBlock, SCacheRowsReader* p } } - pBlock->info.rows += allNullRow ? 0 : 1; + // pBlock->info.rows += allNullRow ? 0 : 1; + ++pBlock->info.rows; } else { tsdbError("invalid retrieve type:%d, %s", pReader->type, idStr); return TSDB_CODE_INVALID_PARA; From b740ce8ad634847468ac9de2973fbecec7b34d54 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Tue, 29 Aug 2023 17:49:43 +0800 Subject: [PATCH 32/33] fix(tsdb): add more check for block rows to detect the duplicate rows in data block. --- source/dnode/vnode/src/tsdb/tsdbRead2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/dnode/vnode/src/tsdb/tsdbRead2.c b/source/dnode/vnode/src/tsdb/tsdbRead2.c index e635862200..c10d8c628d 100644 --- a/source/dnode/vnode/src/tsdb/tsdbRead2.c +++ b/source/dnode/vnode/src/tsdb/tsdbRead2.c @@ -1268,7 +1268,7 @@ static void getBlockToLoadInfo(SDataBlockToLoadInfo* pInfo, SFileDataBlockInfo* } // has duplicated ts of different version in this block - pInfo->hasDupTs = (pBlockInfo->record.numRow > pBlockInfo->record.count); + pInfo->hasDupTs = (pBlockInfo->record.numRow > pBlockInfo->record.count) || (pBlockInfo->record.count <= 0); pInfo->overlapWithDelInfo = overlapWithDelSkyline(pScanInfo, &pBlockInfo->record, pReader->info.order); if (hasDataInLastBlock(pLastBlockReader)) { From d6368798dac5af531dd0c3933225d39ec33bc258 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Tue, 29 Aug 2023 18:11:17 +0800 Subject: [PATCH 33/33] fix(query): disable the set quit flag for tsdbreader. --- source/dnode/vnode/src/tsdb/tsdbRead.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/dnode/vnode/src/tsdb/tsdbRead.c b/source/dnode/vnode/src/tsdb/tsdbRead.c index 52e6fe6312..c02cff3aa9 100644 --- a/source/dnode/vnode/src/tsdb/tsdbRead.c +++ b/source/dnode/vnode/src/tsdb/tsdbRead.c @@ -5608,4 +5608,4 @@ void tsdbReaderSetId(STsdbReader* pReader, const char* idstr) { pReader->idStr = taosStrdup(idstr); } -void tsdbReaderSetCloseFlag(STsdbReader* pReader) { pReader->code = TSDB_CODE_TSC_QUERY_CANCELLED; } +void tsdbReaderSetCloseFlag(STsdbReader* pReader) { /*pReader->code = TSDB_CODE_TSC_QUERY_CANCELLED;*/ }