[TABLELOCK] fix core in replace_lock

This commit is contained in:
YangEfei 2024-12-03 15:48:42 +00:00 committed by ob-robot
parent 04e4cfe68f
commit 67d03a812f
2 changed files with 161 additions and 1 deletions

View File

@ -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

View File

@ -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));