[FEAT MERGE] merge transfer

Co-authored-by: wxhwang <wxhwang@126.com>
Co-authored-by: godyangfight <godyangfight@gmail.com>
Co-authored-by: Tyshawn <tuyunshan@gmail.com>
This commit is contained in:
xuhuleon
2023-06-21 11:42:26 +00:00
committed by ob-robot
parent d06678002e
commit 9dae112952
1280 changed files with 149724 additions and 48813 deletions

View File

@ -0,0 +1,13 @@
#ifndef UNITTEST_STORAGE_MULTI_DATA_SOURCE_COMMON_DEFINE_H
#define UNITTEST_STORAGE_MULTI_DATA_SOURCE_COMMON_DEFINE_H
#include "src/share/scn.h"
#include "example_user_data_define.h"
namespace oceanbase {
namespace unittest {
inline share::SCN mock_scn(int64_t val) { share::SCN scn; scn.convert_for_gts(val); return scn; }
}
}
#endif

View File

@ -0,0 +1,129 @@
#ifndef UNITTEST_SHARE_MULTI_DATA_SOURCE_EXAMPLE_USER_DATA_DEFINE_H
#define UNITTEST_SHARE_MULTI_DATA_SOURCE_EXAMPLE_USER_DATA_DEFINE_H
#include "lib/ob_errno.h"
#include "lib/utility/ob_print_utils.h"
#include "lib/utility/ob_unify_serialize.h"
#include "lib/utility/serialization.h"
#include "meta_programming/ob_meta_serialization.h"
#include "common_define.h"
namespace oceanbase {
namespace unittest {
struct ExampleUserKey {
OB_UNIS_VERSION(1);
public:
ExampleUserKey() : value_(0) {}
ExampleUserKey(const int val) : value_(val) {}
TO_STRING_KV(K_(value));
bool operator<(const ExampleUserKey &rhs) const { return value_ < rhs.value_; }
bool operator==(const ExampleUserKey &rhs) const { return value_ == rhs.value_; }
int64_t value_;
};
OB_SERIALIZE_MEMBER_TEMP(inline, ExampleUserKey, value_);
struct ExampleUserData1 {// simple data structure
OB_UNIS_VERSION(1);
public:
bool operator==(const ExampleUserData1 &rhs) const { return value_ == rhs.value_; }
ExampleUserData1() : value_(0) {}
ExampleUserData1(const int val) : value_(val) {}
TO_STRING_KV(K_(value));
int value_;
};
OB_SERIALIZE_MEMBER_TEMP(inline, ExampleUserData1, value_);
struct ExampleUserData2 {// complicated data structure
public:
ExampleUserData2() : alloc_(nullptr) {}
~ExampleUserData2() {
if (OB_NOT_NULL(alloc_)) {
if (!data_.empty()) {
alloc_->free(data_.ptr());
}
}
}
int serialize(char *buf, const int64_t buf_len, int64_t &pos) const {
int ret = OB_SUCCESS;
if (OB_ISNULL(alloc_)) {
} else if (OB_FAIL(serialization::encode(buf, buf_len, pos, (int64_t)data_.length()))) {
} else {
for (int64_t idx = 0; idx < data_.length() && OB_SUCC(ret); ++idx) {
ret = serialization::encode(buf, buf_len, pos, data_.ptr()[idx]);
}
}
return ret;
}
int deserialize(ObIAllocator &alloc, const char *buf, const int64_t buf_len, int64_t &pos) {
int ret = OB_SUCCESS;
int64_t length = 0;
char *buffer = nullptr;
if (OB_FAIL(serialization::decode(buf, buf_len, pos, length))) {
} else if (OB_ISNULL(buffer = (char *)alloc.alloc(length))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
} else {
for (int64_t idx = 0; idx < length && OB_SUCC(ret); ++idx) {
ret = serialization::decode(buf, buf_len, pos, buffer[idx]);
}
if (OB_SUCC(ret)) {
data_.assign(buffer, length);
alloc_ = &alloc;
}
}
return ret;
}
int64_t get_serialize_size() const {
int64_t total_size = 0;
total_size += serialization::encoded_length((int64_t)data_.length());
for (int64_t idx = 0; idx < data_.length(); ++idx) {
total_size += serialization::encoded_length(data_.ptr()[idx]);
}
return total_size;
}
int assign(ObIAllocator &alloc, const char *str) {
int ret = OB_SUCCESS;
OB_ASSERT(OB_ISNULL(alloc_) && data_.empty());
int64_t len = strlen(str);
char *buffer = nullptr;
if (OB_ISNULL(buffer = (char *)alloc.alloc(len))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
} else {
memcpy(buffer, str, len);
alloc_ = &alloc;
data_.assign(buffer, len);
}
return ret;
}
int assign(ObIAllocator &alloc, const ExampleUserData2 &rhs) {
int ret = OB_SUCCESS;
if (OB_NOT_NULL(rhs.alloc_)) {
OB_ASSERT(OB_ISNULL(alloc_) && data_.empty());
int64_t len = rhs.data_.length();
char *buffer = nullptr;
if (OB_ISNULL(buffer = (char *)alloc.alloc(len))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
} else {
memcpy(buffer, rhs.data_.ptr(), len);
alloc_ = &alloc;
data_.assign(buffer, len);
}
}
return ret;
}
// support move semantic
ExampleUserData2(ExampleUserData2 &&rhs) {
if (OB_NOT_NULL(rhs.alloc_)) {
data_ = rhs.data_;
alloc_ = rhs.alloc_;
rhs.alloc_ = nullptr;
rhs.data_.reset();
MDS_LOG(INFO, "call move construction", K(*this));
}
}
TO_STRING_KV(KP(data_.ptr()), K(data_.length()));
ObIAllocator *alloc_;
ObString data_;
};
}
}
#endif

View File

@ -0,0 +1,123 @@
#ifdef TEST_MDS_TRANSACTION
#include "example_user_helper_define.h"
#include "storage/multi_data_source/mds_table_handle.h"
namespace oceanbase {
namespace unittest {
using namespace storage;
using namespace mds;
MdsTableHandle TestMdsTable;
const storage::mds::MdsWriter ExampleUserHelperCtx::get_writer() const
{
return storage::mds::MdsWriter(transaction::ObTransID(0));
}
int ExampleUserHelperFunction1::on_register(const char* buf,
const int64_t len,
storage::mds::BufferCtx &ctx)
{
int ret = OB_SUCCESS;
int64_t test_value;
int64_t pos = 0;
if (OB_FAIL(serialization::decode(buf, len, pos, test_value))) {
MDS_LOG(ERROR, "[UNITTEST] ExampleUserHelperFunction1 fail to deserialize", KR(ret));
} else {
MDS_LOG(INFO, "[UNITTEST] ExampleUserHelperFunction1 call on_register with helper", K(test_value));
}
return ret;
}
int ExampleUserHelperFunction1::on_replay(const char* buf,
const int64_t len,
const share::SCN &scn, // 日志scn
storage::mds::BufferCtx &ctx)
{
UNUSED(scn);
return on_register(buf, len, ctx);
}
int ExampleUserHelperFunction2::on_register(const char* buf,
const int64_t len,
storage::mds::BufferCtx &ctx)
{
int ret = OB_SUCCESS;
int64_t test_value;
int64_t pos = 0;
if (OB_FAIL(serialization::decode(buf, len, pos, test_value))) {
MDS_LOG(ERROR, "[UNITTEST] ExampleUserHelperFunction2 fail to deserialize", KR(ret));
} else {
ExampleUserData1 data(test_value);
MdsCtx &mds_ctx = dynamic_cast<MdsCtx &>(ctx);
if (OB_FAIL(TestMdsTable.set(data, mds_ctx))) {
MDS_LOG(ERROR, "[UNITTEST] ExampleUserHelperFunction2 fail to set mdstable", KR(ret));
} else {
MDS_LOG(INFO, "[UNITTEST] ExampleUserHelperFunction2 call on_register with helper", K(test_value));
}
}
return ret;
}
int ExampleUserHelperFunction2::on_replay(const char* buf,
const int64_t len,
const share::SCN &scn, // 日志scn
storage::mds::BufferCtx &ctx)
{
UNUSED(scn);
return on_register(buf, len, ctx);
}
int ExampleUserHelperFunction3::on_register(const char* buf,
const int64_t len,
storage::mds::BufferCtx &ctx)
{
int ret = OB_SUCCESS;
int64_t test_value;
int64_t pos = 0;
if (OB_FAIL(serialization::decode(buf, len, pos, test_value))) {
MDS_LOG(ERROR, "[UNITTEST] ExampleUserHelperFunction3 fail to deserialize", KR(ret));
} else {
MDS_LOG(INFO, "[UNITTEST] ExampleUserHelperFunction3 call on_register with helper", K(test_value));
}
return ret;
}
int ExampleUserHelperFunction3::on_replay(const char* buf,
const int64_t len,
const share::SCN &scn, // 日志scn
storage::mds::BufferCtx &ctx)
{
UNUSED(scn);
return on_register(buf, len, ctx);
}
void ExampleUserHelperCtx::on_redo(const share::SCN &)
{
MDS_LOG(INFO, "[UNITTEST] call on_redo with ctx", K(++call_times_));
}
void ExampleUserHelperCtx::before_prepare()
{
MDS_LOG(INFO, "[UNITTEST] call before_prepare with ctx", K(++call_times_));
}
void ExampleUserHelperCtx::on_prepare(const share::SCN &prepare_version)
{
MDS_LOG(INFO, "[UNITTEST] call on_prepare with ctx", K(++call_times_));
}
void ExampleUserHelperCtx::on_commit(const share::SCN &commit_version, const share::SCN &)
{
MDS_LOG(INFO, "[UNITTEST] call on_commit with ctx", K(++call_times_));
}
void ExampleUserHelperCtx::on_abort(const share::SCN &end_scn)
{
MDS_LOG(INFO, "[UNITTEST] call on_abort with ctx", K(++call_times_), K(end_scn));
}
}
}
#endif

View File

@ -0,0 +1,151 @@
#ifndef UNITTEST_SHARE_MULTI_DATA_SOURCE_EXAMPLE_USER_HELPER_DEFINE_H
#define UNITTEST_SHARE_MULTI_DATA_SOURCE_EXAMPLE_USER_HELPER_DEFINE_H
#include "storage/multi_data_source/buffer_ctx.h"
#include "lib/oblog/ob_log_module.h"
#include "lib/utility/serialization.h"
#include "share/scn.h"
#include "storage/multi_data_source/runtime_utility/common_define.h"
namespace oceanbase {
namespace unittest {
struct ExampleUserHelperFunction1 {
static int on_register(const char* buf,
const int64_t len,
storage::mds::BufferCtx &ctx); // 出参,将对应修改记录在Ctx中
static int on_replay(const char* buf,
const int64_t len,
const share::SCN &scn, // 日志scn
storage::mds::BufferCtx &ctx); // 备机回放
};
struct ExampleUserHelperFunction2 {
static int on_register(const char* buf,
const int64_t len,
storage::mds::BufferCtx &ctx); // 出参,将对应修改记录在Ctx中
static int on_replay(const char* buf,
const int64_t len,
const share::SCN &scn, // 日志scn
storage::mds::BufferCtx &ctx); // 备机回放
};
struct ExampleUserHelperFunction3 {
static int on_register(const char* buf,
const int64_t len,
storage::mds::BufferCtx &ctx); // 出参,将对应修改记录在Ctx中
static int on_replay(const char* buf,
const int64_t len,
const share::SCN &scn, // 日志scn
storage::mds::BufferCtx &ctx); // 备机回放
};
struct ExampleUserHelperCtx : public storage::mds::BufferCtx {
ExampleUserHelperCtx() : call_times_(0) {}
virtual const storage::mds::MdsWriter get_writer() const override;
virtual void on_redo(const share::SCN &redo_scn) override;
virtual void before_prepare() override;
virtual void on_prepare(const share::SCN &prepare_version) override;
virtual void on_commit(const share::SCN &commit_version, const share::SCN &scn) override;
virtual void on_abort(const share::SCN &scn) override;
int assign(const ExampleUserHelperCtx &rhs) {
call_times_ = rhs.call_times_;
return OB_SUCCESS;
}
int call_times_;// 这个类可以有自己的内部状态
virtual int64_t to_string(char*, const int64_t buf_len) const { return 0; }
// 同事务状态一起持久化以及恢复
virtual int serialize(char*, const int64_t, int64_t&) const { return OB_SUCCESS; }
virtual int deserialize(const char*, const int64_t, int64_t&) { return OB_SUCCESS; }
virtual int64_t get_serialize_size(void) const { return 0; }
};
#ifndef TEST_MDS_TRANSACTION
inline int ExampleUserHelperFunction1::on_register(const char* buf,
const int64_t len,
storage::mds::BufferCtx &ctx)
{
int ret = OB_SUCCESS;
return ret;
}
inline int ExampleUserHelperFunction1::on_replay(const char* buf,
const int64_t len,
const share::SCN &scn, // 日志scn
storage::mds::BufferCtx &ctx)
{
int ret = OB_SUCCESS;
return ret;
}
inline int ExampleUserHelperFunction2::on_register(const char*,
const int64_t,
storage::mds::BufferCtx &)
{
int ret = OB_SUCCESS;
return ret;
}
inline int ExampleUserHelperFunction2::on_replay(const char*,
const int64_t,
const share::SCN &, // 日志scn
storage::mds::BufferCtx &)
{
int ret = OB_SUCCESS;
return ret;
}
inline int ExampleUserHelperFunction3::on_register(const char*,
const int64_t,
storage::mds::BufferCtx &)
{
int ret = OB_SUCCESS;
return ret;
}
inline int ExampleUserHelperFunction3::on_replay(const char* buf,
const int64_t len,
const share::SCN &scn, // 日志scn
storage::mds::BufferCtx &ctx)
{
UNUSED(scn);
return on_register(buf, len, ctx);
}
inline const storage::mds::MdsWriter ExampleUserHelperCtx::get_writer() const
{
return storage::mds::MdsWriter(storage::mds::WriterType::TRANSACTION, 1);
}
inline void ExampleUserHelperCtx::on_redo(const share::SCN &redo_scn)
{
MDS_LOG(INFO, "[UNITTEST] call on_redo with ctx", K(++call_times_));
}
inline void ExampleUserHelperCtx::before_prepare()
{
MDS_LOG(INFO, "[UNITTEST] call before_prepare with ctx", K(++call_times_));
}
inline void ExampleUserHelperCtx::on_prepare(const share::SCN &prepare_version)
{
MDS_LOG(INFO, "[UNITTEST] call on_prepare with ctx", K(++call_times_));
}
inline void ExampleUserHelperCtx::on_commit(const share::SCN &commit_version, const share::SCN &scn)
{
MDS_LOG(INFO, "[UNITTEST] call on_commit with ctx", K(++call_times_));
}
inline void ExampleUserHelperCtx::on_abort(const share::SCN &scn)
{
MDS_LOG(INFO, "[UNITTEST] call on_abort with ctx", K(++call_times_));
}
#endif
}
}
#endif

