388 lines
15 KiB
C++
388 lines
15 KiB
C++
// owner: muwei.ym
|
|
// owner group: storage_ha
|
|
|
|
/**
|
|
* Copyright (c) 2022 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 <gmock/gmock.h>
|
|
|
|
#define USING_LOG_PREFIX SHARE
|
|
#define protected public
|
|
#define private public
|
|
|
|
#include "env/ob_simple_cluster_test_base.h"
|
|
#include "rootserver/ob_tenant_transfer_service.h" // ObTenantTransferService
|
|
#include "share/transfer/ob_transfer_task_operator.h" // ObTransferTaskOperator
|
|
#include "storage/tx_storage/ob_ls_service.h"
|
|
|
|
using namespace oceanbase::unittest;
|
|
|
|
namespace oceanbase
|
|
{
|
|
namespace storage
|
|
{
|
|
using namespace share::schema;
|
|
using namespace common;
|
|
using namespace share;
|
|
using namespace transaction::tablelock;
|
|
using namespace rootserver;
|
|
|
|
static uint64_t g_tenant_id;
|
|
static ObTransferPartList g_part_list;
|
|
static ObTransferPartList g_batch_part_list;
|
|
|
|
class TestTransferHandler : public unittest::ObSimpleClusterTestBase
|
|
{
|
|
public:
|
|
TestTransferHandler() : unittest::ObSimpleClusterTestBase("test_transfer_handler") {}
|
|
int read_sql(ObMySQLProxy &sql_proxy, const ObSqlString &sql, ObTransferPartList &part_list);
|
|
int gen_mock_data(const ObTransferTaskID task_id, const ObTransferStatus &status, ObTransferTask &task);
|
|
int wait_transfer_task(
|
|
const ObTransferTaskID task_id,
|
|
const ObTransferStatus &expected_status,
|
|
const bool &is_from_his,
|
|
ObTransferTask &task);
|
|
int wait_transfer_out_deleted_tablet_gc(
|
|
const ObTransferTask &task);
|
|
};
|
|
|
|
int TestTransferHandler::read_sql(
|
|
ObMySQLProxy &sql_proxy,
|
|
const ObSqlString &sql,
|
|
ObTransferPartList &part_list)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
SMART_VAR(ObMySQLProxy::MySQLResult, result) {
|
|
if (OB_UNLIKELY(!is_valid_tenant_id(g_tenant_id))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
} else if (OB_FAIL(sql_proxy.read(result, sql.ptr()))) {
|
|
} else if (OB_ISNULL(result.get_result())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
} else {
|
|
sqlclient::ObMySQLResult &res = *result.get_result();
|
|
uint64_t table_id = OB_INVALID_ID;
|
|
if (OB_SUCC(ret) && OB_SUCC(res.next())) {
|
|
EXTRACT_INT_FIELD_MYSQL(res, "object_id", table_id, uint64_t);
|
|
}
|
|
while(OB_SUCC(ret)) {
|
|
uint64_t part_id = OB_INVALID_ID;
|
|
ObTransferPartInfo part_info;
|
|
if (OB_SUCC(res.next())) {
|
|
EXTRACT_INT_FIELD_MYSQL(res, "object_id", part_id, uint64_t);
|
|
if (OB_FAIL(part_info.init(table_id, part_id))) {
|
|
} else if (OB_FAIL(part_list.push_back(part_info))) {
|
|
}
|
|
}
|
|
}
|
|
if (OB_ITER_END == ret) {
|
|
ret = OB_SUCCESS;
|
|
} else {
|
|
LOG_WARN("fail to generate data", K(sql));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int TestTransferHandler::gen_mock_data(const ObTransferTaskID task_id, const ObTransferStatus &status, ObTransferTask &task)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObLSID src_ls(1001);
|
|
ObLSID dest_ls(1002);
|
|
share::SCN start_scn;
|
|
share::SCN finish_scn;
|
|
ObTableLockOwnerID owner_id;
|
|
start_scn.convert_for_inner_table_field(1666844202200632);
|
|
finish_scn.convert_for_inner_table_field(1666844202208490);
|
|
owner_id.convert_from_value(999);
|
|
ObCurTraceId::TraceId trace_id;
|
|
trace_id.init(GCONF.self_addr_);
|
|
uint64_t data_version = 0;
|
|
ret = task.init(task_id, src_ls, dest_ls, ObString::make_string("500016:500014"), ObString("500030:500031"), ObString("500016:500015"),
|
|
ObString::make_string("1152921504606846983"), ObString::make_string("1152921504606846983:0"), start_scn, finish_scn, status, trace_id, OB_SUCCESS,
|
|
ObTransferTaskComment::EMPTY_COMMENT, ObBalanceTaskID(123), owner_id, data_version);
|
|
return ret;
|
|
}
|
|
|
|
int TestTransferHandler::wait_transfer_task(
|
|
const ObTransferTaskID task_id,
|
|
const ObTransferStatus &expected_status,
|
|
const bool &is_from_his,
|
|
ObTransferTask &task)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
task.reset();
|
|
ObMySQLProxy &inner_sql_proxy = get_curr_observer().get_mysql_proxy();
|
|
int64_t create_time = OB_INVALID_TIMESTAMP;
|
|
int64_t finish_time = OB_INVALID_TIMESTAMP;
|
|
const int64_t current_time = ObTimeUtil::current_time();
|
|
const int64_t MAX_WAIT_TIME = 5 * 60 * 1000 * 1000; //5min
|
|
const int64_t SLEEP_INTERVAL = 200 * 1000; // 200ms
|
|
bool get_task = false;
|
|
|
|
if (!task_id.is_valid() || !expected_status.is_valid()) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("wait transfer task get invalid argument", K(ret), K(task_id), K(expected_status));
|
|
} else if (is_from_his) {
|
|
while (OB_SUCC(ret)) {
|
|
if (OB_FAIL(ObTransferTaskOperator::get_history_task(inner_sql_proxy, g_tenant_id, task_id, task, create_time, finish_time))) {
|
|
if (OB_ENTRY_NOT_EXIST == ret) {
|
|
if (ObTimeUtil::current_time() - current_time >= MAX_WAIT_TIME) {
|
|
LOG_WARN("cannot wait transfer task", K(ret), K(task_id), K(expected_status));
|
|
} else {
|
|
ret = OB_SUCCESS;
|
|
usleep(SLEEP_INTERVAL);
|
|
}
|
|
} else {
|
|
LOG_WARN("failed to get history task", K(ret), K(task_id), K(expected_status));
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
while (OB_SUCC(ret)) {
|
|
if (OB_FAIL(ObTransferTaskOperator::get_task_with_time(inner_sql_proxy, g_tenant_id, task_id, false/*for_update*/,
|
|
task, create_time, finish_time))) {
|
|
if (OB_ENTRY_NOT_EXIST == ret) {
|
|
get_task = false;
|
|
ret = OB_SUCCESS;
|
|
} else {
|
|
LOG_WARN("failed to get history task", K(ret), K(task_id), K(expected_status));
|
|
}
|
|
} else if (task.get_status() != expected_status) {
|
|
get_task = false;
|
|
} else {
|
|
get_task = true;
|
|
break;
|
|
}
|
|
|
|
if (OB_SUCC(ret) && !get_task) {
|
|
if (ObTimeUtil::current_time() - current_time >= MAX_WAIT_TIME) {
|
|
LOG_WARN("cannot wait transfer task", K(ret), K(task_id), K(expected_status));
|
|
} else {
|
|
ret = OB_SUCCESS;
|
|
usleep(SLEEP_INTERVAL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int TestTransferHandler::wait_transfer_out_deleted_tablet_gc(
|
|
const ObTransferTask &task)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
MTL_SWITCH(g_tenant_id) {
|
|
const ObLSID &ls_id = task.src_ls_;
|
|
ObLS *ls = nullptr;
|
|
ObLSHandle ls_handle;
|
|
ObLSService *ls_svr = nullptr;
|
|
const int64_t current_time = ObTimeUtil::current_time();
|
|
const int64_t MAX_WAIT_TIME = 5 * 60 * 1000 * 1000; //5min
|
|
const int64_t SLEEP_INTERVAL = 200 * 1000; // 200ms
|
|
|
|
if (!task.is_valid()) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("wait transfer out deleted tablet gc get invalid argument", K(ret), K(task));
|
|
} else if (OB_ISNULL(ls_svr = MTL(ObLSService *))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("ls service should not be NULL", K(ret), K(task));
|
|
} else if (OB_FAIL(ls_svr->get_ls(ls_id, ls_handle, ObLSGetMod::STORAGE_MOD))) {
|
|
LOG_WARN("failed to get ls", K(ret), K(ls_id));
|
|
} else if (OB_ISNULL(ls = ls_handle.get_ls())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("ls should not be NULL", K(ret), KP(ls), K(ls_id));
|
|
} else {
|
|
int64_t index = 0;
|
|
while (OB_SUCC(ret) && index < task.tablet_list_.count()) {
|
|
ObTabletHandle tablet_handle;
|
|
const ObTransferTabletInfo &transfer_info = task.tablet_list_.at(index);
|
|
if (OB_FAIL(ls->get_tablet(transfer_info.tablet_id_, tablet_handle,
|
|
ObTabletCommon::DEFAULT_GET_TABLET_DURATION_US, ObMDSGetTabletMode::READ_WITHOUT_CHECK))) {
|
|
if (OB_TABLET_NOT_EXIST == ret) {
|
|
ret = OB_SUCCESS;
|
|
index++;
|
|
} else {
|
|
LOG_WARN("failed to get tablet", K(ret), K(transfer_info));
|
|
}
|
|
} else if (ObTimeUtil::current_time() - current_time >= MAX_WAIT_TIME) {
|
|
ret = OB_TIMEOUT;
|
|
LOG_WARN("wait transfer out delete tablet gc timeout", K(ret), K(transfer_info));
|
|
} else {
|
|
usleep(SLEEP_INTERVAL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
TEST_F(TestTransferHandler, prepare_valid_data)
|
|
{
|
|
g_tenant_id = OB_INVALID_TENANT_ID;
|
|
|
|
ASSERT_EQ(OB_SUCCESS, create_tenant());
|
|
ASSERT_EQ(OB_SUCCESS, get_tenant_id(g_tenant_id));
|
|
ASSERT_EQ(OB_SUCCESS, get_curr_simple_server().init_sql_proxy2());
|
|
ObMySQLProxy &sql_proxy = get_curr_simple_server().get_sql_proxy2();
|
|
|
|
usleep(5 * 1000 * 1000);
|
|
|
|
// create table
|
|
ObSqlString sql;
|
|
int64_t affected_rows = 0;
|
|
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("create table ttt1(c1 int primary key, c2 blob) partition by hash(c1) partitions 2"));
|
|
ASSERT_EQ(OB_SUCCESS, sql_proxy.write(sql.ptr(), affected_rows));
|
|
sql.reset();
|
|
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("create index ttt1_idx on ttt1(c1) local"));
|
|
ASSERT_EQ(OB_SUCCESS, sql_proxy.write(sql.ptr(), affected_rows));
|
|
sql.reset();
|
|
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("create index ttt1_global ON ttt1(c1) global partition by hash(c1) partitions 2"));
|
|
ASSERT_EQ(OB_SUCCESS, sql_proxy.write(sql.ptr(), affected_rows));
|
|
sql.reset();
|
|
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("select object_id from oceanbase.DBA_OBJECTS where OBJECT_NAME='ttt1'"));
|
|
ASSERT_EQ(OB_SUCCESS, read_sql(sql_proxy, sql, g_part_list));
|
|
sql.reset();
|
|
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("select object_id from oceanbase.DBA_OBJECTS where OBJECT_NAME='ttt1_global'"));
|
|
ASSERT_EQ(OB_SUCCESS, read_sql(sql_proxy, sql, g_part_list));
|
|
|
|
// create 101 partitions
|
|
sql.reset();
|
|
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("create table ttt2(c1 int primary key) partition by hash(c1) partitions 101"));
|
|
ASSERT_EQ(OB_SUCCESS, sql_proxy.write(sql.ptr(), affected_rows));
|
|
sql.reset();
|
|
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("select object_id from oceanbase.DBA_OBJECTS where OBJECT_NAME='ttt2'"));
|
|
ASSERT_EQ(OB_SUCCESS, read_sql(sql_proxy, sql, g_batch_part_list));
|
|
|
|
//create other ls by cluster table
|
|
sql.reset();
|
|
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("create table part_table_with_dup(c1 int) duplicate_scope = 'CLUSTER' partition by hash(c1) partitions 4"));
|
|
ASSERT_EQ(OB_SUCCESS, sql_proxy.write(sql.ptr(), affected_rows));
|
|
sql.reset();
|
|
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("select object_id from oceanbase.DBA_OBJECTS where OBJECT_NAME='ttt2'"));
|
|
ASSERT_EQ(OB_SUCCESS, read_sql(sql_proxy, sql, g_batch_part_list));
|
|
|
|
|
|
ObMySQLProxy &inner_sql_proxy = get_curr_observer().get_mysql_proxy();
|
|
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("alter system set _enable_balance_kill_transaction = true tenant = 'tt1';"));
|
|
ASSERT_EQ(OB_SUCCESS, inner_sql_proxy.write(OB_SYS_TENANT_ID, sql.ptr(), affected_rows));
|
|
usleep(100000); // wait for debug_sync_timeout to take effect
|
|
sql.reset();
|
|
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("alter system set _balance_wait_killing_transaction_end_threshold = '10s' tenant = 'tt1';"));
|
|
ASSERT_EQ(OB_SUCCESS, inner_sql_proxy.write(OB_SYS_TENANT_ID, sql.ptr(), affected_rows));
|
|
|
|
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("alter system set _enable_balance_kill_transaction = true tenant = 'tt1';"));
|
|
ASSERT_EQ(OB_SUCCESS, inner_sql_proxy.write(OB_SYS_TENANT_ID, sql.ptr(), affected_rows));
|
|
usleep(100000); // wait for debug_sync_timeout to take effect
|
|
sql.reset();
|
|
|
|
}
|
|
|
|
TEST_F(TestTransferHandler, test_transfer_1001_to_1002)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObMySQLProxy &inner_sql_proxy = get_curr_observer().get_mysql_proxy();
|
|
ObSqlString sql;
|
|
int64_t affected_rows = 0;
|
|
// stuck storage transfer
|
|
//ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("alter system set debug_sync_timeout = '1000s'"));
|
|
//ASSERT_EQ(OB_SUCCESS, inner_sql_proxy.write(OB_SYS_TENANT_ID, sql.ptr(), affected_rows));
|
|
//usleep(100000); // wait for debug_sync_timeout to take effect
|
|
//sql.reset();
|
|
//ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("set ob_global_debug_sync = 'BEFORE_START_TRANSFER_TRANS wait_for signal execute 10000'"));
|
|
//ASSERT_EQ(OB_SUCCESS, inner_sql_proxy.write(OB_SYS_TENANT_ID, sql.ptr(), affected_rows));
|
|
|
|
ASSERT_EQ(4, g_part_list.count());
|
|
ASSERT_TRUE(is_valid_tenant_id(g_tenant_id));
|
|
share::ObTenantSwitchGuard tenant_guard;
|
|
ASSERT_EQ(OB_SUCCESS, tenant_guard.switch_to(g_tenant_id));
|
|
ObTenantTransferService *tenant_transfer = MTL(ObTenantTransferService*);
|
|
ASSERT_TRUE(OB_NOT_NULL(tenant_transfer));
|
|
|
|
//title: 1001 ls transfer to 1002 ls
|
|
// generate transfer task
|
|
ObTransferTaskID task_id;
|
|
ObTransferTask transfer_task;
|
|
ObMySQLTransaction trans;
|
|
ASSERT_EQ(OB_SUCCESS, trans.start(&inner_sql_proxy, g_tenant_id));
|
|
ASSERT_EQ(OB_SUCCESS, tenant_transfer->generate_transfer_task(trans, ObLSID(1001), ObLSID(1002),
|
|
g_part_list, ObBalanceTaskID(123), transfer_task));
|
|
task_id = transfer_task.get_task_id();
|
|
if (trans.is_started()) {
|
|
int tmp_ret = OB_SUCCESS;
|
|
if (OB_SUCCESS != (tmp_ret = trans.end(OB_SUCC(ret)))) {
|
|
LOG_WARN("failed to commit trans", KR(ret), KR(tmp_ret));
|
|
ret = OB_SUCC(ret) ? tmp_ret : ret;
|
|
}
|
|
}
|
|
|
|
//check observer transfer
|
|
ObTransferStatus expected_status(ObTransferStatus::COMPLETED);
|
|
ObTransferTask task;
|
|
ASSERT_EQ(OB_SUCCESS, wait_transfer_task(task_id, expected_status, true/*is_from_his*/, task));
|
|
LOG_INFO("generate transfer task", K(task));
|
|
ASSERT_EQ(OB_SUCCESS, task.result_);
|
|
//wait 1001 ls transfer out tablet deleted gc.
|
|
ASSERT_EQ(OB_SUCCESS, wait_transfer_out_deleted_tablet_gc(task));
|
|
}
|
|
|
|
TEST_F(TestTransferHandler, test_transfer_1002_to_1001)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObMySQLProxy &inner_sql_proxy = get_curr_observer().get_mysql_proxy();
|
|
ObSqlString sql;
|
|
int64_t affected_rows = 0;
|
|
|
|
ASSERT_EQ(4, g_part_list.count());
|
|
ASSERT_TRUE(is_valid_tenant_id(g_tenant_id));
|
|
share::ObTenantSwitchGuard tenant_guard;
|
|
ASSERT_EQ(OB_SUCCESS, tenant_guard.switch_to(g_tenant_id));
|
|
ObTenantTransferService *tenant_transfer = MTL(ObTenantTransferService*);
|
|
ASSERT_TRUE(OB_NOT_NULL(tenant_transfer));
|
|
|
|
//title: 1002 ls transfer to 1001 ls
|
|
// generate transfer task
|
|
ObTransferTaskID task_id;
|
|
ObTransferTask transfer_task;
|
|
ObMySQLTransaction trans;
|
|
ASSERT_EQ(OB_SUCCESS, trans.start(&inner_sql_proxy, g_tenant_id));
|
|
ASSERT_EQ(OB_SUCCESS, tenant_transfer->generate_transfer_task(trans, ObLSID(1002), ObLSID(1001),
|
|
g_part_list, ObBalanceTaskID(123), transfer_task));
|
|
task_id = transfer_task.get_task_id();
|
|
if (trans.is_started()) {
|
|
int tmp_ret = OB_SUCCESS;
|
|
if (OB_SUCCESS != (tmp_ret = trans.end(OB_SUCC(ret)))) {
|
|
LOG_WARN("failed to commit trans", KR(ret), KR(tmp_ret));
|
|
ret = OB_SUCC(ret) ? tmp_ret : ret;
|
|
}
|
|
}
|
|
//check observer transfer
|
|
ObTransferStatus expected_status(ObTransferStatus::COMPLETED);
|
|
ObTransferTask task;
|
|
ASSERT_EQ(OB_SUCCESS, wait_transfer_task(task_id, expected_status, true/*is_from_his*/, task));
|
|
LOG_INFO("generate transfer task", K(task));
|
|
ASSERT_EQ(OB_SUCCESS, task.result_);
|
|
//wait 1001 ls transfer out tablet deleted gc.
|
|
ASSERT_EQ(OB_SUCCESS, wait_transfer_out_deleted_tablet_gc(task));
|
|
}
|
|
|
|
} // namespace rootserver
|
|
} // namespace oceanbase
|
|
int main(int argc, char **argv)
|
|
{
|
|
oceanbase::unittest::init_log_and_gtest(argc, argv);
|
|
OB_LOGGER.set_log_level("INFO");
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|