443 lines
17 KiB
C++
443 lines
17 KiB
C++
/**
|
|
* Copyright (c) 2021 OceanBase
|
|
* OceanBase CE is licensed under Mulan PubL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PubL v2.
|
|
* You may obtain a copy of Mulan PubL v2 at:
|
|
* http://license.coscl.org.cn/MulanPubL-2.0
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PubL v2 for more details.
|
|
*/
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <stdlib.h>
|
|
#define USING_LOG_PREFIX STORAGE
|
|
#define protected public
|
|
#define private public
|
|
#include "lib/container/ob_tuple.h"
|
|
#include "lib/ob_define.h"
|
|
#include "ob_tablet_id.h"
|
|
#include "share/inner_table/ob_inner_table_schema_constants.h"
|
|
#include "share/ob_ls_id.h"
|
|
#include "env/ob_simple_cluster_test_base.h"
|
|
#include "env/ob_simple_server_restart_helper.h"
|
|
#include "lib/mysqlclient/ob_mysql_result.h"
|
|
#include "logservice/rcservice/ob_role_change_service.h"
|
|
#include "logservice/ob_ls_adapter.h"
|
|
#include "storage/access/ob_rows_info.h"
|
|
#include "storage/checkpoint/ob_data_checkpoint.h"
|
|
#include "storage/compaction/ob_schedule_dag_func.h"
|
|
#include "storage/compaction/ob_tablet_merge_task.h"
|
|
#include "storage/ls/ob_freezer.h"
|
|
#include "storage/ls/ob_ls.h"
|
|
#include "storage/ls/ob_ls_meta.h"
|
|
#include "storage/ls/ob_ls_tablet_service.h"
|
|
#include "storage/ls/ob_ls_tx_service.h"
|
|
#include "storage/meta_mem/ob_tablet_handle.h"
|
|
#include "storage/meta_mem/ob_tenant_meta_mem_mgr.h"
|
|
#include "storage/ob_relative_table.h"
|
|
#include "storage/ob_storage_table_guard.h"
|
|
#include "storage/tx_storage/ob_ls_map.h"
|
|
#include "storage/tx_storage/ob_ls_service.h"
|
|
#include "storage/multi_data_source/runtime_utility/mds_tenant_service.h"
|
|
#include <fstream>
|
|
|
|
#undef private
|
|
#undef protected
|
|
|
|
static const char *TEST_FILE_NAME = "test_mds_recover";
|
|
static const char *BORN_CASE_NAME = "ObTestMdsBeforeRecover";
|
|
static const char *RESTART_CASE_NAME = "ObTestMdsAfterRecover";
|
|
static const char *BEFORE_RECOVER_RESULT_FILE = "/tmp/1.txt";
|
|
static const char *AFTER_RECOVER_RESULT_FILE = "/tmp/2.txt";
|
|
|
|
using namespace std;
|
|
|
|
ObSimpleServerRestartHelper *helper_ptr = nullptr;
|
|
|
|
oceanbase::share::ObLSID TEST_LS_ID;
|
|
oceanbase::common::ObTabletID TEST_TABLET_ID;
|
|
oceanbase::share::SCN LAST_SCN, REPLAY_BASE_SCN;
|
|
using VirtualTableResult = oceanbase::common::ObArray<ObTuple<int64_t/*unit_id*/, ObStringHolder/*user_key*/, int64_t/*version_idx*/, ObStringHolder/*writer_type*/,
|
|
int64_t/*writer_id*/, int64_t/*seq_no*/, uint64_t/*redo_scn*/, uint64_t/*end_scn*/, uint64_t/*trans_version*/,
|
|
ObStringHolder/*node_type*/, ObStringHolder/*state*/, ObStringHolder/*user_data*/, ObStringHolder/*position*/>>;
|
|
|
|
VirtualTableResult RESULT;
|
|
|
|
namespace oceanbase
|
|
{
|
|
using namespace transaction;
|
|
using namespace storage;
|
|
using namespace share;
|
|
namespace unittest
|
|
{
|
|
class ObTestMdsBeforeRecover : public ObSimpleClusterTestBase
|
|
{
|
|
public:
|
|
ObTestMdsBeforeRecover() : ObSimpleClusterTestBase(TEST_FILE_NAME) {}
|
|
};
|
|
|
|
void wait_user()
|
|
{
|
|
std::cout << "Enter anything to continue..." << std::endl;
|
|
char a = '\0';
|
|
std::cin >> a;
|
|
}
|
|
|
|
void create_or_find_test_table(const char *table_name, share::ObLSID &ls_id, ObTabletID &tablet_id, bool create)
|
|
{
|
|
if (create) {
|
|
int64_t _;
|
|
char create_table_sql[512] = { 0 };
|
|
databuff_printf(create_table_sql, 512, "create table %s(a int)", table_name);
|
|
// 1. 新建一个tablet
|
|
ASSERT_EQ(OB_SUCCESS, GCTX.sql_proxy_->write(OB_SYS_TENANT_ID, create_table_sql, _));
|
|
}
|
|
// 2. 从表名拿到它的tablet_id
|
|
char where_condition1[512] = { 0 };
|
|
databuff_printf(where_condition1, 512, "where table_name = '%s'", table_name);
|
|
ASSERT_EQ(OB_SUCCESS, ObTableAccessHelper::read_single_row(OB_SYS_TENANT_ID,
|
|
{"tablet_id"},
|
|
OB_ALL_TABLE_TNAME,
|
|
where_condition1,
|
|
tablet_id));
|
|
// 3. 从tablet_id拿到它的ls_id
|
|
char where_condition2[512] = { 0 };
|
|
databuff_printf(where_condition2, 512, "where tablet_id = %ld", tablet_id.id());
|
|
ASSERT_EQ(OB_SUCCESS, ObTableAccessHelper::read_single_row(OB_SYS_TENANT_ID,
|
|
{"ls_id"},
|
|
OB_ALL_TABLET_TO_LS_TNAME,
|
|
where_condition2,
|
|
ls_id));
|
|
// 4. 从ls_id找到ls
|
|
storage::ObLSHandle ls_handle;
|
|
ASSERT_EQ(OB_SUCCESS, MTL(storage::ObLSService *)->get_ls(ls_id, ls_handle, ObLSGetMod::TRANS_MOD));
|
|
}
|
|
|
|
void insert_row_to_write_inc_seq(const char *table_name)
|
|
{
|
|
int64_t _;
|
|
char insert_sql[512] = { 0 };
|
|
databuff_printf(insert_sql, 512, "insert into %s values(0)", table_name);
|
|
// 1. 插入数据
|
|
ASSERT_EQ(OB_SUCCESS, GCTX.sql_proxy_->write(OB_SYS_TENANT_ID, insert_sql, _));
|
|
}
|
|
|
|
void add_column_to_write_dll_info(const char *table_name, share::ObLSID &ls_id, ObTabletID &tablet_id)
|
|
{
|
|
int64_t _;
|
|
char insert_sql[512] = { 0 };
|
|
databuff_printf(insert_sql, 512, "alter table %s add b int after a", table_name);
|
|
// 1. 加列
|
|
ASSERT_EQ(OB_SUCCESS, GCTX.sql_proxy_->write(OB_SYS_TENANT_ID, insert_sql, _));
|
|
// 2. 从表名拿到它的tablet_id
|
|
char where_condition1[512] = { 0 };
|
|
databuff_printf(where_condition1, 512, "where table_name = '%s'", table_name);
|
|
ASSERT_EQ(OB_SUCCESS, ObTableAccessHelper::read_single_row(OB_SYS_TENANT_ID,
|
|
{"tablet_id"},
|
|
OB_ALL_TABLE_TNAME,
|
|
where_condition1,
|
|
tablet_id));
|
|
// 3. 从tablet_id拿到它的ls_id
|
|
char where_condition2[512] = { 0 };
|
|
databuff_printf(where_condition2, 512, "where tablet_id = %ld", tablet_id.id());
|
|
ASSERT_EQ(OB_SUCCESS, ObTableAccessHelper::read_single_row(OB_SYS_TENANT_ID,
|
|
{"ls_id"},
|
|
OB_ALL_TABLET_TO_LS_TNAME,
|
|
where_condition2,
|
|
ls_id));
|
|
// 4. 从ls_id找到ls
|
|
storage::ObLSHandle ls_handle;
|
|
ASSERT_EQ(OB_SUCCESS, MTL(storage::ObLSService *)->get_ls(ls_id, ls_handle, ObLSGetMod::TRANS_MOD));
|
|
}
|
|
|
|
void do_major_to_write_medium_info()
|
|
{
|
|
int64_t _;
|
|
// 1. 插入数据
|
|
ASSERT_EQ(OB_SUCCESS, GCTX.sql_proxy_->write(OB_SYS_TENANT_ID, "alter system minor freeze", _));
|
|
}
|
|
|
|
void do_flush_mds_table(share::ObLSID ls_id, ObTabletID tablet_id)
|
|
{
|
|
// 1. 从ls_id找到ls
|
|
storage::ObLSHandle ls_handle;
|
|
ASSERT_EQ(OB_SUCCESS, MTL(storage::ObLSService *)->get_ls(ls_id, ls_handle, ObLSGetMod::TRANS_MOD));
|
|
// 2. 从ls拿到tablet handle
|
|
storage::ObTabletHandle tablet_handle;
|
|
ASSERT_EQ(OB_SUCCESS, ls_handle.get_ls()->get_tablet(tablet_id, tablet_handle));
|
|
// 3. 从tablet handle拿到tablet pointer
|
|
const ObTabletPointerHandle &pointer_handle = tablet_handle.get_obj()->get_pointer_handle();
|
|
ObTabletPointer *tablet_pointer = pointer_handle.get_resource_ptr();
|
|
// 4. 做flush动作
|
|
mds::MdsTableHandle handle;
|
|
share::SCN max_decided_scn;
|
|
ASSERT_EQ(OB_SUCCESS, ls_handle.get_ls()->get_max_decided_scn(max_decided_scn));
|
|
ASSERT_EQ(OB_SUCCESS, tablet_pointer->get_mds_table(tablet_id, handle));
|
|
ASSERT_EQ(OB_SUCCESS, handle.flush(share::SCN::max_scn(), max_decided_scn));
|
|
ASSERT_EQ(true, handle.p_mds_table_base_->flushing_scn_.is_valid());
|
|
// 5. 等flush完成
|
|
share::SCN rec_scn = share::SCN::min_scn();
|
|
while (!rec_scn.is_max()) {
|
|
ASSERT_EQ(OB_SUCCESS, handle.get_rec_scn(rec_scn));
|
|
sleep(1);
|
|
OCCAM_LOG(INFO, "waiting node dump", K(rec_scn));
|
|
}
|
|
}
|
|
|
|
void do_recycle_and_gc_mds_table(share::ObLSID ls_id, ObTabletID tablet_id)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
// 1. 从ls_id找到ls
|
|
storage::ObLSHandle ls_handle;
|
|
ASSERT_EQ(OB_SUCCESS, MTL(storage::ObLSService *)->get_ls(ls_id, ls_handle, ObLSGetMod::TRANS_MOD));
|
|
// 2. 从ls拿到tablet handle
|
|
storage::ObTabletHandle tablet_handle;
|
|
ASSERT_EQ(OB_SUCCESS, ls_handle.get_ls()->get_tablet(tablet_id, tablet_handle));
|
|
// 3. gc mds table
|
|
mds::ObTenantMdsTimer timer;
|
|
while (OB_FAIL(timer.process_with_tablet_(*tablet_handle.get_obj()))) {
|
|
sleep(1);
|
|
}
|
|
ASSERT_EQ(OB_SUCCESS, ret);
|
|
}
|
|
|
|
void get_latest_scn(share::ObLSID ls_id, ObTabletID tablet_id, uint64_t &latest_scn)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
char where_condition[512] = { 0 };
|
|
databuff_printf(where_condition, 512, "where tenant_id=%ld and ls_id =%ld and tablet_id=%ld and end_scn != 18446744073709551615 order by end_scn desc limit 1",
|
|
MTL_ID(), ls_id.id(), tablet_id.id());
|
|
ret = ObTableAccessHelper::read_single_row(MTL_ID(),
|
|
{"end_scn"},
|
|
OB_ALL_VIRTUAL_MDS_NODE_STAT_TNAME,
|
|
where_condition,
|
|
latest_scn);
|
|
ASSERT_EQ(OB_SUCCESS, ret);
|
|
}
|
|
|
|
void read_virtual_mds_stat(share::ObLSID ls_id, ObTabletID tablet_id, VirtualTableResult &result) {
|
|
int ret = OB_SUCCESS;
|
|
char where_condition[512] = { 0 };
|
|
result.reset();
|
|
databuff_printf(where_condition, 512, "where tenant_id=%ld and ls_id =%ld and tablet_id=%ld and position='DISK'",
|
|
MTL_ID(), ls_id.id(), tablet_id.id());
|
|
int retry_time = 0;
|
|
do {
|
|
if (ret == OB_TABLE_NOT_EXIST) {
|
|
sleep(1);
|
|
}
|
|
ret = ObTableAccessHelper::read_multi_row(MTL_ID(),
|
|
{"unit_id", "user_key", "version_idx", "writer_type", "writer_id", "seq_no",
|
|
"redo_scn", "end_scn", "trans_version", "node_type", "state", "user_data",
|
|
"position"},
|
|
OB_ALL_VIRTUAL_MDS_NODE_STAT_TNAME,
|
|
where_condition,
|
|
result);
|
|
} while (ret == OB_TABLE_NOT_EXIST && ++retry_time < 20);
|
|
ASSERT_EQ(OB_SUCCESS, ret);
|
|
}
|
|
|
|
void advance_checkpoint(share::ObLSID ls_id, share::SCN aim_scn)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
// 1. 从ls_id找到ls
|
|
storage::ObLSHandle ls_handle;
|
|
ASSERT_EQ(OB_SUCCESS, MTL(storage::ObLSService *)->get_ls(ls_id, ls_handle, ObLSGetMod::TRANS_MOD));
|
|
int64_t retry_times = 60;
|
|
SCN checkpoint = SCN::min_scn();
|
|
// 2. 等max_decided_scn推过目标点
|
|
SCN max_decided_scn;
|
|
do {
|
|
ret = ls_handle.get_ls()->get_max_decided_scn(max_decided_scn);
|
|
::sleep(1);
|
|
fprintf(stdout,
|
|
"waiting advance max decided scn, max_decided_scn = %lu aim_scn = %lu\n",
|
|
max_decided_scn.get_val_for_inner_table_field(),
|
|
aim_scn.get_val_for_inner_table_field());
|
|
} while (OB_SUCC(ret) && max_decided_scn <= aim_scn);
|
|
ASSERT_EQ(OB_SUCCESS, ret);
|
|
ASSERT_GT(max_decided_scn, aim_scn);
|
|
// 3. 推checkpoint并且等checkpoint推过
|
|
while (--retry_times > 0) {
|
|
checkpoint = ls_handle.get_ls()->get_clog_checkpoint_scn();
|
|
if (checkpoint > aim_scn) {
|
|
fprintf(stdout,
|
|
"checkpoint scn is higher than aim scn, checkpoint_scn:%ld, aim_scn:%ld\n",
|
|
checkpoint.get_val_for_inner_table_field(),
|
|
aim_scn.get_val_for_inner_table_field());
|
|
break;
|
|
} else {
|
|
::sleep(1);
|
|
fprintf(stdout,
|
|
"waiting advance checkpoint, checkpoint = %lu aim_scn = %lu\n",
|
|
checkpoint.get_val_for_inner_table_field(),
|
|
aim_scn.get_val_for_inner_table_field());
|
|
ls_handle.get_ls()->checkpoint_executor_.advance_checkpoint_by_flush(SCN::scn_inc(aim_scn));
|
|
}
|
|
}
|
|
ASSERT_GT(ls_handle.get_ls()->get_clog_checkpoint_scn(), aim_scn);
|
|
// 4. 等CLOG回收日志
|
|
SCN min_start_scn = SCN::min_scn();
|
|
SCN keep_alive_scn = SCN::min_scn();
|
|
MinStartScnStatus status = MinStartScnStatus::UNKOWN;
|
|
retry_times = 40;
|
|
while (--retry_times > 0) {
|
|
ls_handle.get_ls()->get_min_start_scn(min_start_scn, keep_alive_scn, status);
|
|
if (MinStartScnStatus::HAS_CTX == status) {
|
|
break;
|
|
} else {
|
|
::sleep(1);
|
|
fprintf(stdout,
|
|
"waiting min_start_scn has ctx min_start_scn = %ld keep_alive_scn = %ld status = %d\n",
|
|
min_start_scn.get_val_for_inner_table_field(),
|
|
keep_alive_scn.get_val_for_inner_table_field(),
|
|
status);
|
|
}
|
|
}
|
|
ASSERT_NE(SCN::min_scn(), min_start_scn);
|
|
ASSERT_NE(SCN::min_scn(), keep_alive_scn);
|
|
ASSERT_EQ(MinStartScnStatus::HAS_CTX, status);
|
|
fprintf(stdout,
|
|
"advance checkpoint done, checkpoint = %lu aim_scn = %lu min_start_scn = %lu keep_alive_scn = %lu\n",
|
|
checkpoint.get_val_for_inner_table_field(),
|
|
aim_scn.get_val_for_inner_table_field(),
|
|
min_start_scn.get_val_for_inner_table_field(),
|
|
keep_alive_scn.get_val_for_inner_table_field());
|
|
}
|
|
|
|
void write_result_to_file(const char *file_name)
|
|
{
|
|
char buffer[2048] = { 0 };
|
|
databuff_printf(buffer, 2048, "result from virtual table:%s", to_cstring(RESULT));
|
|
std::ofstream file(file_name);
|
|
file << TEST_LS_ID.id() << std::endl
|
|
<< TEST_TABLET_ID.id() << std::endl
|
|
<< LAST_SCN.val_ << std::endl
|
|
<< buffer << std::endl;
|
|
}
|
|
|
|
TEST_F(ObTestMdsBeforeRecover, before_recover_test)
|
|
{
|
|
ObLSID ls_id;
|
|
common::ObTabletID tablet_id;
|
|
share::SCN aim_checkpoint;
|
|
uint64_t scn_from_table;
|
|
int ret = OB_SUCCESS;
|
|
MTL_SWITCH(OB_SYS_TENANT_ID)
|
|
{
|
|
create_or_find_test_table("test_a", ls_id, tablet_id, true);
|
|
insert_row_to_write_inc_seq("test_a");
|
|
add_column_to_write_dll_info("test_a", TEST_LS_ID, TEST_TABLET_ID);
|
|
do_flush_mds_table(TEST_LS_ID, TEST_TABLET_ID);
|
|
do_recycle_and_gc_mds_table(TEST_LS_ID, TEST_TABLET_ID);
|
|
get_latest_scn(TEST_LS_ID, TEST_TABLET_ID, scn_from_table);
|
|
read_virtual_mds_stat(TEST_LS_ID, TEST_TABLET_ID, RESULT);
|
|
ASSERT_EQ(OB_SUCCESS, LAST_SCN.convert_for_inner_table_field(scn_from_table));
|
|
advance_checkpoint(TEST_LS_ID, LAST_SCN);
|
|
write_result_to_file(BEFORE_RECOVER_RESULT_FILE);
|
|
}
|
|
}
|
|
|
|
class ObTestMdsAfterRecover : public ObSimpleClusterTestBase
|
|
{
|
|
public:
|
|
ObTestMdsAfterRecover() : ObSimpleClusterTestBase(TEST_FILE_NAME) {}
|
|
};
|
|
|
|
bool compare_before_and_after_recover_results(const char *file1, const char *file2)
|
|
{
|
|
ifstream file1_stream(file1);
|
|
ifstream file2_stream(file2);
|
|
cout << file1 << " context:" << endl << file1_stream.rdbuf() << endl;
|
|
cout << file2 << " context:" << endl << file2_stream.rdbuf() << endl;
|
|
char c1, c2;
|
|
while (file1_stream.get(c1) && file2_stream.get(c2)) {
|
|
if (c1 != c2) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
TEST_F(ObTestMdsAfterRecover, after_recover_test)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
{
|
|
std::ifstream file(BEFORE_RECOVER_RESULT_FILE);
|
|
file >> TEST_LS_ID.id_ >> TEST_TABLET_ID.id_ >> LAST_SCN.val_;
|
|
ASSERT_GE(REPLAY_BASE_SCN, LAST_SCN);
|
|
}
|
|
MTL_SWITCH(OB_SYS_TENANT_ID)
|
|
{
|
|
read_virtual_mds_stat(TEST_LS_ID, TEST_TABLET_ID, RESULT);// to fill cache
|
|
read_virtual_mds_stat(TEST_LS_ID, TEST_TABLET_ID, RESULT);
|
|
write_result_to_file(AFTER_RECOVER_RESULT_FILE);
|
|
ASSERT_EQ(true, compare_before_and_after_recover_results(BEFORE_RECOVER_RESULT_FILE, AFTER_RECOVER_RESULT_FILE));
|
|
}
|
|
}
|
|
|
|
} // namespace unittest
|
|
} // namespace oceanbase
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int c = 0;
|
|
int time_sec = 0;
|
|
int concurrency = 1;
|
|
char *log_level = (char *)"INFO";
|
|
while (EOF != (c = getopt(argc, argv, "t:l:"))) {
|
|
switch (c) {
|
|
case 't':
|
|
time_sec = atoi(optarg);
|
|
break;
|
|
case 'l':
|
|
log_level = optarg;
|
|
oceanbase::unittest::ObSimpleClusterTestBase::enable_env_warn_log_ = false;
|
|
break;
|
|
case 'c':
|
|
concurrency = atoi(optarg);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
std::string gtest_file_name = std::string(TEST_FILE_NAME) + "_gtest.log";
|
|
oceanbase::unittest::init_gtest_output(gtest_file_name);
|
|
|
|
int ret = 0;
|
|
ObSimpleServerRestartHelper restart_helper(argc, argv, TEST_FILE_NAME, BORN_CASE_NAME,
|
|
RESTART_CASE_NAME);
|
|
helper_ptr = &restart_helper;
|
|
restart_helper.set_sleep_sec(time_sec);
|
|
restart_helper.run();
|
|
|
|
return ret;
|
|
}
|
|
|
|
namespace oceanbase {
|
|
namespace logservice
|
|
{
|
|
int ObReplayStatus::enable(const LSN &base_lsn, const SCN &base_scn)
|
|
{
|
|
/****************************************************************************/
|
|
REPLAY_BASE_SCN = base_scn;
|
|
/****************************************************************************/
|
|
int ret = OB_SUCCESS;
|
|
if (is_enabled()) {
|
|
ret = OB_STATE_NOT_MATCH;
|
|
CLOG_LOG(WARN, "replay status already enable", K(ret));
|
|
} else {
|
|
WLockGuard wlock_guard(rwlock_);
|
|
if (OB_FAIL(enable_(base_lsn, base_scn))) {
|
|
CLOG_LOG(WARN, "enable replay status failed", K(ret), K(base_lsn), K(base_scn), K(ls_id_));
|
|
} else {
|
|
CLOG_LOG(INFO, "enable replay status success", K(ret), K(base_lsn), K(base_scn), K(ls_id_));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
} // namespace oceanbase
|