/** * 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 USING_LOG_PREFIX TABLELOCK #include #define protected public #define private public #include "lib/allocator/page_arena.h" #include "storage/ls/ob_freezer.h" #include "storage/meta_mem/ob_tenant_meta_mem_mgr.h" #include "storage/tablelock/ob_lock_memtable.h" #include "storage/tablelock/ob_mem_ctx_table_lock.h" #include "table_lock_common_env.h" namespace oceanbase { using namespace common; using namespace share; using namespace storage; namespace transaction { namespace tablelock { int ObLockMemtable::update_lock_status(const ObTableLockOp &op_info, const share::SCN &commit_version, const share::SCN &commit_scn, const ObTableLockOpStatus status) { UNUSEDx(op_info, commit_version, commit_scn, status); return OB_SUCCESS; } class TestMemCtxTableLock : public ::testing::Test { public: TestMemCtxTableLock() : ls_id_(1), fake_t3m_(common::OB_SERVER_TENANT_ID), mem_ctx_(allocator_) { LOG_INFO("construct TestMemCtxTableLock"); } ~TestMemCtxTableLock() {} static void SetUpTestCase(); static void TearDownTestCase(); virtual void SetUp() override { create_memtable(); init_mem_ctx(); LOG_INFO("set up success"); } virtual void TearDown() override { LOG_INFO("tear down success"); } public: void create_memtable() { ObITable::TableKey table_key; table_key.table_type_ = ObITable::LOCK_MEMTABLE; table_key.tablet_id_ = LS_LOCK_TABLET; table_key.scn_range_.start_scn_.convert_for_gts(1); // fake table_key.scn_range_.end_scn_.convert_for_gts(2); // fake ASSERT_EQ(OB_SUCCESS, memtable_.init(table_key, ls_id_, &freezer_)); ASSERT_EQ(OB_SUCCESS, handle_.set_table(&memtable_, &fake_t3m_, ObITable::LOCK_MEMTABLE)); } void init_mem_ctx() { ASSERT_EQ(OB_SUCCESS, mem_ctx_.init(handle_)); } private: ObLSID ls_id_; ObLockMemtable memtable_; ObTableHandleV2 handle_; ObTenantMetaMemMgr fake_t3m_; ObFreezer freezer_; ObLockMemCtx mem_ctx_; ObArenaAllocator allocator_; }; void TestMemCtxTableLock::SetUpTestCase() { LOG_INFO("SetUpTestCase"); init_default_lock_test_value(); } void TestMemCtxTableLock::TearDownTestCase() { LOG_INFO("TearDownTestCase"); } TEST_F(TestMemCtxTableLock, add_lock_record) { LOG_INFO("TestMemCtxTableLock::add_lock_record"); int ret = OB_SUCCESS; bool lock_exist = false; unsigned char lock_mode_in_same_trans = 0x0; ObMemCtxLockOpLinkNode *lock_op_node = nullptr; // 1. IN TRANS LOCK // 1.1 add lock record LOG_INFO("TestMemCtxTableLock::add_lock_record 1.1"); ret = mem_ctx_.add_lock_record(DEFAULT_IN_TRANS_LOCK_OP, lock_op_node); ASSERT_EQ(OB_SUCCESS, ret); // 1.2 check lock exist LOG_INFO("TestMemCtxTableLock::add_lock_record 1.2"); 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_, DEFAULT_IN_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, true); // 2. OUT TRANS LOCK // 2.1 add lock redord LOG_INFO("TestMemCtxTableLock::add_lock_record 2.1"); ret = mem_ctx_.add_lock_record(DEFAULT_OUT_TRANS_LOCK_OP, lock_op_node); ASSERT_EQ(OB_SUCCESS, ret); // 2.2 check lock exist LOG_INFO("TestMemCtxTableLock::add_lock_record 2.2"); ret = mem_ctx_.check_lock_exist(DEFAULT_OUT_TRANS_LOCK_OP.lock_id_, DEFAULT_OUT_TRANS_LOCK_OP.owner_id_, DEFAULT_OUT_TRANS_LOCK_OP.lock_mode_, DEFAULT_OUT_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, true); // 3. REMOVE CHECK // 3.1 in trans lock remove test LOG_INFO("TestMemCtxTableLock::add_lock_record 3.1"); mem_ctx_.remove_lock_record(DEFAULT_IN_TRANS_LOCK_OP); 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_, DEFAULT_IN_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, false); // 3.2 out trans lock remove test LOG_INFO("TestMemCtxTableLock::add_lock_record 3.2"); mem_ctx_.remove_lock_record(DEFAULT_OUT_TRANS_LOCK_OP); ret = mem_ctx_.check_lock_exist(DEFAULT_OUT_TRANS_LOCK_OP.lock_id_, DEFAULT_OUT_TRANS_LOCK_OP.owner_id_, DEFAULT_OUT_TRANS_LOCK_OP.lock_mode_, DEFAULT_OUT_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, false); // 4 REMOVE WITH PTR // 4.1 add lock record LOG_INFO("TestMemCtxTableLock::add_lock_record 4.1"); ret = mem_ctx_.add_lock_record(DEFAULT_IN_TRANS_LOCK_OP, lock_op_node); ASSERT_EQ(OB_SUCCESS, 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_, DEFAULT_IN_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, true); // 4.2 remove lock record LOG_INFO("TestMemCtxTableLock::add_lock_record 4.2"); mem_ctx_.remove_lock_record(lock_op_node); 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_, DEFAULT_IN_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, false); } TEST_F(TestMemCtxTableLock, clear_table_lock) { LOG_INFO("TestMemCtxTableLock::clear_table_lock"); int ret = OB_SUCCESS; bool lock_exist = false; unsigned char lock_mode_in_same_trans = 0x0; ObMemCtxLockOpLinkNode *lock_op_node = nullptr; bool is_committed = false; share::SCN commit_version; share::SCN commit_scn; commit_version.set_min(); commit_scn.set_min(); // 1 EMPTY CLEAR LOG_INFO("TestMemCtxTableLock::clear_table_lock 1"); ret = mem_ctx_.clear_table_lock(is_committed, commit_version, commit_scn); ASSERT_EQ(OB_SUCCESS, ret); // 2 COMMIT CLEAR is_committed = true; commit_version = share::SCN::plus(share::SCN::min_scn(), 1); // 2.1 in trans lock record LOG_INFO("TestMemCtxTableLock::clear_table_lock 2.1"); ret = mem_ctx_.add_lock_record(DEFAULT_IN_TRANS_LOCK_OP, lock_op_node); ASSERT_EQ(OB_SUCCESS, ret); ret = mem_ctx_.clear_table_lock(is_committed, commit_version, commit_scn); ASSERT_EQ(OB_SUCCESS, 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_, DEFAULT_IN_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, false); // 2.2 out trans lock record LOG_INFO("TestMemCtxTableLock::clear_table_lock 2.2"); ret = mem_ctx_.add_lock_record(DEFAULT_OUT_TRANS_LOCK_OP, lock_op_node); ASSERT_EQ(OB_SUCCESS, ret); ret = mem_ctx_.clear_table_lock(is_committed, commit_version, commit_scn); ASSERT_EQ(OB_SUCCESS, ret); ret = mem_ctx_.check_lock_exist(DEFAULT_OUT_TRANS_LOCK_OP.lock_id_, DEFAULT_OUT_TRANS_LOCK_OP.owner_id_, DEFAULT_OUT_TRANS_LOCK_OP.lock_mode_, DEFAULT_OUT_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, false); // 2.3 out trans unlock record LOG_INFO("TestMemCtxTableLock::clear_table_lock 2.3"); ret = mem_ctx_.add_lock_record(DEFAULT_OUT_TRANS_UNLOCK_OP, lock_op_node); ASSERT_EQ(OB_SUCCESS, ret); ret = mem_ctx_.clear_table_lock(is_committed, commit_version, commit_scn); ASSERT_EQ(OB_SUCCESS, ret); ret = mem_ctx_.check_lock_exist(DEFAULT_OUT_TRANS_UNLOCK_OP.lock_id_, DEFAULT_OUT_TRANS_UNLOCK_OP.owner_id_, DEFAULT_OUT_TRANS_UNLOCK_OP.lock_mode_, DEFAULT_OUT_TRANS_UNLOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, false); // 3 ABORT CLEAR is_committed = false; LOG_INFO("TestMemCtxTableLock::clear_table_lock 3.1"); ret = mem_ctx_.add_lock_record(DEFAULT_IN_TRANS_LOCK_OP, lock_op_node); ASSERT_EQ(OB_SUCCESS, ret); ret = mem_ctx_.clear_table_lock(is_committed, commit_version, commit_scn); 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_, DEFAULT_IN_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, false); } TEST_F(TestMemCtxTableLock, rollback_table_lock) { LOG_INFO("TestMemCtxTableLock::rollback_table_lock"); int ret = OB_SUCCESS; bool lock_exist = false; ObMemCtxLockOpLinkNode *lock_op_node = nullptr; unsigned char lock_mode_in_same_trans = 0x0; // 1.1 add lock record LOG_INFO("TestMemCtxTableLock::rollback_table_lock 1.1"); ObTableLockOp lock_op1 = DEFAULT_IN_TRANS_LOCK_OP; lock_op1.lock_seq_no_ = 1; ret = mem_ctx_.add_lock_record(lock_op1, lock_op_node); ASSERT_EQ(OB_SUCCESS, ret); ObTableLockOp lock_op2 = DEFAULT_OUT_TRANS_LOCK_OP; lock_op2.lock_seq_no_ = 2; ret = mem_ctx_.add_lock_record(lock_op2, lock_op_node); ASSERT_EQ(OB_SUCCESS, ret); // 1.2 check exist LOG_INFO("TestMemCtxTableLock::rollback_table_lock 1.2"); 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_, DEFAULT_IN_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, true); ret = mem_ctx_.check_lock_exist(DEFAULT_OUT_TRANS_LOCK_OP.lock_id_, DEFAULT_OUT_TRANS_LOCK_OP.owner_id_, DEFAULT_OUT_TRANS_LOCK_OP.lock_mode_, DEFAULT_OUT_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, true); // 1.3 rollback LOG_INFO("TestMemCtxTableLock::rollback_table_lock 1.3"); int64_t rollback_to_seq_no = 1; ret = mem_ctx_.rollback_table_lock(rollback_to_seq_no); ASSERT_EQ(OB_SUCCESS, 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_, DEFAULT_IN_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, true); ret = mem_ctx_.check_lock_exist(DEFAULT_OUT_TRANS_LOCK_OP.lock_id_, DEFAULT_OUT_TRANS_LOCK_OP.owner_id_, DEFAULT_OUT_TRANS_LOCK_OP.lock_mode_, DEFAULT_OUT_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, false); // 1.4 rollback again LOG_INFO("TestMemCtxTableLock::rollback_table_lock 1.4"); rollback_to_seq_no = 0; ret = mem_ctx_.rollback_table_lock(rollback_to_seq_no); ASSERT_EQ(OB_SUCCESS, 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_, DEFAULT_IN_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, false); } TEST_F(TestMemCtxTableLock, check_lock_need_replay) { LOG_INFO("TestMemCtxTableLock::check_lock_need_replay"); unsigned char lock_mode_in_same_trans = 0x0; int ret = OB_SUCCESS; bool lock_exist = false; share::SCN scn; scn.set_min(); bool need_replay = false; ObMemCtxLockOpLinkNode *lock_op_node = nullptr; // 1.1 empty check. LOG_INFO("TestMemCtxTableLock::check_lock_need_replay 1.1"); ret = mem_ctx_.check_lock_need_replay(scn, DEFAULT_IN_TRANS_LOCK_OP, need_replay); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(need_replay, true); // 1.2 add a lock record LOG_INFO("TestMemCtxTableLock::check_lock_need_replay 1.2"); ret = mem_ctx_.add_lock_record(DEFAULT_IN_TRANS_LOCK_OP, lock_op_node); ASSERT_EQ(OB_SUCCESS, ret); // 1.3 not need replay because the lock op exist. LOG_INFO("TestMemCtxTableLock::check_lock_need_replay 1.3"); ret = mem_ctx_.check_lock_need_replay(scn, DEFAULT_IN_TRANS_LOCK_OP, need_replay); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(need_replay, false); // 1.4 need replay because of lock op no exist. LOG_INFO("TestMemCtxTableLock::check_lock_need_replay 1.4"); ret = mem_ctx_.check_lock_need_replay(scn, DEFAULT_OUT_TRANS_LOCK_OP, need_replay); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(need_replay, true); // 1.5 can not replay because of log ts too small LOG_INFO("TestMemCtxTableLock::check_lock_need_replay 1.5"); scn = share::SCN::plus(share::SCN::min_scn(), 2); mem_ctx_.max_durable_scn_ = scn; scn = share::SCN::plus(share::SCN::min_scn(), 1); ret = mem_ctx_.check_lock_need_replay(scn, DEFAULT_OUT_TRANS_LOCK_OP, need_replay); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(need_replay, false); // 1.6 clear the lock op LOG_INFO("TestMemCtxTableLock::check_lock_need_replay 1.6"); mem_ctx_.remove_lock_record(lock_op_node); 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_, DEFAULT_IN_TRANS_LOCK_OP.op_type_, lock_exist, lock_mode_in_same_trans); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_EQ(lock_exist, false); } TEST_F(TestMemCtxTableLock, get_table_lock_store_info) { LOG_INFO("TestMemCtxTableLock::check_lock_need_replay"); int ret = OB_SUCCESS; bool lock_exist = false; ObMemCtxLockOpLinkNode *lock_op_node = nullptr; ObTableLockInfo table_lock_info; int64_t op_count = 0; share::SCN scn; scn.set_min(); // 1 CHECK NOT LOGGED LOCK // 1.1 add in trans lock LOG_INFO("TestMemCtxTableLock::check_lock_need_replay 1.1"); ret = mem_ctx_.add_lock_record(DEFAULT_IN_TRANS_LOCK_OP, lock_op_node); ASSERT_EQ(OB_SUCCESS, ret); ret = mem_ctx_.get_table_lock_store_info(table_lock_info); ASSERT_EQ(OB_SUCCESS, ret); op_count = table_lock_info.table_lock_ops_.count(); ASSERT_EQ(0, op_count); // 2 CHECK LOGGED LOCK LOG_INFO("TestMemCtxTableLock::check_lock_need_replay 2.1"); scn.set_base(); mem_ctx_.set_log_synced(lock_op_node, scn); ret = mem_ctx_.get_table_lock_store_info(table_lock_info); ASSERT_EQ(OB_SUCCESS, ret); op_count = table_lock_info.table_lock_ops_.count(); ASSERT_EQ(1, op_count); ASSERT_EQ(scn, table_lock_info.max_durable_scn_); // 3 CHECK LOGGED WITH NOT LOGGED LOG_INFO("TestMemCtxTableLock::check_lock_need_replay 3.1"); table_lock_info.reset(); ret = mem_ctx_.add_lock_record(DEFAULT_OUT_TRANS_LOCK_OP, lock_op_node); ASSERT_EQ(OB_SUCCESS, ret); ret = mem_ctx_.get_table_lock_store_info(table_lock_info); ASSERT_EQ(OB_SUCCESS, ret); op_count = table_lock_info.table_lock_ops_.count(); ASSERT_EQ(1, op_count); ASSERT_EQ(scn, table_lock_info.max_durable_scn_); // 4 CHECK TWO LOGGED LOG_INFO("TestMemCtxTableLock::check_lock_need_replay 4.1"); table_lock_info.reset(); scn = share::SCN::plus(share::SCN::min_scn(), 2); mem_ctx_.set_log_synced(lock_op_node, scn); ret = mem_ctx_.get_table_lock_store_info(table_lock_info); ASSERT_EQ(OB_SUCCESS, ret); op_count = table_lock_info.table_lock_ops_.count(); ASSERT_EQ(2, op_count); ASSERT_EQ(scn, table_lock_info.max_durable_scn_); // 5 CLEAN TEST LOG_INFO("TestMemCtxTableLock::check_lock_need_replay 5.1"); mem_ctx_.remove_lock_record(DEFAULT_IN_TRANS_LOCK_OP); mem_ctx_.remove_lock_record(DEFAULT_OUT_TRANS_LOCK_OP); table_lock_info.reset(); ret = mem_ctx_.get_table_lock_store_info(table_lock_info); ASSERT_EQ(OB_SUCCESS, ret); op_count = table_lock_info.table_lock_ops_.count(); ASSERT_EQ(0, op_count); } } // tablelock } // transaction } // oceanbase int main(int argc, char **argv) { system("rm -f test_mem_ctx_table_lock.log*"); oceanbase::common::ObLogger::get_logger().set_file_name("test_mem_ctx_table_lock.log", true); oceanbase::common::ObLogger::get_logger().set_log_level("DEBUG"); oceanbase::common::ObLogger::get_logger().set_enable_async_log(false); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }