mirror of
https://github.com/taosdata/TDengine
synced 2026-05-24 10:09:01 +00:00
* feat: add xnode syntax * refactor(xnode): reduce function complexity * chore: add lost xnode.h file * feat(xnode): create xnode task * chore: fix double free error * add xnoded * start xnoded as subprocess * complete xnode task feature * complete show xnode jobs feature * complete with option feature * complete alter xnode job feature * complete alter xnode task feature * complete user pass feature * clean code * modify status type as char * fix leader ep null * fix start task req null * fix pass id for status * support timeout msg * drop xnode task relative jobs * clean code * wip * chore: add test cases for xnode * chore: fix 3.0 merge changes * fix drain core dump and create task core dump * add password check * retrieve xnode status from xnoded * pass integer as double to cjson * add some debug log * add some job log * fix start task lock * do not handle http response * fix coredump drop xnode task by name * support start/stop/drop task by name * remove mock xnoded * support unix socket * kill pre-xnoded before start * support dnode close xnoded * test(xnode): add unit test cases for xnode * rebalance support where clause * fix some test issue * unformat http post content json string * add xnode zh doc * modify drain description * remove job create/stop/drop operation * support rebalance all without where condition * support alter task by name * add NULL param for mndCheckOperPrivilege * add xnode txnode module for libmnode.a * code clean * change parser len to 4096 * clean code * chore: try to fix gtest/gtest.h not found * chore: fix markdown files * chore: fix markdown in zh * chore: fix enum issue and add ci * chore: fix test case problem * chore: fix pKeyVal overflow * chore: rename to 排空节点 * chore: external cmake remove parallel * chore: add DEP_ext_gtest for xnode test * chore: fix gtest errors * chore: remove gtest pthread lib * chore: fix data type not match * chore: fix some lint errors * chore: fix void unlink * chore: fix return with null pointer check * chore: fix pointer double free and xnodeMemoryTest strncpy null * chore: fix xnode encode action invalid datelen * chore: remove TD_LINUX condition * chore: use PRIu64 denote long long * chore: fix task parser NULL and allow no with clause * fix(xnode): fix windows build error * chore: fix windows curl error * chore: fix test case ins_tables relative error * chore: fix memory leak * docs: update taosx docs * chore: update taosx docs * chore: add role priviledge table * chore: fix error code doc * chore: fix test_xnode.py * chore: fix doc typo * fix: ci error while run test_user_privilege_sysinfo.py --------- Co-authored-by: Linhe Huo <linhehuo@gmail.com> Co-authored-by: huohong <sallyhuo@taosdata.com> Co-authored-by: Simon Guan <guanshengliang@qq.com>
362 lines
8.3 KiB
C++
362 lines
8.3 KiB
C++
/**
|
|
* @file xnodeMemoryTest.cpp
|
|
* @brief XNode memory management unit tests
|
|
* @version 1.0
|
|
* @date 2025-12-25
|
|
*/
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <vector>
|
|
|
|
// Test structure
|
|
// typedef struct SXnodeObj {
|
|
// int32_t id;
|
|
// int32_t urlLen;
|
|
// char* url;
|
|
// int32_t statusLen;
|
|
// char* status;
|
|
// int64_t createTime;
|
|
// int64_t updateTime;
|
|
// } SXnodeObj;
|
|
|
|
// typedef struct SXnodeTaskObj {
|
|
// int32_t tid;
|
|
// char name[64];
|
|
// int32_t xnodeId;
|
|
// int32_t agentId;
|
|
// int32_t optionsLen;
|
|
// char* options;
|
|
// int64_t createTime;
|
|
// int64_t updateTime;
|
|
// } SXnodeTaskObj;
|
|
|
|
extern "C" {
|
|
#include "mndDef.h"
|
|
}
|
|
|
|
class XnodeMemoryTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
allocCount = 0;
|
|
freeCount = 0;
|
|
}
|
|
|
|
void TearDown() override {
|
|
// Verify no memory leaks
|
|
EXPECT_EQ(allocCount, freeCount) << "Memory leak detected!";
|
|
}
|
|
|
|
int allocCount;
|
|
int freeCount;
|
|
|
|
SXnodeObj* createXnode(int id, const char* url, const char* status) {
|
|
SXnodeObj* obj = (SXnodeObj*)taosMemMalloc(sizeof(SXnodeObj));
|
|
allocCount++;
|
|
|
|
memset(obj, 0, sizeof(SXnodeObj));
|
|
obj->id = id;
|
|
|
|
if (url) {
|
|
obj->urlLen = strlen(url) + 1;
|
|
obj->url = (char*)taosMemMalloc(obj->urlLen);
|
|
allocCount++;
|
|
strcpy(obj->url, url);
|
|
}
|
|
|
|
if (status) {
|
|
obj->statusLen = strlen(status) + 1;
|
|
obj->status = (char*)taosMemMalloc(obj->statusLen);
|
|
allocCount++;
|
|
strcpy(obj->status, status);
|
|
}
|
|
|
|
obj->createTime = 1000000;
|
|
obj->updateTime = 2000000;
|
|
|
|
return obj;
|
|
}
|
|
|
|
void freeXnode(SXnodeObj* obj) {
|
|
if (!obj) return;
|
|
|
|
if (obj->url) {
|
|
taosMemFree(obj->url);
|
|
}
|
|
|
|
if (obj->status) {
|
|
taosMemFree(obj->status);
|
|
}
|
|
|
|
taosMemFree(obj);
|
|
freeCount++;
|
|
}
|
|
|
|
SXnodeTaskObj* createTask(int tid, const char* name, int xnodeId) {
|
|
SXnodeTaskObj* task = (SXnodeTaskObj*)taosMemMalloc(sizeof(SXnodeTaskObj));
|
|
allocCount++;
|
|
memset(task, 0, sizeof(SXnodeTaskObj));
|
|
task->id = tid;
|
|
task->xnodeId = xnodeId;
|
|
task->via = -1;
|
|
task->name = (char*)taosMemCalloc(1, strlen(name) + 1);
|
|
allocCount++;
|
|
|
|
if (name) {
|
|
strncpy(task->name, name, sizeof(task->name) - 1);
|
|
}
|
|
|
|
return task;
|
|
}
|
|
|
|
void freeTask(SXnodeTaskObj* task) {
|
|
if (!task) return;
|
|
|
|
if (task->name) {
|
|
taosMemFree(task->name);
|
|
freeCount++;
|
|
}
|
|
if (task->sourceDsn) {
|
|
taosMemFree(task->sourceDsn);
|
|
freeCount++;
|
|
}
|
|
if (task->sinkDsn) {
|
|
taosMemFree(task->sinkDsn);
|
|
freeCount++;
|
|
}
|
|
if (task->parser) {
|
|
taosMemFree(task->parser);
|
|
freeCount++;
|
|
}
|
|
if (task->status) {
|
|
taosMemFree(task->status);
|
|
freeCount++;
|
|
}
|
|
if (task->reason) {
|
|
taosMemFree(task->reason);
|
|
freeCount++;
|
|
}
|
|
taosMemFree(task);
|
|
freeCount++;
|
|
}
|
|
};
|
|
|
|
TEST_F(XnodeMemoryTest, SingleObjectAllocFree) {
|
|
SXnodeObj* obj = createXnode(1, "node1:6050", "online");
|
|
ASSERT_NE(obj, nullptr);
|
|
EXPECT_EQ(obj->id, 1);
|
|
EXPECT_STREQ(obj->url, "node1:6050");
|
|
EXPECT_STREQ(obj->status, "online");
|
|
|
|
freeXnode(obj);
|
|
// Leak check in TearDown
|
|
}
|
|
|
|
TEST_F(XnodeMemoryTest, MultipleObjectsAllocFree) {
|
|
std::vector<SXnodeObj*> objects;
|
|
|
|
// Create 100 objects
|
|
for (int i = 0; i < 100; i++) {
|
|
char url[32];
|
|
snprintf(url, sizeof(url), "node%d:6050", i);
|
|
SXnodeObj* obj = createXnode(i, url, i % 2 ? "online" : "offline");
|
|
objects.push_back(obj);
|
|
}
|
|
|
|
EXPECT_EQ(objects.size(), 100);
|
|
|
|
// Free all objects
|
|
for (auto obj : objects) {
|
|
freeXnode(obj);
|
|
}
|
|
|
|
objects.clear();
|
|
}
|
|
|
|
TEST_F(XnodeMemoryTest, NullFieldsHandling) {
|
|
// Create object with null fields
|
|
SXnodeObj* obj = createXnode(1, nullptr, nullptr);
|
|
ASSERT_NE(obj, nullptr);
|
|
EXPECT_EQ(obj->urlLen, 0);
|
|
EXPECT_EQ(obj->statusLen, 0);
|
|
EXPECT_EQ(obj->url, nullptr);
|
|
EXPECT_EQ(obj->status, nullptr);
|
|
|
|
freeXnode(obj);
|
|
}
|
|
|
|
TEST_F(XnodeMemoryTest, PartialFieldsHandling) {
|
|
// Only URL, no status
|
|
SXnodeObj* obj1 = createXnode(1, "node1:6050", nullptr);
|
|
ASSERT_NE(obj1, nullptr);
|
|
EXPECT_NE(obj1->url, nullptr);
|
|
EXPECT_EQ(obj1->status, nullptr);
|
|
freeXnode(obj1);
|
|
|
|
// Only status, no URL
|
|
SXnodeObj* obj2 = createXnode(2, nullptr, "online");
|
|
ASSERT_NE(obj2, nullptr);
|
|
EXPECT_EQ(obj2->url, nullptr);
|
|
EXPECT_NE(obj2->status, nullptr);
|
|
freeXnode(obj2);
|
|
}
|
|
|
|
TEST_F(XnodeMemoryTest, TaskObjectMemory) {
|
|
SXnodeTaskObj* task = createTask(100, "test_task", 1);
|
|
ASSERT_NE(task, nullptr);
|
|
EXPECT_EQ(task->id, 100);
|
|
EXPECT_STREQ(task->name, "test_task");
|
|
EXPECT_EQ(task->xnodeId, 1);
|
|
|
|
// Add options
|
|
const char* opts = "key1=value1,key2=value2";
|
|
// task->optionsLen = strlen(opts) + 1;
|
|
// task->options = (char*)taosMemMalloc(task->optionsLen);
|
|
allocCount++;
|
|
// strcpy(task->options, opts);
|
|
|
|
// EXPECT_STREQ(task->options, opts);
|
|
|
|
freeTask(task);
|
|
}
|
|
|
|
TEST_F(XnodeMemoryTest, MultipleTasksMemory) {
|
|
std::vector<SXnodeTaskObj*> tasks;
|
|
|
|
for (int i = 0; i < 50; i++) {
|
|
char name[64];
|
|
snprintf(name, sizeof(name), "task_%d", i);
|
|
SXnodeTaskObj* task = createTask(i, name, i % 5);
|
|
tasks.push_back(task);
|
|
}
|
|
|
|
EXPECT_EQ(tasks.size(), 50);
|
|
|
|
// Free all tasks
|
|
for (auto task : tasks) {
|
|
freeTask(task);
|
|
}
|
|
|
|
tasks.clear();
|
|
}
|
|
|
|
TEST_F(XnodeMemoryTest, LargeStringFields) {
|
|
// Create object with large strings
|
|
char largeUrl[1024];
|
|
char largeStatus[512];
|
|
|
|
memset(largeUrl, 'A', sizeof(largeUrl) - 1);
|
|
largeUrl[sizeof(largeUrl) - 1] = '\0';
|
|
|
|
memset(largeStatus, 'S', sizeof(largeStatus) - 1);
|
|
largeStatus[sizeof(largeStatus) - 1] = '\0';
|
|
|
|
SXnodeObj* obj = createXnode(1, largeUrl, largeStatus);
|
|
ASSERT_NE(obj, nullptr);
|
|
EXPECT_EQ(obj->urlLen, sizeof(largeUrl));
|
|
EXPECT_EQ(obj->statusLen, sizeof(largeStatus));
|
|
EXPECT_STREQ(obj->url, largeUrl);
|
|
EXPECT_STREQ(obj->status, largeStatus);
|
|
|
|
freeXnode(obj);
|
|
}
|
|
|
|
TEST_F(XnodeMemoryTest, ZeroLengthStrings) {
|
|
// Empty strings (not null)
|
|
SXnodeObj* obj = createXnode(1, "", "");
|
|
ASSERT_NE(obj, nullptr);
|
|
EXPECT_EQ(obj->urlLen, 1); // '\0' only
|
|
EXPECT_EQ(obj->statusLen, 1);
|
|
EXPECT_STREQ(obj->url, "");
|
|
EXPECT_STREQ(obj->status, "");
|
|
|
|
freeXnode(obj);
|
|
}
|
|
|
|
TEST_F(XnodeMemoryTest, RapidAllocFree) {
|
|
// Test rapid allocation and deallocation
|
|
for (int i = 0; i < 1000; i++) {
|
|
SXnodeObj* obj = createXnode(i, "node:6050", "online");
|
|
ASSERT_NE(obj, nullptr);
|
|
freeXnode(obj);
|
|
}
|
|
|
|
// All should be properly freed (checked in TearDown)
|
|
}
|
|
|
|
TEST_F(XnodeMemoryTest, MixedObjectTypes) {
|
|
// Mix XNode and Task objects
|
|
SXnodeObj* xnode = createXnode(1, "node1:6050", "online");
|
|
SXnodeTaskObj* task1 = createTask(100, "task1", 1);
|
|
SXnodeTaskObj* task2 = createTask(101, "task2", 1);
|
|
SXnodeObj* xnode2 = createXnode(2, "node2:6051", "offline");
|
|
|
|
ASSERT_NE(xnode, nullptr);
|
|
ASSERT_NE(task1, nullptr);
|
|
ASSERT_NE(task2, nullptr);
|
|
ASSERT_NE(xnode2, nullptr);
|
|
|
|
// Free in different order
|
|
freeTask(task1);
|
|
freeXnode(xnode);
|
|
freeTask(task2);
|
|
freeXnode(xnode2);
|
|
}
|
|
|
|
TEST_F(XnodeMemoryTest, DoubleFreePrevention) {
|
|
SXnodeObj* obj = createXnode(1, "node1:6050", "online");
|
|
ASSERT_NE(obj, nullptr);
|
|
|
|
freeXnode(obj);
|
|
|
|
// Second free with nullptr should be safe
|
|
freeXnode(nullptr);
|
|
|
|
// This is just to demonstrate safe null handling
|
|
// In real code, we should not double-free
|
|
}
|
|
|
|
TEST_F(XnodeMemoryTest, StressTest) {
|
|
// Stress test: create and free many objects in random order
|
|
std::vector<SXnodeObj*> objects;
|
|
|
|
// Create 500 objects
|
|
for (int i = 0; i < 500; i++) {
|
|
char url[32];
|
|
snprintf(url, sizeof(url), "node%d:%d", i, 6050 + (i % 10));
|
|
SXnodeObj* obj = createXnode(i, url, i % 3 ? "online" : "offline");
|
|
objects.push_back(obj);
|
|
}
|
|
|
|
// Free every other object
|
|
for (size_t i = 0; i < objects.size(); i += 2) {
|
|
freeXnode(objects[i]);
|
|
objects[i] = nullptr;
|
|
}
|
|
|
|
// Free remaining objects
|
|
for (auto obj : objects) {
|
|
if (obj) {
|
|
freeXnode(obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(XnodeMemoryTest, BoundaryValues) {
|
|
// Test with boundary integer values
|
|
SXnodeObj* obj = createXnode(INT32_MAX, "max:6050", "status");
|
|
ASSERT_NE(obj, nullptr);
|
|
EXPECT_EQ(obj->id, INT32_MAX);
|
|
freeXnode(obj);
|
|
|
|
obj = createXnode(INT32_MIN, "min:6050", "status");
|
|
ASSERT_NE(obj, nullptr);
|
|
EXPECT_EQ(obj->id, INT32_MIN);
|
|
freeXnode(obj);
|
|
|
|
obj = createXnode(0, "zero:6050", "status");
|
|
ASSERT_NE(obj, nullptr);
|
|
EXPECT_EQ(obj->id, 0);
|
|
freeXnode(obj);
|
|
}
|