Files
oceanbase/mittest/mtlenv/storage/test_mds_compat.cpp

462 lines
15 KiB
C++

// owner: gaishun.gs
// owner group: storage
/**
* Copyright (c) 2024 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>
#define protected public
#define private public
#include "mtlenv/storage/medium_info_helper.h"
#include "unittest/storage/test_tablet_helper.h"
#include "unittest/storage/test_dml_common.h"
#include "storage/tablet/ob_tablet_mds_table_mini_merger.h"
#define USING_LOG_PREFIX STORAGE
using namespace oceanbase::common;
using namespace oceanbase::share;
namespace oceanbase
{
namespace storage
{
class TestMdsCompat : public::testing::Test
{
public:
TestMdsCompat() = default;
virtual ~TestMdsCompat() = default;
public:
static void SetUpTestCase();
static void TearDownTestCase();
public:
static int create_ls(const uint64_t tenant_id, const share::ObLSID &ls_id, ObLSHandle &ls_handle);
static int remove_ls(const share::ObLSID &ls_id);
int create_tablet(const common::ObTabletID &tablet_id, ObTabletHandle &tablet_handle);
static int get_tablet(const common::ObTabletID &tablet_id, ObTabletHandle &tablet_handle);
static int wait_for_mds_table_flush(const common::ObTabletID &tablet_id);
static int wait_for_all_mds_nodes_released(const common::ObTabletID &tablet_id);
static int try_schedule_mds_minor(const common::ObTabletID &tablet_id);
public:
static constexpr uint64_t TENANT_ID = 1001;
static const share::ObLSID LS_ID;
public:
common::ObArenaAllocator allocator_;
};
const share::ObLSID TestMdsCompat::LS_ID(1234);
void TestMdsCompat::SetUpTestCase()
{
int ret = OB_SUCCESS;
ret = MockTenantModuleEnv::get_instance().init();
ASSERT_EQ(OB_SUCCESS, ret);
ObServerStorageMetaService::get_instance().is_started_ = true;
// create ls
ObLSHandle ls_handle;
ret = create_ls(TENANT_ID, LS_ID, ls_handle);
ASSERT_EQ(OB_SUCCESS, ret);
}
void TestMdsCompat::TearDownTestCase()
{
int ret = OB_SUCCESS;
// remove ls
ret = remove_ls(LS_ID);
ASSERT_EQ(OB_SUCCESS, ret);
MockTenantModuleEnv::get_instance().destroy();
}
int TestMdsCompat::create_ls(const uint64_t tenant_id, const share::ObLSID &ls_id, ObLSHandle &ls_handle)
{
int ret = OB_SUCCESS;
ret = TestDmlCommon::create_ls(tenant_id, ls_id, ls_handle);
return ret;
}
int TestMdsCompat::remove_ls(const share::ObLSID &ls_id)
{
int ret = OB_SUCCESS;
ret = MTL(ObLSService*)->remove_ls(ls_id);
return ret;
}
int TestMdsCompat::create_tablet(const common::ObTabletID &tablet_id, ObTabletHandle &tablet_handle)
{
int ret = OB_SUCCESS;
const uint64_t table_id = 1234567;
share::schema::ObTableSchema table_schema;
ObLSHandle ls_handle;
ObLS *ls = nullptr;
if (OB_FAIL(MTL(ObLSService*)->get_ls(LS_ID, ls_handle, ObLSGetMod::STORAGE_MOD))) {
LOG_WARN("failed to get ls", K(ret));
} else if (OB_ISNULL(ls = ls_handle.get_ls())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("ls is null", K(ret), KP(ls));
} else if (OB_FAIL(build_test_schema(table_schema, table_id))) {
LOG_WARN("failed to build table schema");
} else if (OB_FAIL(TestTabletHelper::create_tablet(ls_handle, tablet_id, table_schema, allocator_,
ObTabletStatus::MAX, share::SCN::invalid_scn(), tablet_handle))) {
LOG_WARN("failed to create tablet", K(ret));
}
return ret;
}
int TestMdsCompat::get_tablet(const common::ObTabletID &tablet_id, ObTabletHandle &tablet_handle)
{
int ret = OB_SUCCESS;
ObLSHandle ls_handle;
ObLS *ls = nullptr;
if (OB_FAIL(MTL(ObLSService*)->get_ls(LS_ID, ls_handle, ObLSGetMod::STORAGE_MOD))) {
LOG_WARN("failed to get ls", K(ret));
} else if (OB_ISNULL(ls = ls_handle.get_ls())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("ls is null", K(ret), KP(ls));
} else if (OB_FAIL(ls->get_tablet_svr()->direct_get_tablet(tablet_id, tablet_handle))) {
LOG_WARN("failed to get tablet", K(ret), KP(ls));
}
return ret;
}
int TestMdsCompat::wait_for_mds_table_flush(const common::ObTabletID &tablet_id)
{
int ret = OB_SUCCESS;
ObTabletHandle tablet_handle;
ObTablet *tablet = nullptr;
int times = 0;
share::SCN rec_scn = share::SCN::min_scn();
// get before cnt
ret = TestMdsCompat::get_tablet(tablet_id, tablet_handle);
EXPECT_EQ(OB_SUCCESS, ret);
tablet = tablet_handle.get_obj();
EXPECT_NE(nullptr, tablet);
ObTabletMemberWrapper<ObTabletTableStore> table_store_wrapper;
ret = tablet->fetch_table_store(table_store_wrapper);
EXPECT_EQ(OB_SUCCESS, ret);
const ObTabletTableStore *table_store = table_store_wrapper.get_member();
const int64_t mds_sstable_cnt_before = table_store->mds_sstables_.count();
do
{
ret = TestMdsCompat::get_tablet(tablet_id, tablet_handle);
EXPECT_EQ(OB_SUCCESS, ret);
tablet = tablet_handle.get_obj();
EXPECT_NE(nullptr, tablet);
mds::MdsTableHandle mds_table;
ret = tablet->inner_get_mds_table(mds_table, false/*not_exist_create*/);
ret = mds_table.get_rec_scn(rec_scn);
EXPECT_EQ(OB_SUCCESS, ret);
// sleep
::ob_usleep(100_ms);
++times;
} while (OB_SUCCESS == ret && !rec_scn.is_max() && times < 20);
EXPECT_TRUE(rec_scn.is_max());
// check mds sstable
ret = tablet->fetch_table_store(table_store_wrapper);
EXPECT_EQ(OB_SUCCESS, ret);
table_store = table_store_wrapper.get_member();
EXPECT_EQ(mds_sstable_cnt_before + 1, table_store->mds_sstables_.count());
if (::testing::Test::HasFailure()) {
ret = OB_TIMEOUT;
}
return ret;
}
int TestMdsCompat::wait_for_all_mds_nodes_released(const common::ObTabletID &tablet_id)
{
int ret = OB_SUCCESS;
ObTabletHandle tablet_handle;
ObTablet *tablet = nullptr;
int times = 0;
int64_t node_cnt = INT64_MAX;
do {
ret = TestMdsCompat::get_tablet(tablet_id, tablet_handle);
EXPECT_EQ(OB_SUCCESS, ret);
tablet = tablet_handle.get_obj();
EXPECT_NE(nullptr, tablet);
mds::MdsTableHandle mds_table;
ret = tablet->inner_get_mds_table(mds_table, false/*not_exist_create*/);
EXPECT_EQ(OB_SUCCESS, ret);
ret = mds_table.get_node_cnt(node_cnt);
EXPECT_EQ(OB_SUCCESS, ret);
// sleep
::ob_usleep(100_ms);
++times;
} while (OB_SUCCESS == ret && node_cnt != 0 && times < 60);
EXPECT_EQ(0, node_cnt);
if (::testing::Test::HasFailure()) {
ret = OB_TIMEOUT;
}
return ret;
}
TEST_F(TestMdsCompat, migration_param)
{
int ret = OB_SUCCESS;
// create tablet
const common::ObTabletID tablet_id(ObTimeUtility::fast_current_time() % 10000000000000);
ObTabletHandle tablet_handle;
ObTabletStatus status(ObTabletStatus::MAX);
share::SCN create_commit_scn;
create_commit_scn = share::SCN::plus(share::SCN::min_scn(), 50);
ret = create_tablet(tablet_id, tablet_handle);
ASSERT_EQ(OB_SUCCESS, ret);
ObTablet *tablet = tablet_handle.get_obj();
ASSERT_NE(nullptr, tablet);
share::SCN mds_checkpoint_scn = tablet->get_tablet_meta().mds_checkpoint_scn_;
ASSERT_EQ(share::SCN::base_scn(), mds_checkpoint_scn);
share::SCN invalid_scn;
// write data to mds table no.1 row
{
ObTabletCreateDeleteMdsUserData user_data;
user_data.tablet_status_ = ObTabletStatus::NORMAL;
user_data.data_type_ = ObTabletMdsUserDataType::CREATE_TABLET;
mds::MdsCtx ctx(mds::MdsWriter(transaction::ObTransID(123)));
ret = tablet->set_tablet_status(user_data, ctx);
ASSERT_EQ(OB_SUCCESS, ret);
ctx.single_log_commit(create_commit_scn, create_commit_scn);
}
// write data to mds table no.2 row
{
compaction::ObMediumCompactionInfoKey key(100);
compaction::ObMediumCompactionInfo info;
ret = MediumInfoHelper::build_medium_compaction_info(allocator_, info, 100);
ASSERT_EQ(OB_SUCCESS, ret);
mds::MdsCtx ctx(mds::MdsWriter(transaction::ObTransID(777)));
ret = tablet->set(key, info, ctx, 1_s/*lock_timeout_us*/);
ASSERT_EQ(OB_SUCCESS, ret);
share::SCN redo_scn = share::SCN::plus(share::SCN::min_scn(), 110);
ctx.on_redo(redo_scn);
share::SCN commit_scn = share::SCN::plus(share::SCN::min_scn(), 120);
ctx.on_commit(commit_scn, commit_scn);
}
{
compaction::ObMediumCompactionInfoKey key(200);
compaction::ObMediumCompactionInfo info;
ret = MediumInfoHelper::build_medium_compaction_info(allocator_, info, 200);
ASSERT_EQ(OB_SUCCESS, ret);
mds::MdsCtx ctx(mds::MdsWriter(transaction::ObTransID(888)));
ret = tablet->set(key, info, ctx, 1_s/*lock_timeout_us*/);
ASSERT_EQ(OB_SUCCESS, ret);
share::SCN redo_scn = share::SCN::plus(share::SCN::min_scn(), 210);
ctx.on_redo(redo_scn);
share::SCN commit_scn = share::SCN::plus(share::SCN::min_scn(), 220);
ctx.on_commit(commit_scn, commit_scn);
}
// write data to mds table no.3 row
{
ObTabletBindingMdsUserData user_data;
user_data.data_tablet_id_ = 100;
user_data.hidden_tablet_id_ = 101;
user_data.snapshot_version_ = 9527;
mds::MdsCtx ctx(mds::MdsWriter(transaction::ObTransID(1000)));
ret = tablet->set_ddl_info(user_data, ctx, 1_s/*lock_timeout_us*/);
ASSERT_EQ(OB_SUCCESS, ret);
share::SCN redo_scn = share::SCN::plus(share::SCN::min_scn(), 250);
ctx.on_redo(redo_scn);
share::SCN commit_scn = share::SCN::plus(share::SCN::min_scn(), 280);
ctx.on_commit(commit_scn, commit_scn);
}
{
share::ObTabletAutoincSeq user_data;
mds::MdsCtx ctx(mds::MdsWriter(transaction::ObTransID(1200)));
ret = tablet->set(user_data, ctx, 1_s/*lock_timeout_us*/);
ASSERT_EQ(OB_SUCCESS, ret);
share::SCN redo_scn = share::SCN::plus(share::SCN::min_scn(), 300);
ctx.on_redo(redo_scn);
share::SCN commit_scn = share::SCN::plus(share::SCN::min_scn(), 320);
ctx.on_commit(commit_scn, commit_scn);
}
// mock mds data
ObTabletMdsData mds_table_data;
ObTabletMdsData base_data;
base_data.init_for_first_creation();
ObTabletMdsData mocked_mds_data;
ret = tablet->read_mds_table(allocator_, mds_table_data, false/*for_flush*/);
ASSERT_EQ(OB_SUCCESS, ret);
LOG_INFO("read mds table", K(ret), K(mds_table_data));
ret = mocked_mds_data.init_for_mds_table_dump(allocator_, mds_table_data, base_data, 0/*finish_medium_scn*/);
ASSERT_EQ(OB_SUCCESS, ret);
// mds table flush
share::SCN decided_scn;
decided_scn = share::SCN::plus(share::SCN::min_scn(), 410);
ret = tablet->mds_table_flush(decided_scn);
ASSERT_EQ(OB_SUCCESS, ret);
// wait for mds table flush
ret = wait_for_mds_table_flush(tablet_id);
ASSERT_EQ(OB_SUCCESS, ret);
// wait all mds nodes to be released
tablet_handle.reset();
ret = wait_for_all_mds_nodes_released(tablet_id);
ASSERT_EQ(OB_SUCCESS, ret);
ret = TestMdsCompat::get_tablet(tablet_id, tablet_handle);
ASSERT_EQ(OB_SUCCESS, ret);
tablet = tablet_handle.get_obj();
ObMigrationTabletParam param;
ret = tablet->build_migration_tablet_param(param);
param.last_persisted_committed_tablet_status_.on_init();
ASSERT_EQ(OB_SUCCESS, ret);
ASSERT_TRUE(param.is_valid());
// replace mds data in migration tablet param with our mocked mds data
param.mds_data_.reset();
ret = param.mds_data_.init(param.allocator_, mocked_mds_data);
ASSERT_EQ(OB_SUCCESS, ret);
LOG_INFO("start generate mds sstable from param", K(ret), K(param));
ObTableHandleV2 table_handle;
ret = ObMdsDataCompatHelper::generate_mds_mini_sstable(param, allocator_, table_handle);
ASSERT_EQ(common::OB_SUCCESS, ret);
const ObITable *table = table_handle.get_table();
ASSERT_NE(nullptr, table);
ASSERT_EQ(share::SCN::plus(share::SCN::min_scn(), 1), table->get_start_scn());
ASSERT_EQ(param.mds_checkpoint_scn_, table->get_end_scn());
}
TEST_F(TestMdsCompat, compat)
{
int ret = OB_SUCCESS;
// create tablet
const common::ObTabletID tablet_id(ObTimeUtility::fast_current_time() % 10000000000000);
ObTabletHandle tablet_handle;
share::SCN create_commit_scn;
create_commit_scn = share::SCN::plus(share::SCN::min_scn(), 80);
ret = create_tablet(tablet_id, tablet_handle);
ASSERT_EQ(OB_SUCCESS, ret);
ObTablet *tablet = tablet_handle.get_obj();
ASSERT_NE(nullptr, tablet);
share::SCN mds_checkpoint_scn = tablet->get_tablet_meta().mds_checkpoint_scn_;
ASSERT_EQ(share::SCN::base_scn(), mds_checkpoint_scn);
// write data to mds table
{
ObTabletCreateDeleteMdsUserData user_data;
user_data.tablet_status_ = ObTabletStatus::NORMAL;
user_data.data_type_ = ObTabletMdsUserDataType::CREATE_TABLET;
mds::MdsCtx ctx(mds::MdsWriter(transaction::ObTransID(123)));
ret = tablet->set_tablet_status(user_data, ctx);
ASSERT_EQ(OB_SUCCESS, ret);
share::SCN redo_scn = share::SCN::plus(share::SCN::min_scn(), 50);
ctx.on_redo(redo_scn);
ctx.on_commit(create_commit_scn, create_commit_scn);
}
{
compaction::ObMediumCompactionInfoKey key(100);
compaction::ObMediumCompactionInfo info;
ret = MediumInfoHelper::build_medium_compaction_info(allocator_, info, 100);
ASSERT_EQ(OB_SUCCESS, ret);
mds::MdsCtx ctx(mds::MdsWriter(transaction::ObTransID(777)));
ret = tablet->set(key, info, ctx, 1_s/*lock_timeout_us*/);
ASSERT_EQ(OB_SUCCESS, ret);
share::SCN redo_scn = share::SCN::plus(share::SCN::min_scn(), 110);
ctx.on_redo(redo_scn);
share::SCN commit_scn = share::SCN::plus(share::SCN::min_scn(), 120);
ctx.on_commit(commit_scn, commit_scn);
}
// mock mds data
ObTabletMdsData mds_table_data;
ObTabletMdsData base_data;
base_data.init_for_first_creation();
ObTabletMdsData mocked_mds_data;
ret = tablet->read_mds_table(allocator_, mds_table_data, false/*for_flush*/);
ASSERT_EQ(OB_SUCCESS, ret);
LOG_INFO("read mds table", K(ret), K(mds_table_data));
ret = mocked_mds_data.init_for_mds_table_dump(allocator_, mds_table_data, base_data, 0/*finish_medium_scn*/);
ASSERT_EQ(OB_SUCCESS, ret);
// assign to tablet
ASSERT_EQ(nullptr, tablet->mds_data_);
ret = ObTabletObjLoadHelper::alloc_and_new(allocator_, tablet->mds_data_);
ASSERT_NE(nullptr, tablet->mds_data_);
ret = tablet->mds_data_->init_for_evict_medium_info(allocator_, mocked_mds_data, 0/*finish_medium_scn*/);
ASSERT_EQ(OB_SUCCESS, ret);
// convert to mds sstable
ObArenaAllocator allocator;
ObTableHandleV2 table_handle;
ret = ObMdsDataCompatHelper::generate_mds_mini_sstable(*tablet_handle.get_obj(), allocator, table_handle);
ASSERT_EQ(OB_SUCCESS, ret);
blocksstable::ObSSTable *sstable = nullptr;
ASSERT_EQ(OB_SUCCESS, table_handle.get_sstable(sstable));
ASSERT_NE(nullptr, sstable);
ASSERT_TRUE(sstable->is_valid());
// free memory for mds data
tablet->mds_data_->~ObTabletMdsData();
ObTabletObjLoadHelper::free(allocator_, tablet->mds_data_);
ASSERT_EQ(nullptr, tablet->mds_data_);
}
} // namespace storage
} // namespace oceanbase
int main(int argc, char **argv)
{
system("rm -f test_mds_compat.log*");
OB_LOGGER.set_file_name("test_mds_compat.log", true);
OB_LOGGER.set_log_level("INFO");
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}