358 lines
12 KiB
C++
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();
|
|
}
|