View File

@ -0,0 +1,58 @@
/**
* 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.
*/
#define UNITTEST_DEBUG
#include <gtest/gtest.h>
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>
#include "common/ob_clock_generator.h"
#include "storage/multi_data_source/mds_table_handle.h"
#include "storage/multi_data_source/compile_utility/compile_mapper.h"
namespace oceanbase {
namespace unittest {
using namespace common;
using namespace std;
class TestMdsCompile: public ::testing::Test
{
public:
TestMdsCompile() {};
virtual ~TestMdsCompile() {};
virtual void SetUp() {
};
virtual void TearDown() {
};
private:
// disallow copy
DISALLOW_COPY_AND_ASSIGN(TestMdsCompile);
};
TEST_F(TestMdsCompile, basic) {
ObTuple<int, double, char> tuple_;
tuple_.element<int>() = 1;
}
}
}
int main(int argc, char **argv)
{
system("rm -rf test_ob_occam_timer.log");
oceanbase::common::ObLogger &logger = oceanbase::common::ObLogger::get_logger();
logger.set_file_name("test_ob_occam_timer.log", false);
logger.set_log_level(OB_LOG_LEVEL_DEBUG);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,153 @@
/**
* 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 "share/ob_ls_id.h"
#define UNITTEST_DEBUG
#include <gtest/gtest.h>
#define private public
#define protected public
#include "storage/multi_data_source/compile_utility/mds_dummy_key.h"
#include "storage/multi_data_source/compile_utility/map_type_index_in_tuple.h"
#include "common_define.h"
#include "storage/multi_data_source/adapter_define/mds_dump_node.h"
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>
#include "storage/multi_data_source/runtime_utility/mds_factory.h"
#include "common/ob_clock_generator.h"
#include "storage/multi_data_source/mds_node.h"
#include "example_user_helper_define.cpp"
#include "storage/multi_data_source/mds_table_handle.h"
#include "storage/tablet/ob_tablet_meta.h"
namespace oceanbase {
namespace storage
{
namespace mds
{
void *MdsAllocator::alloc(const int64_t size)
{
void *ptr = ob_malloc(size, "MDS");
ATOMIC_INC(&alloc_times_);
MDS_LOG(DEBUG, "alloc obj", KP(ptr), K(size), K(lbt()));
return ptr;
}
void MdsAllocator::free(void *ptr) {
ATOMIC_INC(&free_times_);
MDS_LOG(DEBUG, "free obj", KP(ptr), K(lbt()));
ob_free(ptr);
}
}
}
namespace unittest {
using namespace common;
using namespace std;
using namespace storage;
using namespace mds;
class TestMdsDumpKV: public ::testing::Test
{
public:
TestMdsDumpKV() {};
virtual ~TestMdsDumpKV() {};
virtual void SetUp() {
};
virtual void TearDown() {
};
static void test_dump_node_convert_and_serialize_and_compare();
static void test_dump_dummy_key();
private:
// disallow copy
DISALLOW_COPY_AND_ASSIGN(TestMdsDumpKV);
};
void TestMdsDumpKV::test_dump_node_convert_and_serialize_and_compare()
{
ExampleUserData2 first_data;
ASSERT_EQ(OB_SUCCESS, first_data.assign(MdsAllocator::get_instance(), "123"));
UserMdsNode<DummyKey, ExampleUserData2> user_mds_node(nullptr, MdsNodeType::SET, WriterType::TRANSACTION, 1);
ASSERT_EQ(OB_SUCCESS, meta::move_or_copy_or_assign(first_data, user_mds_node.user_data_, MdsAllocator::get_instance()));
ASSERT_EQ(true, first_data.data_.ptr() != user_mds_node.user_data_.data_.ptr());
ASSERT_EQ(0, memcmp(first_data.data_.ptr(), user_mds_node.user_data_.data_.ptr(), first_data.data_.length()));
user_mds_node.try_on_redo(mock_scn(10));
user_mds_node.try_before_prepare();
user_mds_node.try_on_prepare(mock_scn(11));
user_mds_node.try_on_commit(mock_scn(11), mock_scn(11));
MdsDumpNode dump_node;
ASSERT_EQ(OB_SUCCESS, dump_node.init(GET_MDS_TABLE_ID<NormalMdsTable>::value, GET_MDS_UNIT_ID<UnitTestMdsTable, DummyKey, ExampleUserData2>::value, user_mds_node, mds::MdsAllocator::get_instance()));
constexpr int buf_len = 1024;
char buffer[buf_len] = {0};
int64_t pos = 0;
ASSERT_EQ(OB_SUCCESS, dump_node.serialize(buffer, buf_len, pos));
MdsDumpNode dump_node2;
pos = 0;
ObArenaAllocator allocator;
ASSERT_EQ(OB_SUCCESS, dump_node2.deserialize(allocator, buffer, buf_len, pos));
OB_ASSERT(dump_node2.crc_check_number_ == dump_node2.generate_hash());
ASSERT_EQ(dump_node2.generate_hash(), dump_node2.crc_check_number_);
ASSERT_EQ(dump_node2.crc_check_number_, dump_node.crc_check_number_);
UserMdsNode<DummyKey, ExampleUserData2> user_mds_node2;
ASSERT_EQ(OB_SUCCESS, dump_node2.convert_to_user_mds_node(user_mds_node2, share::ObLSID(1), ObTabletID(1)));
ASSERT_EQ(0, memcmp(user_mds_node.user_data_.data_.ptr(), user_mds_node2.user_data_.data_.ptr(), user_mds_node.user_data_.data_.length()));
ASSERT_EQ(user_mds_node.redo_scn_, user_mds_node2.redo_scn_);
ASSERT_EQ(user_mds_node.end_scn_, user_mds_node2.end_scn_);
ASSERT_EQ(user_mds_node.trans_version_, user_mds_node2.trans_version_);
ASSERT_EQ(user_mds_node.status_.union_.value_, user_mds_node2.status_.union_.value_);
ASSERT_EQ(user_mds_node.seq_no_, user_mds_node2.seq_no_);
}
void TestMdsDumpKV::test_dump_dummy_key()
{
DummyKey key;
MdsDumpKey dump_key1, dump_key2;
dump_key1.init(0, 0, key, MdsAllocator::get_instance());
constexpr int buf_len = 1024;
char buffer[buf_len] = {0};
int64_t pos = 0;
ASSERT_EQ(OB_SUCCESS, dump_key1.serialize(buffer, buf_len, pos));
pos = 0;
ASSERT_EQ(OB_SUCCESS, dump_key2.deserialize(buffer, buf_len, pos));
int compare_result = 0;
ASSERT_EQ(OB_SUCCESS, dump_key1.compare(dump_key2, compare_result));
ASSERT_EQ(0, compare_result);
}
TEST_F(TestMdsDumpKV, test_convert_and_serialize_and_compare) { TestMdsDumpKV::test_dump_node_convert_and_serialize_and_compare(); }
TEST_F(TestMdsDumpKV, test_dump_dummy_key) { TestMdsDumpKV::test_dump_dummy_key(); }
}
}
int main(int argc, char **argv)
{
system("rm -rf test_mds_dump_kv.log");
oceanbase::common::ObLogger &logger = oceanbase::common::ObLogger::get_logger();
logger.set_file_name("test_mds_dump_kv.log", false);
logger.set_log_level(OB_LOG_LEVEL_DEBUG);
testing::InitGoogleTest(&argc, argv);
oceanbase::observer::ObMdsEventBuffer::init();
int ret = RUN_ALL_TESTS();
oceanbase::observer::ObMdsEventBuffer::destroy();
int64_t alloc_times = oceanbase::storage::mds::MdsAllocator::get_alloc_times();
int64_t free_times = oceanbase::storage::mds::MdsAllocator::get_free_times();
if (alloc_times != free_times) {
MDS_LOG(ERROR, "memory may leak", K(free_times), K(alloc_times));
ret = -1;
} else {
MDS_LOG(INFO, "all memory released", K(free_times), K(alloc_times));
}
return ret;
}

View File

@ -0,0 +1,206 @@
/**
* 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.
*/
#define UNITTEST_DEBUG
#include "lib/utility/utility.h"
#include <atomic>
#include <cstdlib>
#include <gtest/gtest.h>
#define private public
#define protected public
#include "storage/multi_data_source/compile_utility/mds_dummy_key.h"
#include "storage/multi_data_source/runtime_utility/common_define.h"
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>
#include "storage/multi_data_source/runtime_utility/mds_factory.h"
#include "common/ob_clock_generator.h"
#include "storage/multi_data_source/mds_node.h"
#include "example_user_helper_define.cpp"
#include "common/meta_programming/ob_type_traits.h"
#include "storage/multi_data_source/mds_row.h"
namespace oceanbase {
namespace storage
{
namespace mds
{
void *MdsAllocator::alloc(const int64_t size)
{
void *ptr = ob_malloc(size, "MDS");
ATOMIC_INC(&alloc_times_);
MDS_LOG(DEBUG, "alloc obj", KP(ptr), K(size), K(lbt()));
return ptr;
}
void MdsAllocator::free(void *ptr) {
ATOMIC_INC(&free_times_);
MDS_LOG(DEBUG, "free obj", KP(ptr), K(lbt()));
ob_free(ptr);
}
}
}
namespace unittest {
using namespace common;
using namespace std;
using namespace storage;
using namespace mds;
class TestMdsList: public ::testing::Test
{
public:
TestMdsList() {};
virtual ~TestMdsList() {};
virtual void SetUp() {
};
virtual void TearDown() {
};
private:
// disallow copy
DISALLOW_COPY_AND_ASSIGN(TestMdsList);
};
TEST_F(TestMdsList, del_from_tail) {
SortedList<UserMdsNode<DummyKey, ExampleUserData1>, SORT_TYPE::DESC> list;
for (int i = 0; i < 10; ++i) {
ListNode<UserMdsNode<DummyKey, ExampleUserData1>> *new_node = new UserMdsNode<DummyKey, ExampleUserData1>();
list.append(new_node);
}
list.for_each_node_from_tail_to_head_until_true([&list](const UserMdsNode<DummyKey, ExampleUserData1> &node) {
list.del((ListNode<UserMdsNode<DummyKey, ExampleUserData1>> *)(&node));
delete &node;
return false;
});
ASSERT_EQ(list.empty(), true);
}
TEST_F(TestMdsList, del_from_head) {
SortedList<UserMdsNode<DummyKey, ExampleUserData1>, SORT_TYPE::DESC> list;
for (int i = 0; i < 10; ++i) {
ListNode<UserMdsNode<DummyKey, ExampleUserData1>> *new_node = new UserMdsNode<DummyKey, ExampleUserData1>();
list.append(new_node);
}
list.for_each_node_from_head_to_tail_until_true([&list](const UserMdsNode<DummyKey, ExampleUserData1> &node) {
list.del((ListNode<UserMdsNode<DummyKey, ExampleUserData1>> *)(&node));
delete &node;
return false;
});
ASSERT_EQ(list.empty(), true);
}
TEST_F(TestMdsList, random_del) {
for (int i = 0; i < 100; ++i) {
SortedList<UserMdsNode<DummyKey, ExampleUserData1>, SORT_TYPE::DESC> list;
vector<ListNode<UserMdsNode<DummyKey, ExampleUserData1>> *> v;
for (int j = 0; j < 10; ++j) {
ListNode<UserMdsNode<DummyKey, ExampleUserData1>> *new_node = new UserMdsNode<DummyKey, ExampleUserData1>();
list.append(new_node);
v.push_back(new_node);
}
int idx = rand() % 10;
for (int k = 0; k < 10; ++k) {
list.del(v[(idx + k) % 10]);
}
ASSERT_EQ(list.empty(), true);
}
}
TEST_F(TestMdsList, insert_to_tail) {
SortedList<UserMdsNode<DummyKey, ExampleUserData1>, SORT_TYPE::DESC> list;
for (int j = 0; j < 10; ++j) {
UserMdsNode<DummyKey, ExampleUserData1> *new_node = new UserMdsNode<DummyKey, ExampleUserData1>();
new_node->redo_scn_ = mock_scn(j);
list.insert(new_node);
}
list.for_each_node_from_head_to_tail_until_true([&list](const UserMdsNode<DummyKey, ExampleUserData1> &node) {
list.del((ListNode<UserMdsNode<DummyKey, ExampleUserData1>> *)(&node));
delete &node;
return false;
});
}
TEST_F(TestMdsList, insert_to_head) {
SortedList<UserMdsNode<DummyKey, ExampleUserData1>, SORT_TYPE::DESC> list;
for (int j = 0; j < 10; ++j) {
UserMdsNode<DummyKey, ExampleUserData1> *new_node = new UserMdsNode<DummyKey, ExampleUserData1>();
new_node->redo_scn_ = mock_scn(10 - j);
list.insert(new_node);
}
list.for_each_node_from_head_to_tail_until_true([&list](const UserMdsNode<DummyKey, ExampleUserData1> &node) {
list.del((ListNode<UserMdsNode<DummyKey, ExampleUserData1>> *)(&node));
delete &node;
return false;
});
}
TEST_F(TestMdsList, random_insert_del) {
for (int i = 0; i < 100; ++i) {
SortedList<UserMdsNode<DummyKey, ExampleUserData1>, SORT_TYPE::DESC> list;
vector<ListNode<UserMdsNode<DummyKey, ExampleUserData1>> *> v;
for (int j = 0; j < 10; ++j) {
UserMdsNode<DummyKey, ExampleUserData1> *new_node = new UserMdsNode<DummyKey, ExampleUserData1>();
new_node->redo_scn_ = mock_scn(rand() % 10);
list.insert(new_node);
v.push_back(new_node);
}
int idx = rand() % 10;
for (int k = 0; k < 10; ++k) {
list.del(v[(idx + k) % 10]);
}
ASSERT_EQ(list.empty(), true);
}
}
TEST_F(TestMdsList, fetch_and_insert) {
SortedList<UserMdsNode<DummyKey, ExampleUserData1>, SORT_TYPE::DESC> list;
UserMdsNode<DummyKey, ExampleUserData1> *new_node = new UserMdsNode<DummyKey, ExampleUserData1>();
UserMdsNode<DummyKey, ExampleUserData1> *new_node2 = new UserMdsNode<DummyKey, ExampleUserData1>();
list.insert_into_head(new_node);
ListNode<UserMdsNode<DummyKey, ExampleUserData1>> *node = list.fetch_from_head();
ASSERT_NE(nullptr, node);
ASSERT_EQ(true, list.empty());
list.insert_into_head(new_node);
node = list.fetch_from_head();
ASSERT_NE(nullptr, node);
ASSERT_EQ(true, list.empty());
list.insert_into_head(new_node);
list.insert_into_head(new_node2);
node = list.fetch_from_head();
ASSERT_EQ(new_node2, node);
ASSERT_EQ(false, list.empty());
list.insert_into_head(new_node2);
node = list.fetch_from_head();
ASSERT_EQ(new_node2, node);
delete new_node;
delete new_node2;
}
}
}
int main(int argc, char **argv)
{
system("rm -rf test_mds_list.log");
oceanbase::common::ObLogger &logger = oceanbase::common::ObLogger::get_logger();
logger.set_file_name("test_mds_list.log", false);
logger.set_log_level(OB_LOG_LEVEL_DEBUG);
testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
int64_t alloc_times = oceanbase::storage::mds::MdsAllocator::get_alloc_times();
int64_t free_times = oceanbase::storage::mds::MdsAllocator::get_free_times();
if (alloc_times != free_times) {
MDS_LOG(ERROR, "memory may leak", K(free_times), K(alloc_times));
ret = -1;
} else {
MDS_LOG(INFO, "all memory released", K(free_times), K(alloc_times));
}
return ret;
}

View File

@ -0,0 +1,237 @@
/**
* 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.
*/
#define UNITTEST_DEBUG
#include "lib/utility/utility.h"
#include <atomic>
#include <gtest/gtest.h>
#define private public
#define protected public
#include "storage/multi_data_source/compile_utility/mds_dummy_key.h"
#include "storage/multi_data_source/runtime_utility/common_define.h"
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>
#include "storage/multi_data_source/runtime_utility/mds_factory.h"
#include "common/ob_clock_generator.h"
#include "storage/multi_data_source/mds_node.h"
#include "example_user_helper_define.cpp"
#include "common/meta_programming/ob_type_traits.h"
#include "storage/multi_data_source/mds_row.h"
namespace oceanbase {
namespace storage
{
namespace mds
{
void *MdsAllocator::alloc(const int64_t size)
{
void *ptr = ob_malloc(size, "MDS");
ATOMIC_INC(&alloc_times_);
MDS_LOG(DEBUG, "alloc obj", KP(ptr), K(size), K(lbt()));
return ptr;
}
void MdsAllocator::free(void *ptr) {
ATOMIC_INC(&free_times_);
MDS_LOG(DEBUG, "free obj", KP(ptr), K(lbt()));
ob_free(ptr);
}
}
}
namespace unittest {
using namespace common;
using namespace std;
using namespace storage;
using namespace mds;
class TestMdsNode: public ::testing::Test
{
public:
TestMdsNode() {};
virtual ~TestMdsNode() {};
virtual void SetUp() {
};
virtual void TearDown() {
};
private:
// disallow copy
DISALLOW_COPY_AND_ASSIGN(TestMdsNode);
};
std::atomic_int call_on_set(0);
std::atomic_int call_try_on_redo(0);
std::atomic_int call_try_on_commit(0);
std::atomic_int call_try_on_abort(0);
struct UserDataWithCallBack
{
UserDataWithCallBack() : val_(-1) {}
UserDataWithCallBack(int val) : val_(val) {}
void on_set() {
static_assert(OB_TRAIT_HAS_ON_SET(UserDataWithCallBack), "compile check on_set will not be called be MDS");
call_on_set++;
}
void on_redo(share::SCN redo_scn) {
static_assert(OB_TRAIT_HAS_ON_REDO(UserDataWithCallBack), "compile check try_on_redo will not be called be MDS");
call_try_on_redo++;
}
void on_commit(share::SCN commit_version, share::SCN commit_scn) {
static_assert(OB_TRAIT_HAS_ON_COMMIT(UserDataWithCallBack), "compile check try_on_commit will not be called be MDS");
ob_usleep(100_ms);
call_try_on_commit++;
}
void on_abort(share::SCN abort_version) {
static_assert(OB_TRAIT_HAS_ON_ABORT(UserDataWithCallBack), "compile check try_on_abort will not be called be MDS");
call_try_on_abort++;
}
TO_STRING_KV("test", val_);
int val_;
};
TEST_F(TestMdsNode, call_user_method) {
MdsRow<DummyKey, UserDataWithCallBack> row;
MdsCtx ctx(mds::MdsWriter(transaction::ObTransID(100)));// commit finally
ASSERT_EQ(OB_SUCCESS, row.set(UserDataWithCallBack(1), ctx, 0));
ctx.on_redo(mock_scn(1));
ctx.before_prepare();
ctx.on_prepare(mock_scn(2));
ctx.on_commit(mock_scn(3), mock_scn(3));
ASSERT_NE(call_on_set, 0);
ASSERT_NE(call_try_on_redo, 0);
ASSERT_NE(call_try_on_commit, 0);
ASSERT_EQ(call_try_on_abort, 0);
}
TEST_F(TestMdsNode, release_node_while_node_in_ctx) {
MdsRow<DummyKey, UserDataWithCallBack> row;
MdsCtx ctx(mds::MdsWriter(transaction::ObTransID(100)));// commit finally
ASSERT_EQ(OB_SUCCESS, row.set(UserDataWithCallBack(1), ctx, 0));
ctx.on_redo(mock_scn(1));
ctx.before_prepare();
row.~MdsRow();
ASSERT_EQ(true, ctx.write_list_.empty());
ctx.on_prepare(mock_scn(2));
ctx.on_commit(mock_scn(3), mock_scn(3));
}
TEST_F(TestMdsNode, release_node_while_node_in_ctx_concurrent) {
call_on_set = 0;
call_try_on_redo = 0;
call_try_on_commit = 0;
call_try_on_abort = 0;
MdsRow<DummyKey, UserDataWithCallBack> row;
MdsCtx ctx(mds::MdsWriter(transaction::ObTransID(100)));// commit finally
// 提交这些node将会耗时50ms
ASSERT_EQ(OB_SUCCESS, row.set(UserDataWithCallBack(1), ctx, 0));
ASSERT_EQ(OB_SUCCESS, row.set(UserDataWithCallBack(2), ctx, 0));
ASSERT_EQ(OB_SUCCESS, row.set(UserDataWithCallBack(3), ctx, 0));
ASSERT_EQ(OB_SUCCESS, row.set(UserDataWithCallBack(4), ctx, 0));
ASSERT_EQ(OB_SUCCESS, row.set(UserDataWithCallBack(5), ctx, 0));
std::thread t1([&ctx]() {
OCCAM_LOG(DEBUG, "t1 start");
ctx.on_redo(mock_scn(1));
ctx.before_prepare();
ctx.on_prepare(mock_scn(2));
ctx.on_commit(mock_scn(3), mock_scn(3));// will cost 500ms
});
std::thread t2([&row]() {
OCCAM_LOG(DEBUG, "t2 start");
ob_usleep(250_ms);
row.~MdsRow();
});
t1.join();
t2.join();
ASSERT_EQ(5, call_on_set);
ASSERT_EQ(5, call_try_on_redo);
ASSERT_GE(call_try_on_commit, 0);
ASSERT_LE(call_try_on_commit, 5);
}
// TEST_F(TestMdsNode, test_prepare_version_with_commit) {
// UserMdsNode<DummyKey, UserDataWithCallBack> node(nullptr, MdsNodeType::SET, WriterType::TRANSACTION, 1);
// ASSERT_EQ(node.get_prepare_version_(), share::SCN::max_scn());
// node.try_on_redo(mock_scn(1));
// ASSERT_EQ(node.get_prepare_version_(), share::SCN::max_scn());
// node.try_before_prepare();
// ASSERT_EQ(node.get_prepare_version_(), share::SCN::min_scn());
// node.try_on_prepare(mock_scn(2));
// ASSERT_EQ(node.get_prepare_version_(), mock_scn(2)); // prepare version没有传下来
// ASSERT_EQ(node.is_aborted_(), false);
// ASSERT_EQ(node.is_committed_(), false);
// ASSERT_EQ(node.is_decided_(), false);
// node.try_on_commit(mock_scn(3), mock_scn(3));
// ASSERT_EQ(node.is_aborted_(), false);
// ASSERT_EQ(node.is_committed_(), true);
// ASSERT_EQ(node.is_decided_(), true);
// ASSERT_EQ(node.get_prepare_version_(), mock_scn(3));
// }
// TEST_F(TestMdsNode, test_prepare_version_with_abort) {
// UserMdsNode<DummyKey, UserDataWithCallBack> node(nullptr, MdsNodeType::SET, WriterType::TRANSACTION, 1);
// ASSERT_EQ(node.is_aborted_(), false);
// ASSERT_EQ(node.is_committed_(), false);
// ASSERT_EQ(node.is_decided_(), false);
// node.try_on_abort(share::SCN::max_scn());
// ASSERT_EQ(node.is_aborted_(), true);
// ASSERT_EQ(node.is_committed_(), false);
// ASSERT_EQ(node.is_decided_(), true);
// ASSERT_EQ(call_try_on_abort, true);
// ASSERT_EQ(node.get_prepare_version_(), share::SCN::max_scn());
// }
// TEST_F(TestMdsNode, test_commit_version_with_commit) {
// UserMdsNode<DummyKey, UserDataWithCallBack> node(nullptr, MdsNodeType::SET, WriterType::TRANSACTION, 1);
// ASSERT_EQ(node.get_commit_version_(), share::SCN::max_scn());
// node.try_on_redo(mock_scn(1));
// ASSERT_EQ(node.get_commit_version_(), share::SCN::max_scn());
// node.try_before_prepare();
// ASSERT_EQ(node.get_commit_version_(), share::SCN::max_scn());
// node.try_on_prepare(mock_scn(2));
// ASSERT_EQ(node.get_commit_version_(), share::SCN::max_scn());
// node.try_on_commit(mock_scn(3), mock_scn(3));
// ASSERT_EQ(node.get_commit_version_(), mock_scn(3));// only valid after commit
// }
// TEST_F(TestMdsNode, test_commit_version_with_abort) {
// UserMdsNode<DummyKey, UserDataWithCallBack> node(nullptr, MdsNodeType::SET, WriterType::TRANSACTION, 1);
// node.try_on_abort(share::SCN::max_scn());
// ASSERT_EQ(node.get_commit_version_(), share::SCN::max_scn());
// }
TEST_F(TestMdsNode, test_node_print) {
UserMdsNode<DummyKey, UserDataWithCallBack> node0(nullptr, MdsNodeType::SET, WriterType::TRANSACTION, 1);
UserMdsNode<DummyKey, UserDataWithCallBack> node1(nullptr, MdsNodeType::SET, WriterType::TRANSACTION, 1);
}
}
}
int main(int argc, char **argv)
{
system("rm -rf test_mds_node.log");
oceanbase::common::ObLogger &logger = oceanbase::common::ObLogger::get_logger();
logger.set_file_name("test_mds_node.log", false);
logger.set_log_level(OB_LOG_LEVEL_DEBUG);
testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
int64_t alloc_times = oceanbase::storage::mds::MdsAllocator::get_alloc_times();
int64_t free_times = oceanbase::storage::mds::MdsAllocator::get_free_times();
if (alloc_times != free_times) {
MDS_LOG(ERROR, "memory may leak", K(free_times), K(alloc_times));
ret = -1;
} else {
MDS_LOG(INFO, "all memory released", K(free_times), K(alloc_times));
}
return ret;
}

View File

@ -0,0 +1,229 @@
/**
* 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.
*/
#define UNITTEST_DEBUG
#include <gtest/gtest.h>
#include "lib/ob_errno.h"
#include "share/ob_errno.h"
#include <exception>
#define private public
#define protected public
#include "common_define.h"
#include "lib/allocator/ob_malloc.h"
#include "storage/multi_data_source/mds_node.h"
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>
#include "common/ob_clock_generator.h"
#include "storage/multi_data_source/mds_row.h"
#include "example_user_helper_define.cpp"
namespace oceanbase {
namespace unittest {
using namespace common;
using namespace std;
using namespace storage;
using namespace mds;
class TestMdsRowAndMdsCtx: public ::testing::Test
{
public:
TestMdsRowAndMdsCtx() {};
virtual ~TestMdsRowAndMdsCtx() {};
virtual void SetUp() {
};
virtual void TearDown() {
};
static void mds_row_set_element();
static void two_thread_set_conflict();
static void get_uncommitted();
static void get_latest();
static void get_by_writer();
static void get_snapshop_until_timeout();
static void get_snapshop_disgard();
static MdsRow<ExampleUserData2> row_;
private:
// disallow copy
DISALLOW_COPY_AND_ASSIGN(TestMdsRowAndMdsCtx);
};
MdsRow<ExampleUserData2> TestMdsRowAndMdsCtx::row_;
void TestMdsRowAndMdsCtx::mds_row_set_element() {
ExampleUserData2 data1(1);
MdsCtx ctx1(1);
ASSERT_EQ(OB_SUCCESS, row_.set(data1, ctx1, 0));// copy user data
UserMdsNode<ExampleUserData2> *user_node = dynamic_cast<UserMdsNode<ExampleUserData2> *>(row_.sorted_list_.list_.list_head_);
ASSERT_NE(user_node->user_data_.data_.ptr(), data1.data_.ptr());
auto p_data = data1.data_.ptr();
ASSERT_EQ(OB_SUCCESS, row_.set(std::move(data1), ctx1, 0));// move user data
user_node = dynamic_cast<UserMdsNode<ExampleUserData2> *>(row_.sorted_list_.list_.list_head_);
ASSERT_EQ(user_node->user_data_.data_.ptr(), p_data);
MdsCtx ctx2(2);
ExampleUserData2 data2(2);
ASSERT_EQ(OB_TRY_LOCK_ROW_CONFLICT, row_.set(data2, ctx2, 0));// 不设超时,报6005
ASSERT_EQ(OB_ERR_EXCLUSIVE_LOCK_CONFLICT, row_.set(data2, ctx2, 200_ms));// 设超时,报6003
ctx1.on_redo(mock_scn(1));
ASSERT_EQ(OB_TRY_LOCK_ROW_CONFLICT, row_.set(data2, ctx2, 0));// 不设超时,报6005
ctx1.before_prepare();
ASSERT_EQ(OB_TRY_LOCK_ROW_CONFLICT, row_.set(data2, ctx2, 0));// 不设超时,报6005
ctx1.on_prepare(mock_scn(2));
ASSERT_EQ(OB_TRY_LOCK_ROW_CONFLICT, row_.set(data2, ctx2, 0));// 不设超时,报6005
ctx1.on_commit(mock_scn(3));
ASSERT_EQ(OB_SUCCESS, row_.set(data2, ctx2, 0));
}
void TestMdsRowAndMdsCtx::two_thread_set_conflict() {
std::thread t1([]() {
ExampleUserData2 data(6);
MdsCtx ctx(3);
ASSERT_EQ(OB_SUCCESS, row_.set(data, ctx, 0));
ob_usleep(500_ms);
ctx.on_redo(mock_scn(6));
ctx.before_prepare();
ctx.on_prepare(mock_scn(6));
ctx.on_commit(mock_scn(6));
});
std::thread t2([]() {
ExampleUserData2 data(7);
MdsCtx ctx(4);
ob_usleep(100_ms);
ASSERT_EQ(OB_TRY_LOCK_ROW_CONFLICT, row_.set(data, ctx, 0));
ASSERT_EQ(OB_SUCCESS, row_.set(data, ctx, 1_s));
ob_usleep(500_ms);
ctx.on_redo(mock_scn(9));
ctx.before_prepare();
ctx.on_prepare(mock_scn(9));
ctx.on_commit(mock_scn(9));
});
t1.join();
t2.join();
}
void TestMdsRowAndMdsCtx::get_uncommitted() {
ExampleUserData2 data(12);
MdsCtx ctx(5);
ASSERT_EQ(OB_SUCCESS, row_.set(data, ctx, 0));
MdsCtx ctx2(6);
int64_t data_size = 0;
ASSERT_EQ(OB_SUCCESS, row_.get_uncommitted([&data_size](const ExampleUserData2 &data) {
data_size = data.data_.length();
return OB_SUCCESS;
}, 0));
ASSERT_EQ(12, data_size);
ctx.on_redo(mock_scn(10));
ctx.before_prepare();
ctx.on_prepare(mock_scn(10));
ctx.on_commit(mock_scn(10));
}
void TestMdsRowAndMdsCtx::get_latest() {
int64_t data_size = 0;
ASSERT_EQ(OB_SUCCESS, row_.get_snapshot([&data_size](const ExampleUserData2 &data) {
data_size = data.data_.length();
return OB_SUCCESS;
}, share::SCN::max_scn(), 0, 0));
ExampleUserData2 data(13);
MdsCtx ctx(7);
ASSERT_EQ(OB_SUCCESS, row_.set(data, ctx, 0));
ASSERT_EQ(OB_ERR_SHARED_LOCK_CONFLICT, row_.get_snapshot([&data_size](const ExampleUserData2 &data) {
data_size = data.data_.length();
return OB_SUCCESS;
}, share::SCN::max_scn(), 0, 0));
ASSERT_EQ(12, data_size);
}
void TestMdsRowAndMdsCtx::get_by_writer() {
ExampleUserData2 data(13);
MdsCtx ctx(9);
int64_t data_size = 0;
ASSERT_EQ(OB_SUCCESS, row_.get_by_writer([&data_size](const ExampleUserData2 &data) {
data_size = data.data_.length();
return OB_SUCCESS;
}, 9, mock_scn(6), 0, 0));// read snapshot
ASSERT_EQ(6, data_size);// read committed version
ASSERT_EQ(OB_SUCCESS, row_.set(data, ctx, 0));
ASSERT_EQ(OB_SUCCESS, row_.get_by_writer([&data_size](const ExampleUserData2 &data) {
data_size = data.data_.length();
return OB_SUCCESS;
}, 9, mock_scn(10)/*not affected*/, 0, 0));
ASSERT_EQ(13, data_size);// read self write
}
void TestMdsRowAndMdsCtx::get_snapshop_until_timeout() {
ExampleUserData2 data(20);
MdsCtx ctx(20);
int64_t data_size = 0;
ASSERT_EQ(OB_SUCCESS, row_.set(data, ctx, 0));
ctx.before_prepare();// block all read operations
ASSERT_EQ(OB_ERR_SHARED_LOCK_CONFLICT, row_.get_by_writer([&data_size](const ExampleUserData2 &data) {
data_size = data.data_.length();
return OB_SUCCESS;
}, 10, mock_scn(6), 0, 500_ms));// read snapshot timeout
ASSERT_EQ(OB_ERR_SHARED_LOCK_CONFLICT, row_.get_snapshot([&data_size](const ExampleUserData2 &data) {
data_size = data.data_.length();
return OB_SUCCESS;
}, mock_scn(6), 0, 500_ms));// read snapshot timeout
ctx.on_redo(mock_scn(20));
ctx.on_prepare(mock_scn(20));
ctx.on_commit(mock_scn(20));
ASSERT_EQ(OB_SUCCESS, row_.get_snapshot([&data_size](const ExampleUserData2 &data) {
data_size = data.data_.length();
return OB_SUCCESS;
}, mock_scn(6), 0, 500_ms));// read snapshot
ASSERT_EQ(6, data_size);
ASSERT_EQ(OB_SUCCESS, row_.get_snapshot([&data_size](const ExampleUserData2 &data) {
data_size = data.data_.length();
return OB_SUCCESS;
}, mock_scn(21), 0, 500_ms));// read snapshot
ASSERT_EQ(20, data_size);
}
void TestMdsRowAndMdsCtx::get_snapshop_disgard() {
ASSERT_EQ(OB_SNAPSHOT_DISCARDED, row_.get_snapshot([](const ExampleUserData2 &data) {
return OB_SUCCESS;
}, mock_scn(1), 0, 500_ms));// read snapshot timeout
}
TEST_F(TestMdsRowAndMdsCtx, mds_row_set_element) { TestMdsRowAndMdsCtx::mds_row_set_element(); }
TEST_F(TestMdsRowAndMdsCtx, two_thread_set_conflict) { TestMdsRowAndMdsCtx::two_thread_set_conflict(); }
TEST_F(TestMdsRowAndMdsCtx, get_uncommitted) { TestMdsRowAndMdsCtx::get_uncommitted(); }
TEST_F(TestMdsRowAndMdsCtx, get_latest) { TestMdsRowAndMdsCtx::get_latest(); }
TEST_F(TestMdsRowAndMdsCtx, get_by_writer) { TestMdsRowAndMdsCtx::get_by_writer(); }
TEST_F(TestMdsRowAndMdsCtx, get_snapshop_until_timeout) { TestMdsRowAndMdsCtx::get_snapshop_until_timeout(); }
TEST_F(TestMdsRowAndMdsCtx, get_snapshop_disgard) { TestMdsRowAndMdsCtx::get_snapshop_disgard(); }
}
}
int main(int argc, char **argv)
{
system("rm -rf test_mds_row.log");
oceanbase::common::ObLogger &logger = oceanbase::common::ObLogger::get_logger();
logger.set_file_name("test_mds_row.log", false);
logger.set_log_level(OB_LOG_LEVEL_TRACE);
testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
oceanbase::unittest::TestMdsRowAndMdsCtx::row_.~MdsRow();
int64_t alloc_times = oceanbase::storage::mds::MdsAllocator::get_alloc_times();
int64_t free_times = oceanbase::storage::mds::MdsAllocator::get_free_times();
if (alloc_times != free_times) {
MDS_LOG(ERROR, "memory may leak", K(free_times), K(alloc_times));
ret = -1;
} else {
MDS_LOG(INFO, "all memory released", K(free_times), K(alloc_times));
}
return ret;
}

View File

@ -0,0 +1,654 @@
/**
* 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.
*/
#define UNITTEST_DEBUG
#include "lib/utility/utility.h"
#include <gtest/gtest.h>
#define private public
#define protected public
#include "multi_data_source/example_user_data_define.h"
#include "share/ob_ls_id.h"
#include "storage/multi_data_source/mds_writer.h"
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>
#include <exception>
#include "common_define.h"
#include "lib/ob_errno.h"
#include "share/ob_errno.h"
#include "storage/multi_data_source/adapter_define/mds_dump_node.h"
#include "lib/allocator/ob_malloc.h"
#include "storage/multi_data_source/mds_node.h"
#include "common/ob_clock_generator.h"
#include "storage/multi_data_source/mds_row.h"
#include "storage/multi_data_source/mds_unit.h"
#include "storage/multi_data_source/mds_table_handle.h"
#include "storage/multi_data_source/mds_table_handler.h"
#include "example_user_helper_define.cpp"
#include "storage/tx/ob_trans_define.h"
#include <algorithm>
#include <numeric>
#include "storage/multi_data_source/runtime_utility/mds_lock.h"
#include "storage/tablet/ob_tablet_meta.h"
namespace oceanbase {
namespace storage
{
namespace mds
{
void *MdsAllocator::alloc(const int64_t size)
{
void *ptr = ob_malloc(size, "MDS");
ATOMIC_INC(&alloc_times_);
MDS_LOG(DEBUG, "alloc obj", KP(ptr), K(size), K(lbt()));
return ptr;
}
void MdsAllocator::free(void *ptr) {
ATOMIC_INC(&free_times_);
MDS_LOG(DEBUG, "free obj", KP(ptr), K(lbt()));
ob_free(ptr);
}
}
}
namespace unittest {
using namespace common;
using namespace std;
using namespace storage;
using namespace mds;
using namespace transaction;
class TestMdsTable: public ::testing::Test
{
public:
TestMdsTable() {}
virtual ~TestMdsTable() {}
virtual void SetUp() {
}
virtual void TearDown() {
}
static void set();
static void replay();
static void get_latest();
static void get_snapshot();
static void get_snapshot_hung_1s();
static void get_by_writer();
static void insert_multi_row();
static void get_multi_row();
// static void for_each_scan();
static void standard_iterator();
static void OB_iterator();
static void test_flush();
static void test_is_locked_by_others();
static void test_multi_key_remove();
private:
// disallow copy
DISALLOW_COPY_AND_ASSIGN(TestMdsTable);
};
ObMdsTableHandler mds_table_hanlder;
MdsTableHandle &mds_table_ = mds_table_hanlder.mds_table_handle_;
/***********************************************Single Row*************************************************************/
struct A { ObSpinLock lock_; };
struct B { MdsLock lock_; };
#define GET_REAL_MDS_TABLE(mds_table) (*((MdsTableImpl<UnitTestMdsTable>*)(dynamic_cast<guard::LightDataBlock<MdsTableImpl<UnitTestMdsTable>>*>((mds_table.p_mds_table_base_.ctrl_ptr_->p_data_block_))->data_)))
void TestMdsTable::set() {
ASSERT_EQ(OB_SUCCESS, mds_table_.init<UnitTestMdsTable>(MdsAllocator::get_instance(), ObTabletID(1), share::ObLSID(1), (ObTabletPointer*)0x111));
MDS_LOG(INFO, "test sizeof", K(sizeof(MdsTableImpl<UnitTestMdsTable>)), K(sizeof(B)), K(mds_table_.p_mds_table_base_.ctrl_ptr_->ref_));
ExampleUserData1 data1(1);
ExampleUserData2 data2;
ASSERT_EQ(OB_SUCCESS, data2.assign(MdsAllocator::get_instance(), "123"));
MdsCtx ctx1(mds::MdsWriter(ObTransID(1)));// commit finally
DummyKey key;
ASSERT_EQ(OB_SUCCESS, mds_table_.set(data1, ctx1));
ASSERT_EQ(OB_SUCCESS, mds_table_.set(data2, ctx1));
ctx1.on_redo(mock_scn(10));
ctx1.before_prepare();
ctx1.on_prepare(mock_scn(10));
ctx1.on_commit(mock_scn(10), mock_scn(10));
ExampleUserData1 data3(3);
MdsCtx ctx2(mds::MdsWriter(ObTransID(2)));// abort by RAII finally
ASSERT_EQ(OB_SUCCESS, mds_table_.set(data1, ctx2));
ctx2.on_abort(mock_scn(8));
}
// <DummyKey, ExampleUserData1>: (data:1, writer:1, ver:10)
// <DummyKey, ExampleUserData2>: (data:2, writer:1, ver:10)
void TestMdsTable::replay() {
ExampleUserData1 data1(3);
MdsCtx ctx1(mds::MdsWriter(ObTransID(1)));// commit finally
ASSERT_EQ(OB_SUCCESS, mds_table_.replay(data1, ctx1, mock_scn(9)));
// ctx1.on_redo(mock_scn(9));// insert to tail on unit ExampleUserData1
ctx1.before_prepare();
ctx1.on_prepare(mock_scn(9));
ctx1.on_commit(mock_scn(9), mock_scn(9));
auto &unit = GET_REAL_MDS_TABLE(mds_table_).unit_tuple_.element<MdsUnit<DummyKey, ExampleUserData1>>();
MDS_LOG(INFO, "xuwang test", K(unit.single_row_));
share::SCN recorde_scn = mock_scn(0);
unit.single_row_.v_.sorted_list_.for_each_node_from_tail_to_head_until_true([&](const UserMdsNode<DummyKey, ExampleUserData1> &node) -> int {
if (!node.is_aborted_()) {
OB_ASSERT(node.redo_scn_ > recorde_scn);
recorde_scn = node.redo_scn_;
}
return OB_SUCCESS;
});
}
// <DummyKey, ExampleUserData1>: (data:1, writer:1, ver:10) -> (data:3, writer:1, ver:9)
// <DummyKey, ExampleUserData2>: (data:2, writer:1, ver:10)
void TestMdsTable::get_latest()
{
ExampleUserData1 data1(5);
MdsCtx ctx1(mds::MdsWriter(ObTransID(1)));// abort finally
ASSERT_EQ(OB_SUCCESS, mds_table_.set(data1, ctx1));
int value = 0;
bool unused_committed_flag = false;
ASSERT_EQ(OB_SUCCESS, mds_table_.get_latest<ExampleUserData1>([&value](const ExampleUserData1 &data) {
value = data.value_;
return OB_SUCCESS;
}, unused_committed_flag));
ASSERT_EQ(5, value);// read uncommitted
ctx1.on_abort(mock_scn(11));
}
void TestMdsTable::get_snapshot()
{
int value = 0;
ASSERT_EQ(OB_SUCCESS, mds_table_.get_snapshot<ExampleUserData1>([&value](const ExampleUserData1 &data) {
value = data.value_;
return OB_SUCCESS;
}, mock_scn(9)));
ASSERT_EQ(3, value);// read snapshot
}
void TestMdsTable::get_snapshot_hung_1s()
{
std::thread th1([&]() {
MdsCtx ctx(mds::MdsWriter(ObTransID(1)));// abort finally
ExampleUserData1 data(1);
ASSERT_EQ(OB_SUCCESS, mds_table_.set(data, ctx));
ctx.on_redo(mock_scn(3));
ctx.before_prepare();
this_thread::sleep_for(chrono::milliseconds(1200));
ctx.on_abort(mock_scn(12));
});
std::thread th2([&]() {
this_thread::sleep_for(chrono::milliseconds(100));
ExampleUserData1 data;
int64_t start_ts = ObClockGenerator::getRealClock();
ASSERT_EQ(OB_SUCCESS, mds_table_.get_snapshot<ExampleUserData1>([&data](const ExampleUserData1 &node_data) {
data = node_data;
return OB_SUCCESS;
}, share::SCN::max_scn(), 0, 2_s));
ASSERT_LE(1_s, ObClockGenerator::getRealClock() - start_ts);
ASSERT_EQ(1, data.value_);// read snapshot
});
th1.join();
th2.join();
}
void TestMdsTable::get_by_writer()
{
ExampleUserData1 data1(15);
MdsCtx ctx1(mds::MdsWriter(ObTransID(15)));// abort finally
ASSERT_EQ(OB_SUCCESS, mds_table_.set(data1, ctx1));
ExampleUserData2 data2;
ASSERT_EQ(OB_SUCCESS, data2.assign(MdsAllocator::get_instance(), "3456"));
MdsCtx ctx2(mds::MdsWriter(ObTransID(20)));// commit finally with version 20
ASSERT_EQ(OB_SUCCESS, mds_table_.set(data2, ctx2));
ctx2.on_redo(mock_scn(20));
ctx2.before_prepare();
ctx2.on_prepare(mock_scn(20));
ctx2.on_commit(mock_scn(20), mock_scn(20));
int value = 0;
ASSERT_EQ(OB_SUCCESS, mds_table_.get_by_writer<ExampleUserData1>([&value](const ExampleUserData1 &data) {
value = data.value_;
return OB_SUCCESS;
}, mds::MdsWriter(ObTransID(15)), mock_scn(15)));// read self uncommitted change
ASSERT_EQ(15, value);// read uncommitted
ASSERT_EQ(OB_SUCCESS, mds_table_.get_by_writer<ExampleUserData2>([&value](const ExampleUserData2 &data) {
value = data.data_.length();
return OB_SUCCESS;
}, mds::MdsWriter(ObTransID(15)), mock_scn(15)));// read others committed change
ASSERT_EQ(3, value);// read last committed
ctx1.on_abort(mock_scn(20));
}
// <DummyKey, ExampleUserData1>: (data:1, writer:1, ver:10) -> (data:3, writer:1, ver:9)
// <DummyKey, ExampleUserData2>: (data:20, writer:20, ver:20) -> (data:2, writer:1, ver:10)
/***********************************************Multi Row**************************************************************/
void TestMdsTable::insert_multi_row() {
ExampleUserKey key(1);
ExampleUserData1 data1(1);
MdsCtx ctx(mds::MdsWriter(ObTransID(1)));// commit finally
ASSERT_EQ(OB_SUCCESS, mds_table_.set(key, data1, ctx));
ctx.on_redo(mock_scn(1));
ctx.before_prepare();
ctx.on_prepare(mock_scn(1));
ctx.on_commit(mock_scn(1), mock_scn(1));
ExampleUserKey key2(2);
ExampleUserData1 data2(2);
MdsCtx ctx2(mds::MdsWriter(ObTransID(2)));// commit finally
ASSERT_EQ(OB_SUCCESS, mds_table_.set(key2, data2, ctx2));
ASSERT_EQ(OB_SUCCESS, mds_table_.set(key, data2, ctx2));// 因为只存储单版本,所以data1读不到了
ctx2.on_redo(mock_scn(2));
ctx2.before_prepare();
ctx2.on_prepare(mock_scn(2));
ctx2.on_commit(mock_scn(2), mock_scn(2));
}
// <DummyKey, ExampleUserData1>: (data:1, writer:1, ver:10) -> (data:3, writer:1, ver:9)
// <DummyKey, ExampleUserData2>: (data:20, writer:20, ver:20) -> (data:2, writer:1, ver:10)
// <ExampleUserKey, ExampleUserData1> : <1> : (data:2, writer:2, ver:2) -> (data:1, writer:1, ver:1)
// <2> : (data:2, writer:2, ver:2)
void TestMdsTable::get_multi_row() {
ExampleUserData1 read_data;
ExampleUserKey key(1);
ExampleUserData1 data3(3);
MdsCtx ctx3(mds::MdsWriter(ObTransID(3)));// abort finally
ASSERT_EQ(OB_SUCCESS, mds_table_.set(key, data3, ctx3));
auto read_op = [&read_data](const ExampleUserData1 &data) { read_data = data; return OB_SUCCESS; };
int ret = mds_table_.get_snapshot<ExampleUserKey, ExampleUserData1>(ExampleUserKey(1), read_op, mock_scn(2));
ASSERT_EQ(OB_SUCCESS, ret);
ASSERT_EQ(2, read_data.value_);
ret = mds_table_.get_snapshot<ExampleUserKey, ExampleUserData1>(ExampleUserKey(1), read_op, mock_scn(1));// 没有转储,旧版本还是保留的
ASSERT_EQ(OB_SUCCESS, ret);
ASSERT_EQ(1, read_data.value_);
bool unused_committed_flag = false;
ret = mds_table_.get_latest<ExampleUserKey, ExampleUserData1>(ExampleUserKey(1), read_op, unused_committed_flag);
ASSERT_EQ(OB_SUCCESS, ret);
ASSERT_EQ(3, read_data.value_);
ctx3.on_abort(mock_scn(3));
}
struct ScanOp {
ScanOp() : valid_count_(0), total_count_(0) {}
int operator()(const MdsNode &node) {
if (!node.is_aborted_())
++valid_count_;
return OB_SUCCESS;
}
int valid_count_;
int total_count_;
};
// void TestMdsTable::for_each_scan() {
// ScanOp op;
// ASSERT_EQ(OB_SUCCESS, mds_table_.for_each_scan_node(op));
// ASSERT_EQ(op.valid_count_, 7);
// }
void TestMdsTable::standard_iterator() {
// iter kv unit, range for
MdsUnit<ExampleUserKey, ExampleUserData1> *p_mds_unit = nullptr;
ASSERT_EQ(OB_SUCCESS, mds_table_.get_mds_unit(p_mds_unit));// need handle protect table life
{
MdsRLockGuard lg(p_mds_unit->lock_);// lock unit
for (auto &kv_row : *p_mds_unit) {
MdsRLockGuard lg(kv_row.v_.lock_);// lock row
for (auto &mds_node : kv_row.v_) {
MDS_LOG(INFO, "print iter mds node", K(mds_node));
}
}
}
// iter dummy key unit, reverse iter
MdsUnit<DummyKey, ExampleUserData1> *p_mds_unit2 = nullptr;
ASSERT_EQ(OB_SUCCESS, mds_table_.get_mds_unit(p_mds_unit2));// need handle protect table life
{
int64_t cnt_committed = 0;
// using KvRowIter = MdsUnit<DummyKey, ExampleUserData2>::iterator;
// using NodeIter = KvRowIter::row_type::iterator;
using KvRowIter = MdsUnit<DummyKey, ExampleUserData1>::reverse_iterator;
using NodeIter = KvRowIter::row_type::reverse_iterator;
MdsRLockGuard lg(p_mds_unit->lock_);// lock unit
for (KvRowIter iter1 = p_mds_unit2->rbegin(); iter1 != p_mds_unit2->rend(); ++iter1) {// there is actually only one
MdsRLockGuard lg(iter1->v_.lock_);// lock row
for (NodeIter iter2 = iter1->v_.rbegin(); iter2 != iter1->v_.rend(); ++iter2) {
if (iter2->is_committed_()) {
cnt_committed += 1;
}
}
int64_t cnt = std::count_if(iter1->v_.begin(), iter1->v_.end(), [](UserMdsNode<DummyKey, ExampleUserData1> &node){ return node.is_committed_(); });
ASSERT_EQ(cnt_committed, cnt);
}
}
}
void TestMdsTable::OB_iterator() {
ObMdsUnitRowNodeScanIterator<ExampleUserKey, ExampleUserData1> iter;
ExampleUserKey key;
UserMdsNode<ExampleUserKey, ExampleUserData1> *p_node = nullptr;
ASSERT_EQ(OB_SUCCESS, iter.init(mds_table_));
ASSERT_EQ(OB_SUCCESS, iter.get_next(key, p_node));
MDS_LOG(INFO, "print iter kv", K(key), K(*p_node));
ASSERT_EQ(ExampleUserKey(1), key);
{
ASSERT_EQ(ExampleUserData1(2), p_node->user_data_);
ASSERT_EQ(true, p_node->is_committed_());
ASSERT_EQ(mock_scn(2), p_node->get_commit_version_());
}
ASSERT_EQ(OB_SUCCESS, iter.get_next(key, p_node));
MDS_LOG(INFO, "print iter kv", K(key), K(*p_node));
ASSERT_EQ(ExampleUserKey(1), key);
{
ASSERT_EQ(ExampleUserData1(1), p_node->user_data_);
ASSERT_EQ(true, p_node->is_committed_());
ASSERT_EQ(mock_scn(1), p_node->get_commit_version_());
}
ASSERT_EQ(OB_SUCCESS, iter.get_next(key, p_node));
ASSERT_EQ(ExampleUserKey(2), key);
ASSERT_EQ(OB_ITER_END, iter.get_next(key, p_node));
}
// <DummyKey, ExampleUserData1>: (data:1, writer:1, ver:10) -> (data:3, writer:1, ver:9)
// <DummyKey, ExampleUserData2>: (data:20, writer:20, ver:20) -> (data:2, writer:1, ver:10)
// <ExampleUserKey, ExampleUserData1> : <1> : (data:100, writer:100, ver:19001) -> (data:2, writer:2, ver:2) -> (data:1, writer:1, ver:1)
// <2> : (data:200, writer:200, ver:MAX)
void TestMdsTable::test_flush() {
ExampleUserKey key(1);
ExampleUserData1 data1(100);
MdsCtx ctx(mds::MdsWriter(ObTransID(100)));// commit finally
ASSERT_EQ(OB_SUCCESS, mds_table_.set(key, data1, ctx));
ctx.on_redo(mock_scn(19001));
ctx.before_prepare();
ctx.on_prepare(mock_scn(19001));
ctx.on_commit(mock_scn(19002), mock_scn(19002));
ExampleUserKey key2(2);
ExampleUserData1 data2(200);
MdsCtx ctx2(mds::MdsWriter(ObTransID(200)));// abort finally
ASSERT_EQ(OB_SUCCESS, mds_table_.set(key2, data2, ctx2));
ctx2.on_redo(mock_scn(200));
int idx = 0;
ASSERT_EQ(OB_SUCCESS, mds_table_.flush(mock_scn(300)));// 1. 以300为版本号进行flush动作
ASSERT_EQ(mock_scn(199), mds_table_.p_mds_table_base_->flushing_scn_);// 2. 实际上以199为版本号进行flush动作
ASSERT_EQ(OB_SUCCESS, mds_table_.for_each_unit_from_small_key_to_big_from_old_node_to_new_to_dump(
[&idx](const MdsDumpKV &kv) -> int {// 2. 转储时扫描mds table
OB_ASSERT(kv.v_.end_scn_ < mock_scn(199));// 扫描时看不到199版本以上的提交
OB_ASSERT(idx < 10);
MDS_LOG(INFO, "print dump node kv", K(kv));
return OB_SUCCESS;
}, true)
);
mds_table_.on_flush(mock_scn(199), OB_SUCCESS);// 3. 推大rec_scn【至少】到200
share::SCN rec_scn;
ASSERT_EQ(OB_SUCCESS, mds_table_.get_rec_scn(rec_scn));
MDS_LOG(INFO, "print rec scn", K(rec_scn));
ASSERT_EQ(rec_scn, mock_scn(200));
MdsCtx ctx3(mds::MdsWriter(ObTransID(101)));// abort finally
ASSERT_EQ(OB_SUCCESS, mds_table_.replay(ExampleUserKey(111), ExampleUserData1(111), ctx3, mock_scn(100)));
ASSERT_EQ(OB_SUCCESS, mds_table_.get_rec_scn(rec_scn));
ASSERT_EQ(rec_scn, mock_scn(100));
ctx3.on_abort(mock_scn(100));
ScanOp op;
// 未转储:一个已决node + 一个未决node
MDS_LOG(INFO, "print free times", K(oceanbase::storage::mds::MdsAllocator::get_alloc_times()), K(oceanbase::storage::mds::MdsAllocator::get_free_times()));// 回收已转储的node
ASSERT_EQ(OB_SUCCESS, mds_table_.try_recycle(mock_scn(200)));
// ASSERT_EQ(OB_SUCCESS, mds_table_.for_each_scan_node(op));
// ASSERT_EQ(op.valid_count_, 2);
ctx2.on_abort(mock_scn(200));
MDS_LOG(INFO, "print free times", K(oceanbase::storage::mds::MdsAllocator::get_free_times()));// 回收已转储的node
}
// <ExampleUserKey, ExampleUserData1> : <1> : (data:100, writer:100, ver:19001)
void TestMdsTable::test_is_locked_by_others() {
int ret = OB_SUCCESS;
bool is_locked = false;
ret = mds_table_.is_locked_by_others<ExampleUserKey, ExampleUserData1>(ExampleUserKey(1), is_locked);
ASSERT_EQ(OB_SUCCESS, ret);
ASSERT_EQ(false, is_locked);
ExampleUserData1 data1(300);
MdsCtx ctx(mds::MdsWriter(ObTransID(100)));// abort finally
ASSERT_EQ(OB_SUCCESS, mds_table_.set(ExampleUserKey(1), data1, ctx));
ret = mds_table_.is_locked_by_others<ExampleUserKey, ExampleUserData1>(ExampleUserKey(1), is_locked);
ASSERT_EQ(OB_SUCCESS, ret);
ASSERT_EQ(true, is_locked);
ret = mds_table_.is_locked_by_others<ExampleUserKey, ExampleUserData1>(ExampleUserKey(1), is_locked, mds::MdsWriter(ObTransID(100)));
ASSERT_EQ(OB_SUCCESS, ret);
ASSERT_EQ(false, is_locked);
}
// <ExampleUserKey, ExampleUserData1> : <1> : (data:100, writer:100, ver:19001)
void TestMdsTable::test_multi_key_remove() {
bool is_committed = false;
int ret = mds_table_.get_latest<ExampleUserKey, ExampleUserData1>(ExampleUserKey(1), [](const ExampleUserData1 &data){
return OB_SUCCESS;
}, is_committed);
ASSERT_EQ(OB_SUCCESS, ret);
ASSERT_EQ(OB_SUCCESS, mds_table_.flush(mock_scn(200)));
ASSERT_EQ(OB_SUCCESS, mds_table_.try_recycle(mock_scn(200)));
ret = mds_table_.get_latest<ExampleUserKey, ExampleUserData1>(ExampleUserKey(2), [](const ExampleUserData1 &data){
return OB_SUCCESS;
}, is_committed);
ASSERT_EQ(OB_ENTRY_NOT_EXIST, ret);
MdsCtx ctx(mds::MdsWriter(ObTransID(1)));
ret = mds_table_.remove<ExampleUserKey, ExampleUserData1>(ExampleUserKey(1), ctx);
ASSERT_EQ(OB_SUCCESS, ret);
ret = mds_table_.get_latest<ExampleUserKey, ExampleUserData1>(ExampleUserKey(1), [](const ExampleUserData1 &data){
return OB_SUCCESS;
}, is_committed);
ASSERT_EQ(OB_ENTRY_NOT_EXIST, ret);
}
TEST_F(TestMdsTable, set) { TestMdsTable::set(); }
TEST_F(TestMdsTable, replay) { TestMdsTable::replay(); }
TEST_F(TestMdsTable, get_latest) { TestMdsTable::get_latest(); }
TEST_F(TestMdsTable, get_snapshot) { TestMdsTable::get_snapshot(); }
TEST_F(TestMdsTable, get_snapshot_hung_1s) { TestMdsTable::get_snapshot_hung_1s(); }
TEST_F(TestMdsTable, get_by_writer) { TestMdsTable::get_by_writer(); }
TEST_F(TestMdsTable, insert_multi_row) { TestMdsTable::insert_multi_row(); }
TEST_F(TestMdsTable, get_multi_row) { TestMdsTable::get_multi_row(); }
TEST_F(TestMdsTable, test_standard_style_iterator) { TestMdsTable::standard_iterator(); }
TEST_F(TestMdsTable, test_OB_style_iterator) { TestMdsTable::OB_iterator(); }
TEST_F(TestMdsTable, test_flush) { TestMdsTable::test_flush(); }
TEST_F(TestMdsTable, test_is_locked_by_others) { TestMdsTable::test_is_locked_by_others(); }
TEST_F(TestMdsTable, test_multi_key_remove) { TestMdsTable::test_multi_key_remove(); }
TEST_F(TestMdsTable, basic_trans_example) {
MdsTableHandle mth;
// 1. 初始化为UnitTestMdsTable
ASSERT_EQ(OB_SUCCESS, mth.init<UnitTestMdsTable>(mds::DefaultAllocator::get_instance(),
ObTabletID(1),
share::ObLSID(1),
nullptr));
MdsTableHandle mth2 = mth;// 两个引用计数
MdsCtx ctx(mds::MdsWriter(ObTransID(123)));// 创建一个写入句柄,接入多源事务,ctx由事务层创建
// 2. 写入数据
ASSERT_EQ(OB_SUCCESS, mth.set(ExampleUserData1(1), ctx));// 写入第一个数据单元,成功
ASSERT_EQ(OB_OBJ_TYPE_ERROR, mth.set((int)54321, ctx));// 写入第二个数据单元,但UnitTestMdsTable并未注册该类型数据,Type ERROR
// 3. 对写入数据写CLOG并进行两阶段提交,接入多源事务,则该流程由事务层代为执行, 用户无感知
ctx.on_redo(mock_scn(100));
ctx.before_prepare();
ctx.on_prepare(mock_scn(100));
ctx.on_commit(mock_scn(100), mock_scn(100));
// 4. 读取最新已提交数据
ASSERT_EQ(OB_SUCCESS, mth.get_snapshot<ExampleUserData1>([](const ExampleUserData1 &data) {
return data.value_ != 1 ? OB_ERR_UNEXPECTED : OB_SUCCESS;
}));
}// 5. 最后一个Handle析构的时候,MdsTable发生真正的析构行为
TEST_F(TestMdsTable, basic_non_trans_example) {
MdsTableHandle mth;
// 1. 初始化为UnitTestMdsTable
ASSERT_EQ(OB_SUCCESS, mth.init<UnitTestMdsTable>(mds::DefaultAllocator::get_instance(),
ObTabletID(1),
share::ObLSID(1),
nullptr));
MdsTableHandle mth2 = mth;// 两个引用计数
MdsCtx ctx(MdsWriter(WriterType::AUTO_INC_SEQ, 1));// 创建一个写入句柄,不接入事务,自己写日志
// 2. 写入数据
ASSERT_EQ(OB_SUCCESS, mth.set(ExampleUserData1(1), ctx));// 写入第一个数据单元,成功
ASSERT_EQ(OB_OBJ_TYPE_ERROR, mth.set((int)54321, ctx));// 写入第二个数据单元,但UnitTestMdsTable并未注册该类型数据,Type ERROR
// 3. 对写入数据写CLOG并进行单条日志提交
ctx.single_log_commit(mock_scn(100), mock_scn(100));
// 4. 读取最新已提交数据
ASSERT_EQ(OB_SUCCESS, mth.get_snapshot<ExampleUserData1>([](const ExampleUserData1 &data) {
return data.value_ != 1 ? OB_ERR_UNEXPECTED : OB_SUCCESS;
}));
}// 5. 最后一个Handle析构的时候,MdsTable发生真正的析构行为
TEST_F(TestMdsTable, test_recycle) {
int64_t alloc_times = oceanbase::storage::mds::MdsAllocator::get_alloc_times();
int64_t free_times = oceanbase::storage::mds::MdsAllocator::get_free_times();
ASSERT_NE(alloc_times, free_times);
ASSERT_EQ(OB_SUCCESS, mds_table_.try_recycle(mock_scn(20000)));
int64_t valid_cnt = 0;
ASSERT_EQ(OB_SUCCESS, mds_table_.get_node_cnt(valid_cnt));
ASSERT_EQ(1, valid_cnt);// 此时还有一个19001版本的已提交数据,因为rec_scn没有推上去
ASSERT_EQ(OB_SUCCESS, mds_table_.flush(mock_scn(20000)));
mds_table_.for_each_unit_from_small_key_to_big_from_old_node_to_new_to_dump([](const MdsDumpKV &){
return OB_SUCCESS;
}, true);
mds_table_.on_flush(mock_scn(20000), OB_SUCCESS);
share::SCN rec_scn;
ASSERT_EQ(OB_SUCCESS, mds_table_.get_rec_scn(rec_scn));
MDS_LOG(INFO, "print rec scn", K(rec_scn));
ASSERT_EQ(share::SCN::max_scn(), rec_scn);
ASSERT_EQ(OB_SUCCESS, mds_table_.try_recycle(mock_scn(20000)));
ASSERT_EQ(OB_SUCCESS, mds_table_.get_node_cnt(valid_cnt));
ASSERT_EQ(0, valid_cnt);// 此时还有一个19001版本的已提交数据,因为rec_scn没有推上去
}
TEST_F(TestMdsTable, test_recalculate_flush_scn_op) {
MdsTableHandle mds_table;
ASSERT_EQ(OB_SUCCESS, mds_table.init<UnitTestMdsTable>(MdsAllocator::get_instance(), ObTabletID(1), share::ObLSID(1), (ObTabletPointer*)0x111));
MdsCtx ctx1(mds::MdsWriter(ObTransID(1)));
MdsCtx ctx2(mds::MdsWriter(ObTransID(2)));
MdsCtx ctx3(mds::MdsWriter(ObTransID(3)));
ASSERT_EQ(OB_SUCCESS, mds_table.set(ExampleUserData1(1), ctx1));
ctx1.on_redo(mock_scn(1));
ctx1.on_commit(mock_scn(3), mock_scn(3));
ASSERT_EQ(OB_SUCCESS, mds_table.set(ExampleUserData1(2), ctx2));
ctx2.on_redo(mock_scn(5));
ctx2.on_commit(mock_scn(7), mock_scn(7));
ASSERT_EQ(OB_SUCCESS, mds_table.set(ExampleUserData1(3), ctx3));
ctx3.on_redo(mock_scn(9));
ctx3.on_commit(mock_scn(11), mock_scn(11));
ASSERT_EQ(OB_SUCCESS, mds_table.flush(mock_scn(4)));
ASSERT_EQ(mock_scn(4), mds_table.p_mds_table_base_->flushing_scn_);
mds_table.on_flush(mock_scn(4), OB_SUCCESS);
ASSERT_EQ(OB_SUCCESS, mds_table.flush(mock_scn(5)));// no need do flush, directly advance rec_scn
ASSERT_EQ(false, mds_table.p_mds_table_base_->flushing_scn_.is_valid());
ASSERT_EQ(OB_SUCCESS, mds_table.flush(mock_scn(6)));// no need do flush, directly advance rec_scn
ASSERT_EQ(false, mds_table.p_mds_table_base_->flushing_scn_.is_valid());
ASSERT_EQ(OB_SUCCESS, mds_table.flush(mock_scn(7)));
ASSERT_EQ(mock_scn(7), mds_table.p_mds_table_base_->flushing_scn_);
mds_table.on_flush(mock_scn(7), OB_SUCCESS);
ASSERT_EQ(OB_SUCCESS, mds_table.flush(mock_scn(8)));
ASSERT_EQ(false, mds_table.p_mds_table_base_->flushing_scn_.is_valid());
ASSERT_EQ(mock_scn(9), mds_table.p_mds_table_base_->rec_scn_);
}
// TEST_F(TestMdsTable, test_node_commit_in_row) {
// MdsRow<DummyKey, ExampleUserData1> row;
// MdsCtx ctx1(mds::MdsWriter(WriterType::AUTO_INC_SEQ, 1));
// ASSERT_EQ(OB_SUCCESS, row.set(ExampleUserData1(1), ctx1, 0));
// ctx1.single_log_commit(mock_scn(1), mock_scn(1));
// MdsCtx ctx2(mds::MdsWriter(WriterType::AUTO_INC_SEQ, 2));
// ASSERT_EQ(OB_SUCCESS, row.set(ExampleUserData1(2), ctx2, 0));
// ctx1.single_log_commit(mock_scn(2), mock_scn(2));// won't release last committed node
// int node_cnt = 0;
// row.sorted_list_.for_each_node_from_head_to_tail_until_true([&node_cnt](const UserMdsNode<DummyKey, ExampleUserData1> &){ ++node_cnt; return false; });
// ASSERT_EQ(2, node_cnt);
// }
TEST_F(TestMdsTable, test_rw_lock_rrlock) {
MdsLock lock;
std::thread t1([&lock]() {
MdsRLockGuard lg(lock);
ob_usleep(1_s);
});
std::thread t2([&lock]() {
MdsRLockGuard lg(lock);
ob_usleep(1_s);
});
t1.join();
t2.join();
}
TEST_F(TestMdsTable, test_rw_lock_wrlock) {
MdsLock lock;
std::thread t1([&lock]() {
MdsWLockGuard lg(lock);
ob_usleep(1_s);
});
std::thread t2([&lock]() {
ob_usleep(200_ms);
MdsRLockGuard lg(lock);
ob_usleep(1_s);
});
t1.join();
t2.join();
}
TEST_F(TestMdsTable, test_rw_lock_rwlock) {
MdsLock lock;
std::thread t1([&lock]() {
ob_usleep(200_ms);
MdsWLockGuard lg(lock);
ob_usleep(1_s);
});
std::thread t2([&lock]() {
MdsRLockGuard lg(lock);
ob_usleep(1_s);
});
t1.join();
t2.join();
}
TEST_F(TestMdsTable, test_rw_lock_wwlock) {
MdsLock lock;
std::thread t1([&lock]() {
MdsWLockGuard lg(lock);
ob_usleep(1_s);
});
std::thread t2([&lock]() {
MdsWLockGuard lg(lock);
ob_usleep(1_s);
});
t1.join();
t2.join();
}
}
}
int main(int argc, char **argv)
{
system("rm -rf test_mds_table.log");
oceanbase::common::ObLogger &logger = oceanbase::common::ObLogger::get_logger();
logger.set_file_name("test_mds_table.log", false);
logger.set_log_level(OB_LOG_LEVEL_DEBUG);
testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
oceanbase::unittest::mds_table_.~MdsTableHandle();
int64_t alloc_times = oceanbase::storage::mds::MdsAllocator::get_alloc_times();
int64_t free_times = oceanbase::storage::mds::MdsAllocator::get_free_times();
if (alloc_times != free_times) {
MDS_LOG(ERROR, "memory may leak", K(free_times), K(alloc_times));
ret = -1;
} else {
MDS_LOG(INFO, "all memory released", K(free_times), K(alloc_times));
}
return ret;
}

View File

@ -0,0 +1,256 @@
/**
* 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 "lib/utility/utility.h"
#include "storage/multi_data_source/compile_utility/mds_dummy_key.h"
#include <gtest/gtest.h>
#define private public
#define protected public
#include "multi_data_source/example_user_data_define.h"
#include "share/ob_ls_id.h"
#include "storage/multi_data_source/mds_writer.h"
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>
#include <exception>
#include "common_define.h"
#include "lib/ob_errno.h"
#include "share/ob_errno.h"
#include "storage/multi_data_source/adapter_define/mds_dump_node.h"
#include "lib/allocator/ob_malloc.h"
#include "storage/multi_data_source/mds_node.h"
#include "common/ob_clock_generator.h"
#include "storage/multi_data_source/mds_row.h"
#include "storage/multi_data_source/mds_unit.h"
#include "storage/multi_data_source/mds_table_handle.h"
#include "storage/multi_data_source/mds_table_handler.h"
#include "example_user_helper_define.cpp"
#include "storage/tx/ob_trans_define.h"
#include <algorithm>
#include <numeric>
#include "storage/multi_data_source/runtime_utility/mds_lock.h"
#include "storage/tablet/ob_tablet_meta.h"
namespace oceanbase {
namespace storage
{
share::SCN MOCK_MAX_CONSEQUENT_CALLBACKED_SCN;
share::SCN MOCK_FLUSHING_SCN;
namespace mds
{
void *MdsAllocator::alloc(const int64_t size)
{
void *ptr = ob_malloc(size, "MDS");
ATOMIC_INC(&alloc_times_);
MDS_LOG(DEBUG, "alloc obj", KP(ptr), K(size), K(lbt()));
return ptr;
}
void MdsAllocator::free(void *ptr) {
ATOMIC_INC(&free_times_);
MDS_LOG(DEBUG, "free obj", KP(ptr), K(lbt()));
ob_free(ptr);
}
int MdsTableBase::get_ls_max_consequent_callbacked_scn_(share::SCN &max_consequent_callbacked_scn) const
{
max_consequent_callbacked_scn = MOCK_MAX_CONSEQUENT_CALLBACKED_SCN;
return OB_SUCCESS;
}
int MdsTableBase::merge(const share::SCN &flushing_scn)
{
MOCK_FLUSHING_SCN = flushing_scn;
return OB_SUCCESS;
}
}
}
namespace unittest {
using namespace common;
using namespace std;
using namespace storage;
using namespace mds;
using namespace transaction;
class TestMdsTableFlush: public ::testing::Test
{
public:
TestMdsTableFlush() {}
virtual ~TestMdsTableFlush() {}
virtual void SetUp() {
}
virtual void TearDown() {
}
private:
// disallow copy
DISALLOW_COPY_AND_ASSIGN(TestMdsTableFlush);
};
// max_decided_scn:475
// │
// │
// │
// ┌──────────┐ │ ┌──────────┐ ┌──────────┐
// MdsUnit<DummyKey, ExampleUserData1> │(MAX, 500]│◄─┼──────────────────────┤[300, 250]│◄──────────────────────────┤(100, 50]│
// └──────────┘ │ └──────────┘ └──────────┘
// │
// │
// │
// │
// │ ┌──────────┐ ┌──────────┐
// MdsUnit<DummyKey, ExampleUserData2> │ │[400, 350]│◄──────────────────────────┤[225, 200]│
// │ └──────────┘ └──────────┘
// │
// │
// │
// │
int construct_tested_mds_table(MdsTableHandle &handle) {
int ret = OB_SUCCESS;
handle.reset();
vector<MdsCtx*> v_ctx;
for (int i = 0; i < 7; ++i) {
v_ctx.push_back(new MdsCtx(MdsWriter(transaction::ObTransID(i))));
}
if (OB_FAIL(handle.init<UnitTestMdsTable>(MdsAllocator::get_instance(), ObTabletID(1), share::ObLSID(1), (ObTabletPointer*)0x111))) {
} else if (OB_FAIL(handle.set<ExampleUserData1>(1, *v_ctx[0]))) {
} else if (FALSE_IT(v_ctx[0]->on_redo(mock_scn(50)))) {
} else if (FALSE_IT(v_ctx[0]->on_commit(mock_scn(100), mock_scn(100)))) {
} else if (OB_FAIL(handle.set<ExampleUserData1>(2, *v_ctx[1]))) {
} else if (FALSE_IT(v_ctx[1]->on_redo(mock_scn(250)))) {
} else if (FALSE_IT(v_ctx[1]->on_commit(mock_scn(300), mock_scn(300)))) {
} else if (OB_FAIL(handle.set<ExampleUserData1>(3, *v_ctx[2]))) {
} else if (FALSE_IT(v_ctx[2]->on_redo(mock_scn(500)))) {
} else if (OB_FAIL(handle.set<ExampleUserData2>(ExampleUserData2(), *v_ctx[3]))) {
} else if (FALSE_IT(v_ctx[3]->on_redo(mock_scn(200)))) {
} else if (FALSE_IT(v_ctx[3]->on_commit(mock_scn(225), mock_scn(225)))) {
} else if (OB_FAIL(handle.set<ExampleUserData2>(ExampleUserData2(), *v_ctx[4]))) {
} else if (FALSE_IT(v_ctx[4]->on_redo(mock_scn(350)))) {
} else if (FALSE_IT(v_ctx[4]->on_commit(mock_scn(400), mock_scn(400)))) {
} else if (OB_SUCCESS != (ret = handle.set<ExampleUserKey, ExampleUserData1>(ExampleUserKey(1), ExampleUserData1(1), *v_ctx[5]))) {
} else if (OB_SUCCESS != (ret = handle.set<ExampleUserKey, ExampleUserData1>(ExampleUserKey(2), ExampleUserData1(2), *v_ctx[6]))) {
}
v_ctx[6]->on_abort(mock_scn(10));
return ret;
}
TEST_F(TestMdsTableFlush, normal_flush) {
MOCK_MAX_CONSEQUENT_CALLBACKED_SCN = mock_scn(125);// 只转储一个node
MdsTableHandle handle;
ASSERT_EQ(OB_SUCCESS, construct_tested_mds_table(handle));
share::SCN rec_scn;
ASSERT_EQ(OB_SUCCESS, handle.get_rec_scn(rec_scn));
ASSERT_EQ(mock_scn(50), rec_scn);// 没转储的时候是最小的node的redo scn值
// 第一次转储
ASSERT_EQ(OB_SUCCESS, handle.flush(mock_scn(1000)));// 因为max_decided_scn较小,所以会用125做flush
bool is_flusing = false;
ASSERT_EQ(OB_SUCCESS, handle.is_flushing(is_flusing));// 在flush流程中
ASSERT_EQ(true, is_flusing);
ASSERT_EQ(mock_scn(125), handle.p_mds_table_base_->flushing_scn_);
int scan_cnt = 0;
ASSERT_EQ(OB_SUCCESS, handle.for_each_unit_from_small_key_to_big_from_old_node_to_new_to_dump([&scan_cnt](const MdsDumpKV &kv) -> int {
scan_cnt++;
return OB_SUCCESS;
}, true));
ASSERT_EQ(1, scan_cnt);
handle.on_flush(MOCK_FLUSHING_SCN, OB_SUCCESS);
ASSERT_EQ(OB_SUCCESS, handle.get_rec_scn(rec_scn));
OCCAM_LOG(INFO, "print rec scn", K(rec_scn));
ASSERT_EQ(mock_scn(200), rec_scn);
// 第二次转储
MOCK_MAX_CONSEQUENT_CALLBACKED_SCN = mock_scn(140);// 对这个MdsTable没有影响
ASSERT_EQ(OB_SUCCESS, handle.flush(mock_scn(1000)));
ASSERT_EQ(OB_SUCCESS, handle.is_flushing(is_flusing));
ASSERT_EQ(false, is_flusing);// 没转储
ASSERT_EQ(OB_SUCCESS, handle.get_rec_scn(rec_scn));
OCCAM_LOG(INFO, "print rec scn", K(rec_scn));
ASSERT_EQ(mock_scn(200), rec_scn);// 没变化
// 第三次转储
MOCK_MAX_CONSEQUENT_CALLBACKED_SCN = mock_scn(275);// 多转一个node
ASSERT_EQ(OB_SUCCESS, handle.flush(mock_scn(1000)));
ASSERT_EQ(OB_SUCCESS, handle.is_flushing(is_flusing));// 在flush流程中
ASSERT_EQ(true, is_flusing);
ASSERT_EQ(mock_scn(249), handle.p_mds_table_base_->flushing_scn_);
scan_cnt = 0;
ASSERT_EQ(OB_SUCCESS, handle.for_each_unit_from_small_key_to_big_from_old_node_to_new_to_dump([&scan_cnt](const MdsDumpKV &kv) -> int {
scan_cnt++;
return OB_SUCCESS;
}, true));
ASSERT_EQ(1, scan_cnt);
handle.on_flush(MOCK_FLUSHING_SCN, OB_SUCCESS);
ASSERT_EQ(OB_SUCCESS, handle.get_rec_scn(rec_scn));
OCCAM_LOG(INFO, "print rec scn", K(rec_scn));
ASSERT_EQ(mock_scn(250), rec_scn);
// 第四次转储
MOCK_MAX_CONSEQUENT_CALLBACKED_SCN = mock_scn(550);// 都转下去
ASSERT_EQ(OB_SUCCESS, handle.flush(mock_scn(1000)));
ASSERT_EQ(OB_SUCCESS, handle.is_flushing(is_flusing));// 在flush流程中
ASSERT_EQ(true, is_flusing);
ASSERT_EQ(mock_scn(499), handle.p_mds_table_base_->flushing_scn_);
scan_cnt = 0;
ASSERT_EQ(OB_SUCCESS, handle.for_each_unit_from_small_key_to_big_from_old_node_to_new_to_dump([&scan_cnt](const MdsDumpKV &kv) -> int {
scan_cnt++;
return OB_SUCCESS;
}, true));
ASSERT_EQ(2, scan_cnt);
handle.on_flush(MOCK_FLUSHING_SCN, OB_SUCCESS);
ASSERT_EQ(OB_SUCCESS, handle.get_rec_scn(rec_scn));
OCCAM_LOG(INFO, "print rec scn", K(rec_scn));
ASSERT_EQ(mock_scn(500), rec_scn);
// 第五次转储
MOCK_MAX_CONSEQUENT_CALLBACKED_SCN = mock_scn(600);// 对这个MdsTable没有影响
ASSERT_EQ(OB_SUCCESS, handle.flush(mock_scn(1000)));
ASSERT_EQ(OB_SUCCESS, handle.is_flushing(is_flusing));
ASSERT_EQ(false, is_flusing);// 没转储
ASSERT_EQ(OB_SUCCESS, handle.get_rec_scn(rec_scn));
OCCAM_LOG(INFO, "print rec scn", K(rec_scn));
ASSERT_EQ(mock_scn(500), rec_scn);// 没变化
// 第六次转储
MOCK_MAX_CONSEQUENT_CALLBACKED_SCN = mock_scn(590);// 直接被过滤掉了
ASSERT_EQ(OB_SUCCESS, handle.flush(mock_scn(1000)));
ASSERT_EQ(OB_SUCCESS, handle.is_flushing(is_flusing));
ASSERT_EQ(false, is_flusing);// 没转储
ASSERT_EQ(OB_SUCCESS, handle.get_rec_scn(rec_scn));
OCCAM_LOG(INFO, "print rec scn", K(rec_scn));
ASSERT_EQ(mock_scn(500), rec_scn);// 没变化
}
}
}
int main(int argc, char **argv)
{
system("rm -rf test_mds_table_flush.log");
oceanbase::common::ObLogger &logger = oceanbase::common::ObLogger::get_logger();
logger.set_file_name("test_mds_table_flush.log", false);
logger.set_log_level(OB_LOG_LEVEL_DEBUG);
testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
int64_t alloc_times = oceanbase::storage::mds::MdsAllocator::get_alloc_times();
int64_t free_times = oceanbase::storage::mds::MdsAllocator::get_free_times();
if (alloc_times != free_times) {
MDS_LOG(ERROR, "memory may leak", K(free_times), K(alloc_times));
ret = -1;
} else {
MDS_LOG(INFO, "all memory released", K(free_times), K(alloc_times));
}
return ret;
}

View File

@ -0,0 +1,82 @@
/**
* 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 "multi_data_source/example_user_data_define.h"
#include "share/ob_errno.h"
#include <gtest/gtest.h>
#define USING_LOG_PREFIX STORAGE
#define protected public
#define private public
#include "lib/guard/ob_light_shared_gaurd.h"
#include "storage/multi_data_source/mds_table_impl.h"
#include "storage/tablet/ob_tablet_meta.h"
namespace oceanbase
{
//using namespace share;
namespace storage
{
namespace mds
{
void *MdsAllocator::alloc(const int64_t size)
{
void *ptr = ob_malloc(size, "MDS");
ATOMIC_INC(&alloc_times_);
MDS_LOG(DEBUG, "alloc obj", KP(ptr), K(lbt()));
return ptr;
}
void MdsAllocator::free(void *ptr) {
ATOMIC_INC(&free_times_);
MDS_LOG(DEBUG, "free obj", KP(ptr), K(lbt()));
ob_free(ptr);
}
}
class TestMdsTableHandle : public ::testing::Test
{
public:
TestMdsTableHandle() {}
virtual ~TestMdsTableHandle() = default;
virtual void SetUp() override {}
virtual void TearDown() override {}
};
TEST_F(TestMdsTableHandle, normal) {
ObLightSharedPtr<unittest::ExampleUserData1> lsp;
lsp.construct(mds::MdsAllocator::get_instance());
ObLightSharedPtr<unittest::ExampleUserData1> lsp2 = lsp;
}
} // end namespace storage
} // end namespace oceanbase
int main(int argc, char **argv)
{
int ret = 0;
system("rm -f test_mds_table_handle.log*");
OB_LOGGER.set_file_name("test_mds_table_handle.log", true);
OB_LOGGER.set_log_level("INFO");
signal(49, SIG_IGN);
testing::InitGoogleTest(&argc, argv);
ret = RUN_ALL_TESTS();
int64_t alloc_times = oceanbase::storage::mds::MdsAllocator::get_alloc_times();
int64_t free_times = oceanbase::storage::mds::MdsAllocator::get_free_times();
if (alloc_times != free_times) {
MDS_LOG(ERROR, "memory may leak", K(free_times), K(alloc_times));
ret = -1;
} else {
MDS_LOG(INFO, "all memory released", K(free_times), K(alloc_times));
}
return ret;
}

View File

@ -0,0 +1,92 @@
/**
* 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.
*/
#define UNITTEST_DEBUG
#include <gtest/gtest.h>
#include "lib/ob_errno.h"
#include "lib/utility/utility.h"
#include "share/ob_errno.h"
#include <exception>
#include "common_define.h"
#include "lib/allocator/ob_malloc.h"
#include "storage/multi_data_source/mds_node.h"
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>
#include "common/ob_clock_generator.h"
#include "storage/multi_data_source/mds_row.h"
#include "storage/multi_data_source/mds_unit.h"
#include "example_user_helper_define.cpp"
namespace oceanbase {
namespace unittest {
using namespace common;
using namespace std;
using namespace storage;
using namespace mds;
class TestMdsUnit: public ::testing::Test
{
public:
TestMdsUnit() {};
virtual ~TestMdsUnit() {};
virtual void SetUp() {
};
virtual void TearDown() {
};
static void set_single_row();
static void get_single_row();
static MdsUnit<DummyKey, ExampleUserData2> signle_row_unit_;
private:
// disallow copy
DISALLOW_COPY_AND_ASSIGN(TestMdsUnit);
};
MdsUnit<DummyKey, ExampleUserData2> TestMdsUnit::signle_row_unit_;
void TestMdsUnit::set_single_row() {
ExampleUserData2 data(1);
MdsCtx ctx(1);
}
void TestMdsUnit::get_single_row() {
}
TEST_F(TestMdsUnit, set_single_row) { TestMdsUnit::set_single_row(); }
TEST_F(TestMdsUnit, get_single_row) { TestMdsUnit::get_single_row(); }
}
}
int main(int argc, char **argv)
{
system("rm -rf test_mds_unit.log");
oceanbase::common::ObLogger &logger = oceanbase::common::ObLogger::get_logger();
logger.set_file_name("test_mds_unit.log", false);
logger.set_log_level(OB_LOG_LEVEL_TRACE);
testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
oceanbase::unittest::TestMdsUnit::signle_row_unit_.~MdsUnit();
int64_t alloc_times = oceanbase::storage::mds::MdsAllocator::get_alloc_times();
int64_t free_times = oceanbase::storage::mds::MdsAllocator::get_free_times();
if (alloc_times != free_times) {
MDS_LOG(ERROR, "memory may leak", K(free_times), K(alloc_times));
if (ret == 0) {
ret = -1;
}
} else {
MDS_LOG(INFO, "all memory released", K(free_times), K(alloc_times));
}
return ret;
}