From 67d03a812f35441437d5ce7d850a32791973a508 Mon Sep 17 00:00:00 2001 From: YangEfei Date: Tue, 3 Dec 2024 15:48:42 +0000 Subject: [PATCH] [TABLELOCK] fix core in replace_lock --- .../test_ob_table_lock_service.cpp | 157 ++++++++++++++++++ src/storage/tablelock/ob_obj_lock.cpp | 5 +- 2 files changed, 161 insertions(+), 1 deletion(-) diff --git a/mittest/simple_server/test_ob_table_lock_service.cpp b/mittest/simple_server/test_ob_table_lock_service.cpp index 2de14c1c1..0d515c7f3 100644 --- a/mittest/simple_server/test_ob_table_lock_service.cpp +++ b/mittest/simple_server/test_ob_table_lock_service.cpp @@ -19,6 +19,7 @@ #define private public #include "env/ob_simple_cluster_test_base.h" +#include "multi_replica/env/ob_multi_replica_util.h" #include "lib/mysqlclient/ob_mysql_result.h" #include "share/schema/ob_tenant_schema_service.h" #include "share/schema/ob_part_mgr_util.h" @@ -2068,6 +2069,162 @@ TEST_F(ObTableLockServiceTest, replace_all_locks) owner_one); ASSERT_EQ(OB_SUCCESS, ret); } + +TEST_F(ObTableLockServiceTest, replace_all_locks_with_dml_locks) +{ + LOG_INFO("ObTableLockServiceTest::replace_all_locks_with_dml_locks"); + int ret = OB_SUCCESS; + common::ObMySQLProxy &sql_proxy = get_curr_simple_server().get_sql_proxy(); + ACQUIRE_CONN_FROM_SQL_PROXY(conn1, sql_proxy); + ACQUIRE_CONN_FROM_SQL_PROXY(conn2, sql_proxy); + + ObTxParam tx_param; + share::ObTenantSwitchGuard tenant_guard; + ObTxDesc *tx_desc1 = nullptr; + ObTxDesc *tx_desc2= nullptr; + ObTxDesc *tx_desc3= nullptr; + ObTxDesc *tx_desc4= nullptr; + ObTransService *txs = nullptr; + uint64_t table_id = 0; + ObTableLockMode check_lock_mode = EXCLUSIVE; + ObTableLockMode new_lock_mode = SHARE; + ObTableLockOwnerID owner_one(ObTableLockOwnerID::get_owner_by_value(1)); + ObTableLockOwnerID owner_two(ObTableLockOwnerID::get_owner_by_value(2)); + ObTableLockMode lock_mode_one = ROW_SHARE; + ObTableLockMode lock_mode_two = ROW_EXCLUSIVE; + ObLockTableRequest lock_arg; + ObLockTableRequest new_lock_arg; + ObUnLockTableRequest unlock_arg; + ObUnLockTableRequest unlock_arg2; + ObArenaAllocator allocator; + ObReplaceAllLocksRequest replace_lock_arg(allocator); + int64_t stmt_timeout_ts = -1; + + tx_param.access_mode_ = ObTxAccessMode::RW; + tx_param.isolation_ = ObTxIsolationLevel::RC; + tx_param.timeout_us_ = 6000 * 1000L; + tx_param.lock_timeout_us_ = -1; + tx_param.cluster_id_ = GCONF.cluster_id; + + ret = tenant_guard.switch_to(OB_SYS_TENANT_ID); + ASSERT_EQ(OB_SUCCESS, ret); + ASSERT_NE(nullptr, MTL(ObTableLockService*)); + + txs = MTL(ObTransService*); + ASSERT_NE(nullptr, txs); + + // 1. insert rows (get 2 RX IN_TRANS locks) + LOG_INFO("ObTableLockServiceTest::replace_all_locks_with_dml_locks 1"); + + OB_LOG(INFO, "insert 2 rows without commit"); + { + WRITE_SQL_BY_CONN(conn1, "begin;"); + WRITE_SQL_BY_CONN(conn1, "INSERT INTO t_one_part VALUES (2, 2);"); + WRITE_SQL_BY_CONN(conn2, "begin;"); + WRITE_SQL_BY_CONN(conn2, "INSERT INTO t_one_part VALUES (3, 3);"); + } + + // 2. lock table with RX lock + get_table_id("t_one_part", table_id); + lock_arg.table_id_ = table_id; + lock_arg.owner_id_ = owner_one; + lock_arg.lock_mode_ = lock_mode_one; + lock_arg.op_type_ = OUT_TRANS_LOCK; + lock_arg.timeout_us_ = 0; + + unlock_arg.table_id_ = table_id; + unlock_arg.owner_id_ = owner_one; + unlock_arg.lock_mode_ = lock_mode_one; + unlock_arg.op_type_ = OUT_TRANS_UNLOCK; + unlock_arg.timeout_us_ = 0; + + ASSERT_EQ(OB_SUCCESS, txs->acquire_tx(tx_desc1)); + ret = MTL(ObTableLockService*)->lock(*tx_desc1, + tx_param, + lock_arg); + ASSERT_EQ(OB_SUCCESS, ret); + + stmt_timeout_ts = ObTimeUtility::current_time() + 1000 * 1000; + ret = txs->commit_tx(*tx_desc1, stmt_timeout_ts); + ASSERT_EQ(OB_SUCCESS, ret); + ret = txs->release_tx(*tx_desc1); + ASSERT_EQ(OB_SUCCESS, ret); + + // 3. check lock + LOG_INFO("ObTableLockServiceTest::replace_all_locks_with_dml_locks 3"); + ret = MTL(ObTableLockService*)->lock_table(table_id, + check_lock_mode, + owner_two); + ASSERT_EQ(OB_EAGAIN, ret); + + // 4. replace lock + LOG_INFO("ObTableLockServiceTest::replace_all_locks_with_dml_locks 4"); + new_lock_arg.table_id_ = table_id; + new_lock_arg.owner_id_ = owner_two; + new_lock_arg.lock_mode_ = EXCLUSIVE; + new_lock_arg.op_type_ = OUT_TRANS_LOCK; + new_lock_arg.timeout_us_ = 0; + + replace_lock_arg.lock_req_ = &new_lock_arg; + replace_lock_arg.unlock_req_list_.push_back(&unlock_arg); + + ASSERT_EQ(OB_SUCCESS, txs->acquire_tx(tx_desc2)); + ret = MTL(ObTableLockService*)->replace_lock(*tx_desc2, + tx_param, + replace_lock_arg); + ASSERT_EQ(OB_EAGAIN, ret); + + stmt_timeout_ts = ObTimeUtility::current_time() + 1000 * 1000; + ret = txs->rollback_tx(*tx_desc2); + ASSERT_EQ(OB_SUCCESS, ret); + ret = txs->release_tx(*tx_desc2); + ASSERT_EQ(OB_SUCCESS, ret); + + // 5. commit insert (release 2 RX IN_TRANS locks) + LOG_INFO("ObTableLockServiceTest::replace_all_locks_with_dml_locks 5"); + { + WRITE_SQL_BY_CONN(conn1, "commit;"); + WRITE_SQL_BY_CONN(conn2, "commit;"); + } + + // 6. replace again + LOG_INFO("ObTableLockServiceTest::replace_all_locks_with_dml_locks 6"); + ASSERT_EQ(OB_SUCCESS, txs->acquire_tx(tx_desc3)); + ret = MTL(ObTableLockService*)->replace_lock(*tx_desc3, + tx_param, + replace_lock_arg); + ASSERT_EQ(OB_SUCCESS, ret); + + stmt_timeout_ts = ObTimeUtility::current_time() + 1000 * 1000; + ret = txs->commit_tx(*tx_desc3, stmt_timeout_ts); + ASSERT_EQ(OB_SUCCESS, ret); + ret = txs->release_tx(*tx_desc3); + ASSERT_EQ(OB_SUCCESS, ret); + + // 7. check replace successfully + LOG_INFO("ObTableLockServiceTest::replace_all_locks_with_dml_locks 7"); + ret = MTL(ObTableLockService*)->lock_table(table_id, + EXCLUSIVE, + owner_two); + ASSERT_EQ(OB_SUCCESS, ret); + + // 8. unlock + LOG_INFO("ObTableLockServiceTest::replace_all_locks_with_dml_locks 8"); + ASSERT_EQ(OB_SUCCESS, txs->acquire_tx(tx_desc4)); + unlock_arg2.table_id_ = table_id; + unlock_arg2.owner_id_ = owner_two; + unlock_arg2.lock_mode_ = EXCLUSIVE; + unlock_arg2.op_type_ = OUT_TRANS_UNLOCK; + unlock_arg2.timeout_us_ = 0; + ret = MTL(ObTableLockService *)->unlock(*tx_desc4, tx_param, unlock_arg2); + ASSERT_EQ(OB_SUCCESS, ret); + + stmt_timeout_ts = ObTimeUtility::current_time() + 1000 * 1000; + ret = txs->commit_tx(*tx_desc4, stmt_timeout_ts); + ASSERT_EQ(OB_SUCCESS, ret); + ret = txs->release_tx(*tx_desc4); + ASSERT_EQ(OB_SUCCESS, ret); +} }// end unittest } // end oceanbase diff --git a/src/storage/tablelock/ob_obj_lock.cpp b/src/storage/tablelock/ob_obj_lock.cpp index abdd99370..c49623aea 100644 --- a/src/storage/tablelock/ob_obj_lock.cpp +++ b/src/storage/tablelock/ob_obj_lock.cpp @@ -1179,7 +1179,10 @@ int ObOBJLock::get_exist_lock_mode_without_curr_trans(const uint64_t lock_mode_c // in the replace situation, 1 is lock_op which is completed, and the // other is unlock_op which is still doing. It means this lock_mode has // no locks on it now. - if (lock_mode_cnt[i] >= 2) { + // NOTICE: we can only replace OUT_TRANS_LOCK, so the lock_op which is completed and the + // unlock_op which is doing should be both in the map. If there're less than 2 lock_ops + // in the map, we don't need to check allow replace, too. + if (lock_mode_cnt[i] >= 2 && OB_NOT_NULL(map_[i]) && map_[i]->get_size() >= 2) { bool allow_replace = false; if (OB_FAIL(check_allow_replace_from_list_(map_[i], trans_id, allow_replace))) { LOG_WARN("check allow replace failed", K(i));