oceanbase/unittest/libobcdc/test_log_trans_ctx_mgr.cpp
wangzelin.wzl 93a1074b0c patch 4.0
2022-10-24 17:57:12 +08:00

358 lines
12 KiB
C++

/**
* Copyright (c) 2021 OceanBase
* OceanBase CE is licensed under Mulan PubL v2.
* You can use this software according to the terms and conditions of the Mulan PubL v2.
* You may obtain a copy of Mulan PubL v2 at:
* http://license.coscl.org.cn/MulanPubL-2.0
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PubL v2 for more details.
*/
#include <gtest/gtest.h>
#include "ob_log_trans_ctx_mgr.h" // ObLogTransCtxMgr
#include "common/ob_clock_generator.h" // ObClockGenerator
using namespace oceanbase::common;
using namespace oceanbase::libobcdc;
using namespace oceanbase::transaction;
class ObLogTransCtxMgrTest : public ::testing::Test
{
public:
static const int64_t SLEEP_TIME = 10000;
static const int64_t THREAD_NUM = 10;
static const int64_t RUN_TIME_SEC = 60;
static const int64_t CACHED_CTX_COUNT = 10000;
static const int64_t TEST_CTX_COUNT = CACHED_CTX_COUNT + 1024;
public:
ObLogTransCtxMgrTest();
virtual ~ObLogTransCtxMgrTest();
virtual void SetUp();
virtual void TearDown();
static void *thread_func(void *args);
public:
void run();
void test_imediately_remove();
void test_dely_remove();
public:
int32_t port_;
ObTransID *trans_ids_;
pthread_t threads_[THREAD_NUM];
ObLogTransCtxMgr mgr_;
private:
// disallow copy
DISALLOW_COPY_AND_ASSIGN(ObLogTransCtxMgrTest);
};
ObLogTransCtxMgrTest::ObLogTransCtxMgrTest() : port_(0), trans_ids_(NULL), mgr_()
{
}
ObLogTransCtxMgrTest::~ObLogTransCtxMgrTest()
{
}
void ObLogTransCtxMgrTest::SetUp()
{
}
void ObLogTransCtxMgrTest::TearDown()
{
}
TEST_F(ObLogTransCtxMgrTest, DISABLED_single_thread_immediately_remove)
{
ObLogTransCtxMgr trans_ctx_mgr;
EXPECT_NE(OB_SUCCESS, trans_ctx_mgr.init(0));
EXPECT_NE(OB_SUCCESS, trans_ctx_mgr.init(-1));
EXPECT_EQ(OB_SUCCESS, trans_ctx_mgr.init(CACHED_CTX_COUNT));
EXPECT_EQ(0, trans_ctx_mgr.get_valid_trans_ctx_count());
EXPECT_EQ(0, trans_ctx_mgr.get_alloc_trans_ctx_count());
EXPECT_EQ(0, trans_ctx_mgr.get_free_trans_ctx_count());
// Up to two transaction context objects are allocated at the same time when used by a single thread following the "allocate-return-release" process.
// One of them is not deleted from the cache. The logic is verified below.
int64_t free_count = 0;
int64_t alloc_count = 0;
for (int64_t index = 0; index < TEST_CTX_COUNT; index++) {
ObAddr svr(ObAddr::IPV4, "127.0.0.1", 1 + (int32_t)index);
ObTransID trans_id;
// At the beginning, the effective number is 0
EXPECT_EQ(0, trans_ctx_mgr.get_valid_trans_ctx_count());
free_count = index <= 1 ? 0 : 1;
alloc_count = index <= 2 ? index : 2;
EXPECT_EQ(free_count, trans_ctx_mgr.get_free_trans_ctx_count());
EXPECT_EQ(alloc_count, trans_ctx_mgr.get_alloc_trans_ctx_count());
// get with a not-exist trans_id
TransCtx *ctx1 = NULL;
EXPECT_EQ(OB_ENTRY_NOT_EXIST, trans_ctx_mgr.get_trans_ctx(trans_id, ctx1));
// create when get a not-exist trans_id
bool enable_create = true;
EXPECT_EQ(OB_SUCCESS, trans_ctx_mgr.get_trans_ctx(trans_id, ctx1, enable_create));
EXPECT_TRUE(NULL != ctx1);
// get trans_ctx that create just now
TransCtx *ctx1_get = NULL;
EXPECT_EQ(OB_SUCCESS, trans_ctx_mgr.get_trans_ctx(trans_id, ctx1_get));
EXPECT_TRUE(ctx1 == ctx1_get);
// Valid quantity is 1
EXPECT_EQ(1, trans_ctx_mgr.get_valid_trans_ctx_count());
// Idle transaction context object used, idle becomes 0, allocated to a maximum of 2
free_count = 0;
alloc_count = index <= 0 ? 1 : 2;
EXPECT_EQ(free_count, trans_ctx_mgr.get_free_trans_ctx_count());
EXPECT_EQ(alloc_count, trans_ctx_mgr.get_alloc_trans_ctx_count());
// revert the trans_ctx
EXPECT_EQ(OB_SUCCESS, trans_ctx_mgr.revert_trans_ctx(ctx1));
// A revert before a remove does not affect the number of objects
EXPECT_EQ(1, trans_ctx_mgr.get_valid_trans_ctx_count());
free_count = 0;
alloc_count = index <= 0 ? 1 : 2;
EXPECT_EQ(free_count, trans_ctx_mgr.get_free_trans_ctx_count());
EXPECT_EQ(alloc_count, trans_ctx_mgr.get_alloc_trans_ctx_count());
// remove
EXPECT_EQ(OB_SUCCESS, trans_ctx_mgr.remove_trans_ctx(trans_id));
EXPECT_EQ(OB_ENTRY_NOT_EXIST, trans_ctx_mgr.remove_trans_ctx(trans_id));
// After deletion, the effective number becomes 0
EXPECT_EQ(0, trans_ctx_mgr.get_valid_trans_ctx_count());
// After deletion, the object just deleted is not released immediately, but the last deleted object is released
// So after the second time, the number of free objects becomes 1, but the number of allocated objects remains the same
free_count = index <= 0 ? 0 : 1;
alloc_count = index <= 0 ? 1 : 2;
EXPECT_EQ(free_count, trans_ctx_mgr.get_free_trans_ctx_count());
EXPECT_EQ(alloc_count, trans_ctx_mgr.get_alloc_trans_ctx_count());
// revert the last one
EXPECT_EQ(OB_SUCCESS, trans_ctx_mgr.revert_trans_ctx(ctx1_get));
// When all returned, the valid number remains 0
EXPECT_EQ(0, trans_ctx_mgr.get_valid_trans_ctx_count());
// Even after swapping them all back, the object just deleted is not immediately released until the next time it is deleted
free_count = index <= 0 ? 0 : 1;
alloc_count = index <= 0 ? 1 : 2;
EXPECT_EQ(free_count, trans_ctx_mgr.get_free_trans_ctx_count());
EXPECT_EQ(alloc_count, trans_ctx_mgr.get_alloc_trans_ctx_count());
}
}
TEST_F(ObLogTransCtxMgrTest, DISABLED_single_thread_delay_remove)
{
ObLogTransCtxMgr trans_ctx_mgr;
ObTransID *tids = (ObTransID *)ob_malloc(sizeof(ObTransID) * TEST_CTX_COUNT);
TransCtx *tctxs_[TEST_CTX_COUNT];
(void)memset(tctxs_, 0, sizeof(tctxs_));
ASSERT_TRUE(NULL != tids);
EXPECT_EQ(OB_SUCCESS, trans_ctx_mgr.init(CACHED_CTX_COUNT));
for (int64_t index = 0; index < TEST_CTX_COUNT; index++) {
new(tids + index) ObTransID();
ObTransID &trans_id = tids[index];
// get with a not-exist trans_id
TransCtx *ctx1 = NULL;
EXPECT_EQ(OB_ENTRY_NOT_EXIST, trans_ctx_mgr.get_trans_ctx(trans_id, ctx1));
// create when get a not-exist trans_id
bool enable_create = true;
EXPECT_EQ(OB_SUCCESS, trans_ctx_mgr.get_trans_ctx(trans_id, ctx1, enable_create));
EXPECT_TRUE(NULL != ctx1);
// revert the trans_ctx
EXPECT_EQ(OB_SUCCESS, trans_ctx_mgr.revert_trans_ctx(ctx1));
}
EXPECT_EQ(TEST_CTX_COUNT + 0, trans_ctx_mgr.get_valid_trans_ctx_count());
EXPECT_EQ(TEST_CTX_COUNT + 0, trans_ctx_mgr.get_alloc_trans_ctx_count());
EXPECT_EQ(0, trans_ctx_mgr.get_free_trans_ctx_count());
int64_t REMOVE_INTERVAL_COUNT = 10;
for (int64_t index = 0; index < TEST_CTX_COUNT; index++) {
ObTransID &trans_id = tids[index];
EXPECT_EQ(OB_SUCCESS, trans_ctx_mgr.get_trans_ctx(trans_id, tctxs_[index]));
EXPECT_TRUE(NULL != tctxs_[index]);
EXPECT_EQ(OB_SUCCESS, trans_ctx_mgr.remove_trans_ctx(trans_id));
EXPECT_EQ(TEST_CTX_COUNT - index - 1, trans_ctx_mgr.get_valid_trans_ctx_count());
// revert REMOVE_INTERVAL_COUNT the object you got before, so that it is actually deleted on the next remove
if (index >= REMOVE_INTERVAL_COUNT) {
int64_t revert_index = index - REMOVE_INTERVAL_COUNT;
// After revert, the next remove will delete
EXPECT_EQ(OB_SUCCESS, trans_ctx_mgr.revert_trans_ctx(tctxs_[revert_index]));
tctxs_[revert_index] = NULL;
static int64_t alloc_count = TEST_CTX_COUNT;
static int64_t free_count = 0;
// The alloc_count is only decremented when a second non-cached object is deleted
if (revert_index > CACHED_CTX_COUNT) {
alloc_count--;
} else if (revert_index > 0) {
// The free_count is incremented when the cached transaction context object is deleted
free_count++;
}
EXPECT_EQ(alloc_count, trans_ctx_mgr.get_alloc_trans_ctx_count());
EXPECT_EQ(free_count, trans_ctx_mgr.get_free_trans_ctx_count());
}
}
EXPECT_EQ(CACHED_CTX_COUNT + REMOVE_INTERVAL_COUNT + 1, trans_ctx_mgr.get_alloc_trans_ctx_count());
EXPECT_EQ(CACHED_CTX_COUNT + 0, trans_ctx_mgr.get_free_trans_ctx_count());
ob_free((void *)tids);
tids = NULL;
}
TEST_F(ObLogTransCtxMgrTest, multiple_thread)
{
EXPECT_EQ(OB_SUCCESS, mgr_.init(CACHED_CTX_COUNT));
OB_ASSERT(NULL == trans_ids_);
trans_ids_ = (ObTransID *)ob_malloc(sizeof(ObTransID) * TEST_CTX_COUNT);
ASSERT_TRUE(NULL != trans_ids_);
for (int64_t index = 0; index < TEST_CTX_COUNT; index++) {
new(trans_ids_ + index) ObTransID();
}
for (int64_t index = 0; index < THREAD_NUM; index++) {
ASSERT_EQ(0, pthread_create(threads_ + index, NULL, thread_func, this));
}
for (int64_t index = 0; index < THREAD_NUM; index++) {
if (0 != threads_[index]) {
pthread_join(threads_[index], NULL);
threads_[index] = 0;
}
}
for (int64_t index = 0; index < TEST_CTX_COUNT; index++) {
trans_ids_[index].~ObTransID();
}
ob_free(trans_ids_);
trans_ids_ = NULL;
}
void *ObLogTransCtxMgrTest::thread_func(void *args)
{
if (NULL != args) {
((ObLogTransCtxMgrTest *)args)->run();
}
return NULL;
}
void ObLogTransCtxMgrTest::run()
{
int64_t end_time = ObTimeUtility::current_time() + RUN_TIME_SEC * 1000000;
while (true) {
test_imediately_remove();
test_dely_remove();
int64_t left_time = end_time - ObTimeUtility::current_time();
if (left_time <= 0) break;
}
}
void ObLogTransCtxMgrTest::test_imediately_remove()
{
ObAddr svr(ObAddr::IPV4, "127.0.0.1", ATOMIC_AAF(&port_, 1));
ObTransID trans_id; // Although the svr is the same, the internal inc will be self-increasing
// get with a not-exist trans_id
TransCtx *ctx1 = NULL;
EXPECT_EQ(OB_ENTRY_NOT_EXIST, mgr_.get_trans_ctx(trans_id, ctx1));
// create when get a not-exist trans_id
bool enable_create = true;
EXPECT_EQ(OB_SUCCESS, mgr_.get_trans_ctx(trans_id, ctx1, enable_create));
EXPECT_TRUE(NULL != ctx1);
// get trans_ctx that create just now
TransCtx *ctx1_get = NULL;
EXPECT_EQ(OB_SUCCESS, mgr_.get_trans_ctx(trans_id, ctx1_get));
EXPECT_TRUE(ctx1 == ctx1_get);
// revert the trans_ctx
EXPECT_EQ(OB_SUCCESS, mgr_.revert_trans_ctx(ctx1));
usleep((useconds_t)random() % SLEEP_TIME);
// remove
EXPECT_EQ(OB_SUCCESS, mgr_.remove_trans_ctx(trans_id));
EXPECT_EQ(OB_ENTRY_NOT_EXIST, mgr_.remove_trans_ctx(trans_id));
// Return to the last acquired
EXPECT_EQ(OB_SUCCESS, mgr_.revert_trans_ctx(ctx1_get));
}
void ObLogTransCtxMgrTest::test_dely_remove()
{
for (int64_t index = 0; index < TEST_CTX_COUNT; index++) {
ObTransID &trans_id = trans_ids_[random() % TEST_CTX_COUNT];
TransCtx *ctx = NULL;
bool enable_create = true;
EXPECT_EQ(OB_SUCCESS, mgr_.get_trans_ctx(trans_id, ctx, enable_create));
EXPECT_TRUE(NULL != ctx);
usleep((useconds_t)random() % SLEEP_TIME);
EXPECT_EQ(OB_SUCCESS, mgr_.revert_trans_ctx(ctx));
}
for (int64_t index = 0; index < TEST_CTX_COUNT; index++) {
ObTransID &trans_id = trans_ids_[random() % TEST_CTX_COUNT];
TransCtx *ctx = NULL;
int ret = mgr_.get_trans_ctx(trans_id, ctx);
if (OB_SUCC(ret)) {
EXPECT_TRUE(NULL != ctx);
ret = mgr_.remove_trans_ctx(trans_id);
EXPECT_TRUE(OB_SUCCESS == ret || OB_ENTRY_NOT_EXIST == ret);
usleep((useconds_t)random() % SLEEP_TIME);
EXPECT_EQ(OB_SUCCESS, mgr_.revert_trans_ctx(ctx));
}
}
}
int main(int argc, char **argv)
{
// Used for initialization of ObTransID
ObClockGenerator::init();
srandom((unsigned)ObTimeUtility::current_time());
OB_LOGGER.set_log_level("INFO");
::testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}