[FEAT MERGE] tablelock_42

Co-authored-by: YangEfei <yangyifei96@outlook.com>
Co-authored-by: joseph12138 <17862707376@163.com>
This commit is contained in:
obdev
2023-05-12 03:11:22 +00:00
committed by ob-robot
parent 3ed0e51931
commit a9b068daa1
23 changed files with 1324 additions and 290 deletions

View File

@ -72,6 +72,7 @@
#include "mock_gts_source.h"
#include "storage/blocksstable/ob_shared_macro_block_manager.h"
#include "storage/concurrency_control/ob_multi_version_garbage_collector.h"
#include "storage/tablelock/ob_table_lock_service.h"
#include "storage/tx/wrs/ob_tenant_weak_read_service.h"
namespace oceanbase
@ -669,6 +670,7 @@ int MockTenantModuleEnv::init()
MTL_BIND2(mtl_new_default, storage::ObTenantFreezeInfoMgr::mtl_init, nullptr, mtl_stop_default, mtl_wait_default, mtl_destroy_default);
MTL_BIND2(mtl_new_default, ObSharedMacroBlockMgr::mtl_init, mtl_start_default, mtl_stop_default, mtl_wait_default, mtl_destroy_default);
MTL_BIND2(mtl_new_default, ObMultiVersionGarbageCollector::mtl_init, mtl_start_default, mtl_stop_default, mtl_wait_default, mtl_destroy_default);
MTL_BIND2(mtl_new_default, ObTableLockService::mtl_init, mtl_start_default, mtl_stop_default, mtl_wait_default, mtl_destroy_default);
MTL_BIND2(server_obj_pool_mtl_new<transaction::ObPartTransCtx>, nullptr, nullptr, nullptr, nullptr, server_obj_pool_mtl_destroy<transaction::ObPartTransCtx>);
}
if (OB_FAIL(ret)) {

View File

@ -138,6 +138,7 @@ TEST_F(TestLockMemtable, lock)
MyTxCtx default_ctx;
ObStoreCtx store_ctx;
ObStoreCtx unlock_store_ctx;
min_commited_scn.set_min();
flushed_scn.set_min();
// 1.1 get store ctx
@ -174,10 +175,6 @@ TEST_F(TestLockMemtable, lock)
memtable_.remove_lock_record(DEFAULT_IN_TRANS_LOCK_OP);
// 1.6 check again
LOG_INFO("TestLockMemtable::lock 1.6");
ret = memtable_.obj_lock_map_.get_obj_lock_with_ref_(DEFAULT_TABLET_LOCK_ID,
obj_lock);
ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
ret = mem_ctx->check_lock_exist(DEFAULT_IN_TRANS_LOCK_OP.lock_id_,
DEFAULT_IN_TRANS_LOCK_OP.owner_id_,
DEFAULT_IN_TRANS_LOCK_OP.lock_mode_,
@ -217,7 +214,6 @@ TEST_F(TestLockMemtable, lock)
// 2.3 unlock not complete lock
LOG_INFO("TestLockMemtable::lock 2.3");
MyTxCtx ctx2;
ObStoreCtx unlock_store_ctx;
start_tx(TRANS_ID2, ctx2);
get_store_ctx(ctx2, unlock_store_ctx);
ctx2.tx_ctx_.change_to_leader();
@ -294,6 +290,7 @@ TEST_F(TestLockMemtable, replay)
share::SCN flushed_scn;
MyTxCtx default_ctx;
ObStoreCtx store_ctx;
ObStoreCtx unlock_store_ctx;
ObMemtableCtx *mem_ctx = nullptr;
bool lock_exist = false;
ObOBJLock *obj_lock = NULL;
@ -346,10 +343,6 @@ TEST_F(TestLockMemtable, replay)
memtable_.remove_lock_record(DEFAULT_IN_TRANS_LOCK_OP);
// 1.5 check again
LOG_INFO("TestLockMemtable::replay 1.5");
ret = memtable_.obj_lock_map_.get_obj_lock_with_ref_(DEFAULT_TABLET_LOCK_ID,
obj_lock);
ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
ret = mem_ctx->check_lock_exist(DEFAULT_IN_TRANS_LOCK_OP.lock_id_,
DEFAULT_IN_TRANS_LOCK_OP.owner_id_,
DEFAULT_IN_TRANS_LOCK_OP.lock_mode_,
@ -387,7 +380,6 @@ TEST_F(TestLockMemtable, replay)
// 2.3 unlock not complete lock
LOG_INFO("TestLockMemtable::replay 2.3");
MyTxCtx ctx2;
ObStoreCtx unlock_store_ctx;
start_tx(TRANS_ID2, ctx2);
get_store_ctx(ctx2, unlock_store_ctx);
ctx2.tx_ctx_.change_to_leader();
@ -462,6 +454,12 @@ TEST_F(TestLockMemtable, recover)
ObOBJLock *obj_lock = NULL;
min_commited_scn.set_min();
flushed_scn.set_min();
share::SCN commit_version;
share::SCN commit_scn;
commit_version.set_base();
commit_scn.set_base();
MyTxCtx ctx2;
ObStoreCtx unlock_store_ctx;
// 1. recover in trans lock
// 1.1 recover
LOG_INFO("TestLockMemtable::recover 1.1");
@ -477,10 +475,14 @@ TEST_F(TestLockMemtable, recover)
LOG_INFO("TestLockMemtable::recover 1.3");
memtable_.remove_lock_record(DEFAULT_IN_TRANS_LOCK_OP);
// 1.4 check exist
LOG_INFO("TestLockMemtable::recover 1.4");
ret = memtable_.obj_lock_map_.get_obj_lock_with_ref_(DEFAULT_TABLET_LOCK_ID,
obj_lock);
ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
// We move obj lock garbage collect process to gc thread
// which is backend, so you can still see obj lock here.
// See ObOBJLockGarbageCollector for details.
//
// LOG_INFO("TestLockMemtable::recover 1.4");
// ret = memtable_.obj_lock_map_.get_obj_lock_with_ref_(DEFAULT_TABLET_LOCK_ID,
// obj_lock);
// ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
// 2. recover out trans lock
// 2.1 recover
@ -489,10 +491,6 @@ TEST_F(TestLockMemtable, recover)
ASSERT_EQ(OB_SUCCESS, ret);
// 2.2 commit
LOG_INFO("TestLockMemtable::recover 2.2");
share::SCN commit_version;
share::SCN commit_scn;
commit_version.set_base();
commit_scn.set_base();
ret = memtable_.update_lock_status(DEFAULT_OUT_TRANS_LOCK_OP,
commit_version,
commit_scn,
@ -500,8 +498,6 @@ TEST_F(TestLockMemtable, recover)
ASSERT_EQ(OB_SUCCESS, ret);
// 2.3 unlock
LOG_INFO("TestLockMemtable::recover 2.3");
MyTxCtx ctx2;
ObStoreCtx unlock_store_ctx;
start_tx(TRANS_ID2, ctx2);
get_store_ctx(ctx2, unlock_store_ctx);
ctx2.tx_ctx_.change_to_leader();
@ -591,10 +587,6 @@ TEST_F(TestLockMemtable, pre_check_lock)
memtable_.remove_lock_record(DEFAULT_IN_TRANS_LOCK_OP);
// 1.7 check again
LOG_INFO("TestLockMemtable::pre_check_lock 1.7");
ret = memtable_.obj_lock_map_.get_obj_lock_with_ref_(DEFAULT_TABLET_LOCK_ID,
obj_lock);
ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
ret = mem_ctx->check_lock_exist(DEFAULT_IN_TRANS_LOCK_OP.lock_id_,
DEFAULT_IN_TRANS_LOCK_OP.owner_id_,
DEFAULT_IN_TRANS_LOCK_OP.lock_mode_,
@ -932,46 +924,50 @@ TEST_F(TestLockMemtable, out_trans_multi_source)
ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
// 8 COMPACT
// This case is invalid after ObOBJLockGarbageCollector
// starts using. In short, when switch to leader, gc thread
// will try to execute a force compact, to avoid this case.
// We don't need to compact residual unlock op anymore.
// 8.1 create an unlock op and committed it.
LOG_INFO("TestLockMemtable::out_trans_multi_source 8.1");
scn = share::SCN::plus(share::SCN::min_scn(), 2);
is_replay = true;
ret = ctx2.tx_ctx_.notify_data_source_(NotifyType::TX_END, scn, is_replay, mds_array_unlock);
ASSERT_EQ(OB_SUCCESS, ret);
memtable_.obj_lock_map_.print();
// LOG_INFO("TestLockMemtable::out_trans_multi_source 8.1");
// scn = share::SCN::plus(share::SCN::min_scn(), 2);
// is_replay = true;
// ret = ctx2.tx_ctx_.notify_data_source_(NotifyType::TX_END, scn, is_replay, mds_array_unlock);
// ASSERT_EQ(OB_SUCCESS, ret);
// memtable_.obj_lock_map_.print();
// 8.2 commit the unlock op
LOG_INFO("TestLockMemtable::out_trans_multi_source 8.2");
is_commit = true;
commit_version = share::SCN::plus(share::SCN::min_scn(), 2);
commit_scn = share::SCN::plus(share::SCN::min_scn(), 2);
mem_ctx = store_ctx2.mvcc_acc_ctx_.mem_ctx_;
ret = mem_ctx->clear_table_lock_(is_commit,
commit_version,
commit_scn);
ASSERT_EQ(OB_SUCCESS, ret);
memtable_.obj_lock_map_.print();
// LOG_INFO("TestLockMemtable::out_trans_multi_source 8.2");
// is_commit = true;
// commit_version = share::SCN::plus(share::SCN::min_scn(), 2);
// commit_scn = share::SCN::plus(share::SCN::min_scn(), 2);
// mem_ctx = store_ctx2.mvcc_acc_ctx_.mem_ctx_;
// ret = mem_ctx->clear_table_lock_(is_commit,
// commit_version,
// commit_scn);
// ASSERT_EQ(OB_SUCCESS, ret);
// memtable_.obj_lock_map_.print();
// 8.3 compact unlock op if lock conflict occur.
LOG_INFO("TestLockMemtable::out_trans_multi_source 8.3");
ObTableLockOp conflict_lock_op = lock_op;
conflict_lock_op.lock_mode_ = DEFAULT_COFLICT_LOCK_MODE;
ObLockParam param;
bool is_try_lock = true;
int64_t expired_time = 0;
param.is_try_lock_ = is_try_lock;
param.expired_time_ = expired_time;
ret = memtable_.lock(param,
store_ctx,
conflict_lock_op);
ASSERT_EQ(OB_SUCCESS, ret);
// LOG_INFO("TestLockMemtable::out_trans_multi_source 8.3");
// ObTableLockOp conflict_lock_op = lock_op;
// conflict_lock_op.lock_mode_ = DEFAULT_COFLICT_LOCK_MODE;
// ObLockParam param;
// bool is_try_lock = true;
// int64_t expired_time = 0;
// param.is_try_lock_ = is_try_lock;
// param.expired_time_ = expired_time;
// ret = memtable_.lock(param,
// store_ctx,
// conflict_lock_op);
// ASSERT_EQ(OB_SUCCESS, ret);
// 8.4 check unlock op exist
LOG_INFO("TestLockMemtable::out_trans_multi_source 8.4");
commit_version = share::SCN::plus(share::SCN::min_scn(), 2);
commit_scn = share::SCN::plus(share::SCN::min_scn(), 2);
ret = memtable_.update_lock_status(unlock_op,
commit_version,
commit_scn,
COMMIT_LOCK_OP_STATUS);
ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
// LOG_INFO("TestLockMemtable::out_trans_multi_source 8.4");
// commit_version = share::SCN::plus(share::SCN::min_scn(), 2);
// commit_scn = share::SCN::plus(share::SCN::min_scn(), 2);
// ret = memtable_.update_lock_status(unlock_op,
// commit_version,
// commit_scn,
// COMMIT_LOCK_OP_STATUS);
// ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
}
} // tablelock

View File

@ -160,9 +160,9 @@ TEST_F(TestObjLockMap, lock)
lock_map_.lock_map_.revert(obj_lock);
obj_lock = NULL;
// 1.4 obj lock release check
ret = lock_map_.get_obj_lock_with_ref_(DEFAULT_TABLET_LOCK_ID,
obj_lock);
ret = lock_map_.unlock(DEFAULT_OUT_TRANS_UNLOCK_OP,
is_try_lock,
expired_time);
ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
lock_map_.reset();
@ -241,9 +241,9 @@ TEST_F(TestObjLockMap, unlock)
min_commited_scn = lock_map_.get_min_ddl_committed_scn(flushed_scn);
ASSERT_EQ(min_commited_scn, share::SCN::max_scn());
// 1.8 obj lock release check
ret = lock_map_.get_obj_lock_with_ref_(DEFAULT_TABLET_LOCK_ID,
obj_lock);
ret = lock_map_.unlock(DEFAULT_OUT_TRANS_UNLOCK_OP,
is_try_lock,
expired_time);
ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
lock_map_.reset();
@ -274,9 +274,9 @@ TEST_F(TestObjLockMap, recover)
// 1.3 commit
lock_map_.remove_lock_record(DEFAULT_IN_TRANS_LOCK_OP);
// 1.4 check exist
ret = lock_map_.get_obj_lock_with_ref_(DEFAULT_TABLET_LOCK_ID,
obj_lock);
ret = lock_map_.unlock(DEFAULT_OUT_TRANS_UNLOCK_OP,
is_try_lock,
expired_time);
ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
// 2 recover out trans lock
@ -315,9 +315,9 @@ TEST_F(TestObjLockMap, recover)
// 2.6 check exist
min_commited_scn = lock_map_.get_min_ddl_committed_scn(flushed_scn);
ASSERT_EQ(min_commited_scn, share::SCN::max_scn());
ret = lock_map_.get_obj_lock_with_ref_(DEFAULT_TABLET_LOCK_ID,
obj_lock);
ret = lock_map_.unlock(DEFAULT_OUT_TRANS_UNLOCK_OP,
is_try_lock,
expired_time);
ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
lock_map_.reset();

View File

@ -25,6 +25,7 @@ ob_unittest_observer(test_ob_black_list_service test_ob_black_list_service.cpp)
ob_unittest_observer(test_ob_minor_freeze test_ob_minor_freeze.cpp)
ob_unittest_observer(test_ob_simple_cluster test_ob_simple_cluster.cpp)
ob_unittest_observer(test_ob_table_lock_service test_ob_table_lock_service.cpp)
ob_unittest_observer(test_ob_obj_lock_garbage_collector test_ob_obj_lock_garbage_collector.cpp)
ob_unittest_observer(test_observer_expand_shrink test_observer_expand_shrink.cpp)
ob_unittest_observer(test_replay_from_middle test_replay_from_middle.cpp)
ob_unittest_observer(test_special_tablet_flush test_special_tablet_flush.cpp)

View File

@ -0,0 +1,639 @@
/**
* 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 TABLELOCK
#define protected public
#define private public
#include "env/ob_simple_cluster_test_base.h"
#include "env/ob_simple_server_restart_helper.h"
#include "lib/mysqlclient/ob_mysql_result.h"
#include "share/schema/ob_tenant_schema_service.h"
#include "share/schema/ob_part_mgr_util.h"
#include "storage/tablelock/ob_table_lock_service.h"
#include "storage/tablelock/ob_lock_memtable.h"
#include "storage/tx_storage/ob_ls_service.h"
#include "storage/tx/ob_trans_service.h"
#include "mittest/mtlenv/tablelock/table_lock_common_env.h"
#undef private
#undef protected
static const char *TEST_FILE_NAME = "test_ob_obj_lock_garbage_collector";
static const char *BORN_CASE_NAME = "ObOBJLockGCBeforeRestartTest";
static const char *RESTART_CASE_NAME = "ObOBJLockGCAfterRestartTest";
namespace oceanbase
{
namespace transaction
{
namespace tablelock
{
// We modify the exection interval of the obj_lcoK_garbage_collector here,
// to make sure that the empty obj locks can be recycled in time.
// int64_t ObOBJLockGarbageCollector::GARBAGE_COLLECT_EXEC_INTERVAL = 1_s;
// int64_t ObOBJLockGarbageCollector::GARBAGE_COLLECT_PRECISION = 100_ms;
} // namespace tablelock
} // namespace transaction
namespace unittest
{
#define EXE_SQL(sql_str) \
ASSERT_EQ(OB_SUCCESS, sql.assign(sql_str)); \
ASSERT_EQ(OB_SUCCESS, sql_proxy.write(sql.ptr(), affected_rows));
#define EXE_SQL_FMT(...) \
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt(__VA_ARGS__)); \
ASSERT_EQ(OB_SUCCESS, sql_proxy.write(sql.ptr(), affected_rows));
#define WRITE_SQL_BY_CONN(conn, sql_str) \
ASSERT_EQ(OB_SUCCESS, sql.assign(sql_str)); \
ASSERT_EQ(OB_SUCCESS, conn->execute_write(OB_SYS_TENANT_ID, sql.ptr(), affected_rows));
#define WRITE_SQL_FMT_BY_CONN(conn, ...) \
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt(__VA_ARGS__)); \
ASSERT_EQ(OB_SUCCESS, conn->execute_write(OB_SYS_TENANT_ID, sql.ptr(), affected_rows));
#define READ_SQL_BY_CONN(conn, sql_str) \
ASSERT_EQ(OB_SUCCESS, sql.assign(sql_str)); \
ASSERT_EQ(OB_SUCCESS, conn->execute_read(OB_SYS_TENANT_ID, sql.ptr(), read_res));
class ObOBJLockGarbageCollectorTestBase : public ObSimpleClusterTestBase
{
public:
ObOBJLockGarbageCollectorTestBase() : ObSimpleClusterTestBase(TEST_FILE_NAME) {}
void get_ls(const uint64_t tenant_id, const ObLSID ls_id, ObLS *&ls)
{
LOG_INFO("get_ls start");
ls = nullptr;
share::ObTenantSwitchGuard tenant_guard;
ASSERT_EQ(OB_SUCCESS, tenant_guard.switch_to(tenant_id));
ObLSService *ls_svr = MTL(ObLSService*);
ASSERT_NE(nullptr, ls_svr);
ObLSHandle handle;
ASSERT_EQ(OB_SUCCESS, ls_svr->get_ls(ls_id, handle, ObLSGetMod::STORAGE_MOD));
ASSERT_NE(nullptr, ls = handle.get_ls());
LOG_INFO("get_ls end");
}
void get_table_id(const char* tname, uint64_t &table_id)
{
int ret = OB_SUCCESS;
static bool need_init = true;
if (need_init) {
need_init = false;
ASSERT_EQ(OB_SUCCESS, get_curr_simple_server().init_sql_proxy2("sys", "oceanbase"));
}
common::ObMySQLProxy &sql_proxy = get_curr_simple_server().get_sql_proxy2();
{
ObSqlString sql;
table_id = 0;
ASSERT_EQ(OB_SUCCESS, sql.assign_fmt("select table_id from __all_table where table_name='%s'", tname));
SMART_VAR(ObMySQLProxy::MySQLResult, res) {
ASSERT_EQ(OB_SUCCESS, sql_proxy.read(res, sql.ptr()));
sqlclient::ObMySQLResult *result = res.get_result();
ASSERT_NE(nullptr, result);
ASSERT_EQ(OB_SUCCESS, result->next());
ASSERT_EQ(OB_SUCCESS, result->get_uint("table_id", table_id));
}
}
}
void get_table_tablets(const uint64_t table_id,
ObTabletIDArray &tablet_list)
{
int ret = OB_SUCCESS;
int64_t latest_schema_version = OB_INVALID_VERSION;
ObRefreshSchemaStatus schema_status;
const uint64_t tenant_id = OB_SYS_TENANT_ID;
ObSchemaGetterGuard schema_guard;
const ObTableSchema *table_schema = nullptr;
ObMultiVersionSchemaService *schema_service = nullptr;
share::ObTenantSwitchGuard tenant_guard;
common::ObMySQLProxy &sql_proxy = get_curr_simple_server().get_sql_proxy();
tablet_list.reset();
ret = tenant_guard.switch_to(tenant_id);
ASSERT_EQ(OB_SUCCESS, ret);
schema_service = MTL(ObTenantSchemaService *)->get_schema_service();
ret = schema_service->get_schema_version_in_inner_table(
sql_proxy, schema_status, latest_schema_version);
ret =
schema_service->async_refresh_schema(tenant_id, latest_schema_version);
ASSERT_EQ(OB_SUCCESS, ret);
ret = schema_service->get_tenant_schema_guard(tenant_id, schema_guard);
ASSERT_EQ(OB_SUCCESS, ret);
ret = schema_guard.get_table_schema(tenant_id, table_id, table_schema);
ASSERT_EQ(OB_SUCCESS, ret);
ASSERT_NE(nullptr, table_schema);
if (PARTITION_LEVEL_ZERO == table_schema->get_part_level()) {
ret = tablet_list.push_back(table_schema->get_tablet_id());
ASSERT_EQ(OB_SUCCESS, ret);
} else {
ObCheckPartitionMode check_partition_mode = CHECK_PARTITION_MODE_NORMAL;
share::schema::ObPartitionSchemaIter partition_iter(*table_schema,
check_partition_mode);
ObTabletID tablet_id;
while (OB_SUCC(partition_iter.next_tablet_id(tablet_id))) {
ret = tablet_list.push_back(tablet_id);
ASSERT_EQ(OB_SUCCESS, ret);
}
ASSERT_EQ(OB_ITER_END, ret);
}
}
// This function is not atomic, it means that it may get obj lock even
// if it was recycled just now. However, we can coontrol the obj lock
// by the execution logic in the test case to avoid this situation.
void table_has_obj_lock(const uint64_t table_id, bool &has_obj_lock)
{
int ret = OB_SUCCESS;
ObLS *ls = nullptr;
ObLockIDIterator lock_id_iter;
ObLockID target_lock_id;
ObLockID curr_lock_id;
get_ls(OB_SYS_TENANT_ID, share::ObLSID(share::ObLSID::SYS_LS_ID), ls);
ASSERT_NE(nullptr, ls);
ASSERT_EQ(OB_SUCCESS, ls->get_lock_id_iter(lock_id_iter));
ASSERT_EQ(true, lock_id_iter.is_ready());
ASSERT_EQ(OB_SUCCESS,
transaction::tablelock::get_lock_id(table_id, target_lock_id));
has_obj_lock = false;
do {
if (OB_SUCC(lock_id_iter.get_next(curr_lock_id))) {
if (target_lock_id == curr_lock_id) {
has_obj_lock = true;
break;
}
}
} while (OB_SUCC(ret));
}
void wakeup_gc_thread()
{
ObTableLockService *tablelock_service = nullptr;
tablelock_service = MTL(transaction::tablelock::ObTableLockService *);
ASSERT_NE(nullptr, tablelock_service);
ASSERT_EQ(OB_SUCCESS, tablelock_service->garbage_collect_right_now());
}
void get_lock_memtable(ObLockMemtable *&lock_memtable)
{
ObLS *ls = nullptr;
ObTableHandleV2 table_handle;
get_ls(OB_SYS_TENANT_ID, share::ObLSID(share::ObLSID::SYS_LS_ID), ls);
ASSERT_NE(nullptr, ls);
ASSERT_EQ(OB_SUCCESS, ls->get_lock_table()->get_lock_memtable(table_handle));
ASSERT_EQ(OB_SUCCESS, table_handle.get_lock_memtable(lock_memtable));
}
void init_test_lock_op()
{
// table_id = 1 and trans_id = 1 are valid in real observer,
// so we modify them to a differnt value to avoid conflict
DEFAULT_TABLE = 123456;
DEFAULT_TRANS_ID = 123456;
init_default_lock_test_value();
}
};
class ObOBJLockGCBeforeRestartTest : public ObOBJLockGarbageCollectorTestBase {
};
class ObOBJLockGCAfterRestartTest : public ObOBJLockGarbageCollectorTestBase {
};
TEST_F(ObOBJLockGCBeforeRestartTest, create_table)
{
LOG_INFO("ObOBJLockGCBeforeRestartTest::create_table");
// 1. CREATE ONE PART TABLE
// 2. CREATE MULTI PART TABLE
int ret = OB_SUCCESS;
common::ObMySQLProxy &sql_proxy = get_curr_simple_server().get_sql_proxy();
// 1. ONE PART TABLE
LOG_INFO("create_table one part table start");
{
ObSqlString sql;
int64_t affected_rows = 0;
ASSERT_EQ(
OB_SUCCESS,
sql.assign_fmt(
"create table t_one_part (id int, data int, primary key(id))"));
ASSERT_EQ(OB_SUCCESS, sql_proxy.write(sql.ptr(), affected_rows));
}
LOG_INFO("create_table one part table succ");
LOG_INFO("insert data start");
{
ObSqlString sql;
int64_t affected_rows = 0;
ASSERT_EQ(OB_SUCCESS,
sql.assign_fmt("insert into t_one_part values(%d, %d)", 1, 1));
ASSERT_EQ(OB_SUCCESS, sql_proxy.write(sql.ptr(), affected_rows));
}
LOG_INFO("check row count");
{
int64_t row_cnt = 0;
ObSqlString sql;
ASSERT_EQ(OB_SUCCESS,
sql.assign_fmt("select count(*) row_cnt from t_one_part"));
SMART_VAR(ObMySQLProxy::MySQLResult, res) {
ASSERT_EQ(OB_SUCCESS, sql_proxy.read(res, sql.ptr()));
sqlclient::ObMySQLResult *result = res.get_result();
ASSERT_NE(nullptr, result);
ASSERT_EQ(OB_SUCCESS, result->next());
ASSERT_EQ(OB_SUCCESS, result->get_int("row_cnt", row_cnt));
}
ASSERT_EQ(row_cnt, 1);
}
// 2. MULTI PART TABLE
LOG_INFO("create_table multi part table start");
{
ObSqlString sql;
int64_t affected_rows = 0;
ASSERT_EQ(
OB_SUCCESS,
sql.assign_fmt(
"create table t_multi_part (id int, data int, primary key(id)) "
"partition by range(id) (partition p0 values less than (100), "
"partition p1 values less than (200), partition p2 values less "
"than MAXVALUE)"));
ASSERT_EQ(OB_SUCCESS, sql_proxy.write(sql.ptr(), affected_rows));
}
LOG_INFO("create_table multi part table succ");
LOG_INFO("insert data start");
{
ObSqlString sql;
int64_t affected_rows = 0;
ASSERT_EQ(OB_SUCCESS,
sql.assign_fmt("insert into t_multi_part values(%d, %d)", 1, 1));
ASSERT_EQ(OB_SUCCESS, sql_proxy.write(sql.ptr(), affected_rows));
ASSERT_EQ(
OB_SUCCESS,
sql.assign_fmt("insert into t_multi_part values(%d, %d)", 101, 101));
ASSERT_EQ(OB_SUCCESS, sql_proxy.write(sql.ptr(), affected_rows));
ASSERT_EQ(
OB_SUCCESS,
sql.assign_fmt("insert into t_multi_part values(%d, %d)", 202, 202));
ASSERT_EQ(OB_SUCCESS, sql_proxy.write(sql.ptr(), affected_rows));
}
LOG_INFO("check row count");
{
int64_t row_cnt = 0;
ObSqlString sql;
ASSERT_EQ(OB_SUCCESS,
sql.assign_fmt("select count(*) row_cnt from t_multi_part"));
SMART_VAR(ObMySQLProxy::MySQLResult, res) {
ASSERT_EQ(OB_SUCCESS, sql_proxy.read(res, sql.ptr()));
sqlclient::ObMySQLResult *result = res.get_result();
ASSERT_NE(nullptr, result);
ASSERT_EQ(OB_SUCCESS, result->next());
ASSERT_EQ(OB_SUCCESS, result->get_int("row_cnt", row_cnt));
}
ASSERT_EQ(row_cnt, 3);
}
}
TEST_F(ObOBJLockGCBeforeRestartTest, obj_lock_gc_with_tablelock_service)
{
LOG_INFO("ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service");
int ret = OB_SUCCESS;
ObTableLockOwnerID OWNER_ONE = 1;
ObTableLockOwnerID OWNER_TWO = 2;
uint64_t table_id = 0;
ObTableLockMode lock_mode = EXCLUSIVE;
share::ObTenantSwitchGuard tenant_guard;
bool has_obj_lock;
ret = tenant_guard.switch_to(OB_SYS_TENANT_ID);
ASSERT_EQ(OB_SUCCESS, ret);
ASSERT_NE(nullptr, MTL(ObTableLockService*));
// 1. LOCK TABLE AND UNLOCK TABLE
// 1.1 lock one part table
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 1.1");
get_table_id("t_one_part", table_id);
ret = MTL(ObTableLockService*)->lock_table(table_id,
lock_mode,
OWNER_ONE);
ASSERT_EQ(OB_SUCCESS, ret);
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_TRUE(has_obj_lock);
// 1.2 lock multi part table
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 1.2");
get_table_id("t_multi_part", table_id);
ret = MTL(ObTableLockService*)->lock_table(table_id,
lock_mode,
OWNER_TWO);
ASSERT_EQ(OB_SUCCESS, ret);
// 1.3 check obj lock status
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 1.3");
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_TRUE(has_obj_lock);
// 1.4 unlock one part table
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 1.4");
get_table_id("t_one_part", table_id);
ret = MTL(ObTableLockService*)->unlock_table(table_id,
lock_mode,
OWNER_ONE);
ASSERT_EQ(OB_SUCCESS, ret);
// 1.5 check obj lock status
// the obj lock is still be there, due to gc thread
// will try to recycle empty obj lock every 10 mins
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 1.5");
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_TRUE(has_obj_lock);
// 1.6 unlock multi part table
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 1.6");
get_table_id("t_multi_part", table_id);
ret = MTL(ObTableLockService*)->unlock_table(table_id,
lock_mode,
OWNER_TWO);
ASSERT_EQ(OB_SUCCESS, ret);
// 1.7 check obj lock status
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 1.7");
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_TRUE(has_obj_lock);
// 1.8 wake up gc thread to clear empty obj locks
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 1.8");
wakeup_gc_thread();
// wait gc thread to recycle obj lock
sleep(2);
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_FALSE(has_obj_lock);
get_table_id("t_one_part", table_id);
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_FALSE(has_obj_lock);
// 2. UNLOCK TABLE AND LOCK TABLE
// 2.1 unlock one part table
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 2.1");
get_table_id("t_one_part", table_id);
ret = MTL(ObTableLockService*)->unlock_table(table_id,
SHARE,
OWNER_ONE);
ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
// 2.2 check obj lock status
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 2.2");
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_FALSE(has_obj_lock);
// 2.3 lock one part table
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 2.3");
ret = MTL(ObTableLockService*)->lock_table(table_id,
SHARE,
OWNER_ONE);
ASSERT_EQ(OB_SUCCESS, ret);
// 2.4 check obj lock status
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 2.4");
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_TRUE(has_obj_lock);
// 3. LOCK WITH DIFFERNT OWNER AND UNLOCK
// 3.1 lock one part table with different owner
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 3.1");
get_table_id("t_one_part", table_id);
ret = MTL(ObTableLockService*)->lock_table(table_id,
SHARE,
OWNER_TWO);
ASSERT_EQ(OB_SUCCESS, ret);
// 3.2 check obj lock status
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 3.2");
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_TRUE(has_obj_lock);
// 3.3 unlock previous lock which is owned by owner one
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 3.3");
ret = MTL(ObTableLockService*)->unlock_table(table_id,
SHARE,
OWNER_ONE);
ASSERT_EQ(OB_SUCCESS, ret);
// 3.4 check obj lock status
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 3.4");
wakeup_gc_thread();
// wait gc thread to recycle obj lock
sleep(2);
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_TRUE(has_obj_lock);
// 3.5 unlock current lock which is owned by owner two
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 3.5");
ret = MTL(ObTableLockService*)->unlock_table(table_id,
SHARE,
OWNER_TWO);
ASSERT_EQ(OB_SUCCESS, ret);
// 3.6 check obj lock status
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 3.6");
wakeup_gc_thread();
// wait gc thread to recycle obj lock
sleep(2);
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_FALSE(has_obj_lock);
// 4. LOCK TABLE FOR RESTART
LOG_INFO(
"ObOBJLockGCBeforeRestartTest::obj_lock_gc_with_tablelock_service 4");
ret = MTL(ObTableLockService*)->lock_table(table_id,
lock_mode,
OWNER_ONE);
ASSERT_EQ(OB_SUCCESS, ret);
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_TRUE(has_obj_lock);
}
TEST_F(ObOBJLockGCBeforeRestartTest, op_list_gc_with_mock_lock_map)
{
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map");
ObLockMemtable *lock_memtable = nullptr;
ObOBJLock *obj_lock = nullptr;
bool has_obj_lock;
share::SCN commit_scn;
share::SCN commit_version;
share::ObTenantSwitchGuard tenant_guard;
commit_scn.set_base();
commit_version.set_base();
get_lock_memtable(lock_memtable);
ObOBJLockMap &obj_lock_map = lock_memtable->obj_lock_map_;
init_test_lock_op();
ASSERT_EQ(OB_SUCCESS, tenant_guard.switch_to(OB_SYS_TENANT_ID));
// 1. REPLAY UNLOCK OP AND LOCK OP,
// THEN COMMIT UNLOCK OP BEFORE LOCK OP
// 1.1 recover unlock op
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 1.1");
ASSERT_EQ(OB_SUCCESS,
obj_lock_map.recover_obj_lock(DEFAULT_OUT_TRANS_UNLOCK_OP));
// 1.2 check obj lock exists
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 1.2");
ASSERT_EQ(OB_SUCCESS, obj_lock_map.get_obj_lock_with_ref_(
DEFAULT_TABLE_LOCK_ID, obj_lock));
ASSERT_NE(nullptr, obj_lock);
// 1.3 recover lock op
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 1.3");
ASSERT_EQ(OB_SUCCESS,
obj_lock_map.recover_obj_lock(DEFAULT_OUT_TRANS_LOCK_OP));
// 1.4 verify obj lock status by log
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 1.4");
obj_lock->print();
// 1.5 commit unlock op
// We will try to compact lock ops if there's paired and committed
// lock op in the op_list when commit unlock op. However, this unlock
// op cannot be compactted here, because the lock op in the op_list
// is still running. You can find that it tried to compact but failed
// (by is_compcat = false) from the log.
// This situation will occur during replyaing in the followers.
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 1.5");
ASSERT_EQ(OB_SUCCESS, obj_lock_map.update_lock_status(
DEFAULT_OUT_TRANS_UNLOCK_OP, commit_version,
commit_scn, COMMIT_LOCK_OP_STATUS));
// 1.6 commit lock op
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 1.6");
ASSERT_EQ(OB_SUCCESS, obj_lock_map.update_lock_status(
DEFAULT_OUT_TRANS_LOCK_OP, commit_version,
commit_scn, COMMIT_LOCK_OP_STATUS));
// 1.7 verify obj lock status by log
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 1.7");
obj_lock->print();
// revert obj lock
obj_lock_map.lock_map_.revert(obj_lock);
// 1.8 check obj lock status
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 1.8");
table_has_obj_lock(DEFAULT_TABLE, has_obj_lock);
ASSERT_TRUE(has_obj_lock);
// 1.9 wake up gc thread
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 1.9");
wakeup_gc_thread();
// wait gc thread to recycle obj lock
sleep(2);
table_has_obj_lock(DEFAULT_TABLE, has_obj_lock);
ASSERT_FALSE(has_obj_lock);
// 2. REPLAY UNLOCK OP AND LOCK OP,
// THEN COMMIT LOCK OP BEFORE UNLOCK OP
// The lock ops will be compacted when the unlock op is committed,
// so there's no need to gc it in this situationl.
// 2.1 recover unlock op
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 2.1");
ASSERT_EQ(OB_SUCCESS,
obj_lock_map.recover_obj_lock(DEFAULT_OUT_TRANS_UNLOCK_OP));
// 2.2 check obj lock exists
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 2.2");
ASSERT_EQ(OB_SUCCESS, obj_lock_map.get_obj_lock_with_ref_(
DEFAULT_TABLE_LOCK_ID, obj_lock));
ASSERT_NE(nullptr, obj_lock);
// 2.3 recover lock op
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 2.3");
ASSERT_EQ(OB_SUCCESS,
obj_lock_map.recover_obj_lock(DEFAULT_OUT_TRANS_LOCK_OP));
// 2.4 verify obj lock status by log
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 2.4");
obj_lock->print();
// 2.5 commit lock op
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 2.5");
ASSERT_EQ(OB_SUCCESS, obj_lock_map.update_lock_status(
DEFAULT_OUT_TRANS_LOCK_OP, commit_version,
commit_scn, COMMIT_LOCK_OP_STATUS));
// 2.6 commit unlock op
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 2.6");
ASSERT_EQ(OB_SUCCESS, obj_lock_map.update_lock_status(
DEFAULT_OUT_TRANS_UNLOCK_OP, commit_version,
commit_scn, COMMIT_LOCK_OP_STATUS));
// 2.7 verify obj lock status by log
// You will find that obj lock is still there, due to we move the
// gc process to the gc thread in the backend. However, the obj lock
// is empty, i.e. there's no lock ops in it. Because the compaction
// process will execute directly if the lock op which will be committed
// is an out trans unlock lock op.
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 2.7");
obj_lock->print();
// revert obj lock
obj_lock_map.lock_map_.revert(obj_lock);
// 2.8 check obj lock status
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 2.8");
table_has_obj_lock(DEFAULT_TABLE, has_obj_lock);
ASSERT_TRUE(has_obj_lock);
// 2.9 wake up gc thread
LOG_INFO("ObOBJLockGCAfterRestartTest::op_list_gc_with_mock_lock_map 2.9");
wakeup_gc_thread();
// wait gc thread to recycle obj lock
sleep(2);
table_has_obj_lock(DEFAULT_TABLE, has_obj_lock);
ASSERT_FALSE(has_obj_lock);
}
TEST_F(ObOBJLockGCAfterRestartTest, obj_lock_gc_after_restart)
{
// You may find that gc thread tried to compact
// the obj lock of this table in force compaction
// mode from the log file. It means that the gc
// thread start successfully before leader comes
// back to work.
// (The gc thread will compact table lock ops in
// force mode only when it's called during the
// period when a follower is switching to leader)
LOG_INFO("ObOBJLockGCAfterRestartTest::obj_lock_gc_after_restart");
uint64_t table_id;
bool has_obj_lock;
// 1. check obj lock status of table t_one_part
LOG_INFO("ObOBJLockGCAfterRestartTest::obj_lock_gc_after_restart 1");
get_table_id("t_one_part", table_id);
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_TRUE(has_obj_lock);
// 2. check obj lock status of table t_multi_part
LOG_INFO("ObOBJLockGCAfterRestartTest::obj_lock_gc_after_restart 2");
get_table_id("t_multi_part", table_id);
table_has_obj_lock(table_id, has_obj_lock);
ASSERT_FALSE(has_obj_lock);
}
} // namespace unittest
} // namespace oceanbase
int main(int argc, char **argv)
{
// std::string gtest_file_name = std::string(TEST_FILE_NAME) + "_gtest.log";
// oceanbase::unittest::init_gtest_output(gtest_file_name);
ObSimpleServerRestartHelper restart_helper(argc,
argv,
TEST_FILE_NAME,
BORN_CASE_NAME,
RESTART_CASE_NAME);
restart_helper.set_sleep_sec(10); // sleep 10s for schema restore
OB_LOGGER.set_mod_log_levels("storage.tablelock:debug"); // it seems doesn't work
OB_LOGGER.set_enable_async_log(false);
return restart_helper.run();
}