BUGFIX: tablelock release memctx lock before retry
This commit is contained in:
@ -971,6 +971,332 @@ TEST_F(TestLockMemtable, out_trans_multi_source)
|
||||
// ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
|
||||
}
|
||||
|
||||
TEST_F(TestLockMemtable, test_lock_retry)
|
||||
{
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry");
|
||||
// 1. LOCK PART CTX
|
||||
// 2. DO OBJ LOCK
|
||||
// 3. CHECK:
|
||||
// the lock at map;
|
||||
// the lock at lock mem ctx;
|
||||
// the lock at part ctx;
|
||||
// 4. UNLOCK PART CTX
|
||||
// 5. DO OBJ LOCK AGAIN
|
||||
// 6. CHECK
|
||||
|
||||
// ctx.lock_.lock()
|
||||
// CtxLockGuard guard(lock_, need_lock);
|
||||
|
||||
int ret = OB_SUCCESS;
|
||||
bool is_try_lock = true;
|
||||
int64_t expired_time = ObClockGenerator::getClock() + 1 * 1000 * 1000;
|
||||
ObLockParam param;
|
||||
ObOBJLock *obj_lock = NULL;
|
||||
ObMemtableCtx *mem_ctx = NULL;
|
||||
bool lock_exist = false;
|
||||
share::SCN min_commited_scn;
|
||||
share::SCN flushed_scn;
|
||||
unsigned char lock_mode_in_same_trans = 0x0;
|
||||
|
||||
ObTableLockOp lock_op = DEFAULT_OUT_TRANS_LOCK_OP;
|
||||
min_commited_scn.set_min();
|
||||
flushed_scn.set_min();
|
||||
|
||||
MyTxCtx default_ctx;
|
||||
ObStoreCtx store_ctx;
|
||||
ObStoreCtx unlock_store_ctx;
|
||||
// 1. lock part ctx.
|
||||
start_tx(DEFAULT_TRANS_ID, default_ctx);
|
||||
get_store_ctx(default_ctx, store_ctx);
|
||||
default_ctx.tx_ctx_.change_to_leader();
|
||||
default_ctx.tx_ctx_.lock_.lock();
|
||||
|
||||
// 2. do obj lock
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry 2 do obj lock");
|
||||
param.is_try_lock_ = is_try_lock;
|
||||
param.expired_time_ = expired_time;
|
||||
ret = memtable_.lock(param,
|
||||
store_ctx,
|
||||
lock_op);
|
||||
ASSERT_EQ(OB_TIMEOUT, ret);
|
||||
|
||||
// 3. check
|
||||
// 3.1 check lock at map
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry 3.1 check lock at map");
|
||||
ret = memtable_.obj_lock_map_.get_obj_lock_with_ref_(DEFAULT_TABLE_LOCK_ID,
|
||||
obj_lock);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
ASSERT_EQ(0, obj_lock->size_without_lock());
|
||||
|
||||
// 3.2 check lock at mem ctx
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry 3.2 check lock at mem ctx");
|
||||
mem_ctx = store_ctx.mvcc_acc_ctx_.mem_ctx_;
|
||||
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(lock_exist, false);
|
||||
|
||||
// 3.3 check lock at part ctx
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry 3.3 check lock at part ctx");
|
||||
ASSERT_EQ(default_ctx.tx_ctx_.mds_cache_.mds_list_.empty(), true);
|
||||
|
||||
// 4. unlock part ctx
|
||||
default_ctx.tx_ctx_.lock_.unlock();
|
||||
|
||||
// 5. do obj lock again.
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry 5 do lock again");
|
||||
param.is_try_lock_ = is_try_lock;
|
||||
param.expired_time_ = ObClockGenerator::getClock() + 1 * 1000 * 1000;
|
||||
ret = memtable_.lock(param,
|
||||
store_ctx,
|
||||
lock_op);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
// 6. check
|
||||
// 6.1 check lock at map
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry 6.1 check lock at map");
|
||||
ret = memtable_.obj_lock_map_.get_obj_lock_with_ref_(DEFAULT_TABLE_LOCK_ID,
|
||||
obj_lock);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
ASSERT_EQ(1, obj_lock->size_without_lock());
|
||||
obj_lock->print();
|
||||
|
||||
// 6.2 check lock at mem ctx
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry 6.2 check lock at mem ctx");
|
||||
mem_ctx = store_ctx.mvcc_acc_ctx_.mem_ctx_;
|
||||
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(lock_exist, true);
|
||||
|
||||
// 6.3 check lock at part ctx
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry 6.3 check lock at part ctx");
|
||||
ASSERT_EQ(default_ctx.tx_ctx_.mds_cache_.mds_list_.empty(), false);
|
||||
|
||||
// 7. clean
|
||||
// 7.1 commit out trans lock
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry 7.1");
|
||||
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,
|
||||
COMMIT_LOCK_OP_STATUS);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
min_commited_scn = memtable_.obj_lock_map_.get_min_ddl_committed_scn(flushed_scn);
|
||||
ASSERT_EQ(min_commited_scn, commit_scn);
|
||||
memtable_.obj_lock_map_.print();
|
||||
|
||||
// 7.2 unlock complete lock
|
||||
MyTxCtx ctx2;
|
||||
start_tx(TRANS_ID2, ctx2);
|
||||
get_store_ctx(ctx2, unlock_store_ctx);
|
||||
ctx2.tx_ctx_.change_to_leader();
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry 7.2");
|
||||
expired_time = ObClockGenerator::getClock() + 1 * 1000 * 1000;
|
||||
ret = memtable_.unlock(unlock_store_ctx,
|
||||
DEFAULT_OUT_TRANS_UNLOCK_OP,
|
||||
is_try_lock,
|
||||
expired_time);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
memtable_.obj_lock_map_.print();
|
||||
// 7.3 unlock commit
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry 7.3");
|
||||
ret = memtable_.update_lock_status(DEFAULT_OUT_TRANS_UNLOCK_OP,
|
||||
commit_version,
|
||||
commit_scn,
|
||||
COMMIT_LOCK_OP_STATUS);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
memtable_.obj_lock_map_.print();
|
||||
min_commited_scn = memtable_.obj_lock_map_.get_min_ddl_committed_scn(flushed_scn);
|
||||
ASSERT_EQ(min_commited_scn, share::SCN::max_scn());
|
||||
|
||||
// 7.4 check
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry 7.4");
|
||||
MyTxCtx ctx3;
|
||||
ObStoreCtx unlock_store_ctx3;
|
||||
start_tx(TRANS_ID3, ctx3);
|
||||
get_store_ctx(ctx3, unlock_store_ctx3);
|
||||
ctx3.tx_ctx_.change_to_leader();
|
||||
expired_time = ObClockGenerator::getClock() + 1 * 1000 * 1000;
|
||||
ret = memtable_.unlock(unlock_store_ctx3,
|
||||
DEFAULT_OUT_TRANS_UNLOCK_OP,
|
||||
is_try_lock,
|
||||
expired_time);
|
||||
ASSERT_EQ(OB_OBJ_LOCK_NOT_EXIST, ret);
|
||||
}
|
||||
|
||||
TEST_F(TestLockMemtable, test_lock_retry_lock_conflict)
|
||||
{
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry_lock_conflict");
|
||||
// 1. LOCK
|
||||
// 2. LOCK AGAIN
|
||||
// 3. UNLOCK
|
||||
// 4. LOCK AGAIN
|
||||
// 5. CHECK:
|
||||
// the lock at map;
|
||||
// the lock at lock mem ctx;
|
||||
// the lock at part ctx;
|
||||
// 6. CLEAN
|
||||
|
||||
int ret = OB_SUCCESS;
|
||||
bool is_try_lock = false;
|
||||
int64_t expired_time = ObClockGenerator::getClock() + 1 * 1000 * 1000;
|
||||
ObLockParam param;
|
||||
ObOBJLock *obj_lock = NULL;
|
||||
ObMemtableCtx *mem_ctx = NULL;
|
||||
bool lock_exist = false;
|
||||
share::SCN min_commited_scn;
|
||||
share::SCN flushed_scn;
|
||||
unsigned char lock_mode_in_same_trans = 0x0;
|
||||
|
||||
ObTableLockOp lock_first = DEFAULT_OUT_TRANS_LOCK_OP; // RX, owner 0
|
||||
ObTableLockOp lock_second = DEFAULT_OUT_TRANS_LOCK_OP;
|
||||
lock_second.lock_mode_ = DEFAULT_COFLICT_LOCK_MODE; // X
|
||||
lock_second.owner_id_ = CONFLICT_OWNER_ID; // owner 1
|
||||
|
||||
MyTxCtx default_ctx;
|
||||
ObStoreCtx store_ctx;
|
||||
MyTxCtx ctx2;
|
||||
ObStoreCtx store_ctx2;
|
||||
MyTxCtx ctx3;
|
||||
ObStoreCtx unlock_store_ctx;
|
||||
|
||||
// 1. lock first.
|
||||
start_tx(DEFAULT_TRANS_ID, default_ctx);
|
||||
get_store_ctx(default_ctx, store_ctx);
|
||||
default_ctx.tx_ctx_.change_to_leader();
|
||||
start_tx(TRANS_ID2, ctx2);
|
||||
get_store_ctx(ctx2, store_ctx2);
|
||||
ctx2.tx_ctx_.change_to_leader();
|
||||
start_tx(TRANS_ID3, ctx3);
|
||||
get_store_ctx(ctx3, unlock_store_ctx);
|
||||
ctx3.tx_ctx_.change_to_leader();
|
||||
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry_lock_conflict 1 lock first");
|
||||
param.is_try_lock_ = is_try_lock;
|
||||
param.expired_time_ = expired_time;
|
||||
ret = memtable_.lock(param,
|
||||
store_ctx,
|
||||
lock_first);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
// 2. lock second, must conflict and retry until timeout
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry_lock_conflict 2 lock second");
|
||||
ret = memtable_.lock(param,
|
||||
store_ctx2,
|
||||
lock_second);
|
||||
ASSERT_EQ(OB_ERR_EXCLUSIVE_LOCK_CONFLICT, ret);
|
||||
|
||||
// 3. unlock
|
||||
// 3.1 commit out trans lock
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry_lock_conflict 3.1");
|
||||
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,
|
||||
COMMIT_LOCK_OP_STATUS);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
min_commited_scn = memtable_.obj_lock_map_.get_min_ddl_committed_scn(flushed_scn);
|
||||
ASSERT_EQ(min_commited_scn, commit_scn);
|
||||
memtable_.obj_lock_map_.print();
|
||||
|
||||
// 3.2 unlock complete lock
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry_lock_conflict 3.2");
|
||||
expired_time = ObClockGenerator::getClock() + 1 * 1000 * 1000;
|
||||
ret = memtable_.unlock(unlock_store_ctx,
|
||||
DEFAULT_OUT_TRANS_UNLOCK_OP,
|
||||
is_try_lock,
|
||||
expired_time);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
memtable_.obj_lock_map_.print();
|
||||
// 3.3 unlock commit
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry_lock_conflict 3.3");
|
||||
ret = memtable_.update_lock_status(DEFAULT_OUT_TRANS_UNLOCK_OP,
|
||||
commit_version,
|
||||
commit_scn,
|
||||
COMMIT_LOCK_OP_STATUS);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
memtable_.obj_lock_map_.print();
|
||||
min_commited_scn = memtable_.obj_lock_map_.get_min_ddl_committed_scn(flushed_scn);
|
||||
ASSERT_EQ(min_commited_scn, share::SCN::max_scn());
|
||||
|
||||
// 4. lock again
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry_lock_conflict 4 lock again");
|
||||
param.expired_time_ = ObClockGenerator::getClock() + 1 * 1000 * 1000;
|
||||
ret = memtable_.lock(param,
|
||||
store_ctx2,
|
||||
lock_second);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
// 5. check
|
||||
// 5.1 check lock at map
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry_lock_conflict 5.1 check lock at map");
|
||||
ret = memtable_.obj_lock_map_.get_obj_lock_with_ref_(DEFAULT_TABLE_LOCK_ID,
|
||||
obj_lock);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
ASSERT_EQ(1, obj_lock->size_without_lock());
|
||||
|
||||
// 5.2 check lock at mem ctx
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry_lock_conflict 5.2 check lock at mem ctx");
|
||||
mem_ctx = store_ctx2.mvcc_acc_ctx_.mem_ctx_;
|
||||
ret = mem_ctx->check_lock_exist(lock_second.lock_id_,
|
||||
lock_second.owner_id_,
|
||||
lock_second.lock_mode_,
|
||||
lock_second.op_type_,
|
||||
lock_exist,
|
||||
lock_mode_in_same_trans);
|
||||
ASSERT_EQ(lock_exist, true);
|
||||
|
||||
// 6. clean
|
||||
// 6.1 commit out trans lock
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry_lock_conflict 6.1");
|
||||
commit_version.set_base();
|
||||
commit_scn.set_base();
|
||||
ret = memtable_.update_lock_status(lock_second,
|
||||
commit_version,
|
||||
commit_scn,
|
||||
COMMIT_LOCK_OP_STATUS);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
min_commited_scn = memtable_.obj_lock_map_.get_min_ddl_committed_scn(flushed_scn);
|
||||
ASSERT_EQ(min_commited_scn, commit_scn);
|
||||
memtable_.obj_lock_map_.print();
|
||||
|
||||
// 6.2 unlock complete lock
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry_lock_conflict 6.2");
|
||||
expired_time = ObClockGenerator::getClock() + 1 * 1000 * 1000;
|
||||
ObTableLockOp unlock_op = DEFAULT_OUT_TRANS_UNLOCK_OP;
|
||||
unlock_op.owner_id_ = lock_second.owner_id_;
|
||||
unlock_op.lock_mode_ = lock_second.lock_mode_;
|
||||
ret = memtable_.unlock(unlock_store_ctx,
|
||||
unlock_op,
|
||||
is_try_lock,
|
||||
expired_time);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
memtable_.obj_lock_map_.print();
|
||||
// 6.3 unlock commit
|
||||
LOG_INFO("TestLockMemtable::test_lock_retry_lock_conflict 6.3");
|
||||
ret = memtable_.update_lock_status(unlock_op,
|
||||
commit_version,
|
||||
commit_scn,
|
||||
COMMIT_LOCK_OP_STATUS);
|
||||
ASSERT_EQ(OB_SUCCESS, ret);
|
||||
memtable_.obj_lock_map_.print();
|
||||
min_commited_scn = memtable_.obj_lock_map_.get_min_ddl_committed_scn(flushed_scn);
|
||||
ASSERT_EQ(min_commited_scn, share::SCN::max_scn());
|
||||
}
|
||||
|
||||
} // tablelock
|
||||
} // transaction
|
||||
} // oceanbase
|
||||
|
||||
@ -29,19 +29,6 @@ namespace transaction
|
||||
{
|
||||
namespace tablelock
|
||||
{
|
||||
int ObOBJLock::register_into_deadlock_detector_(const ObStoreCtx &ctx,
|
||||
const ObTableLockOp &lock_op)
|
||||
{
|
||||
UNUSEDx(ctx, lock_op);
|
||||
return OB_SUCCESS;
|
||||
}
|
||||
|
||||
int ObOBJLock::unregister_from_deadlock_detector_(const ObTableLockOp &lock_op)
|
||||
{
|
||||
UNUSED(lock_op);
|
||||
return OB_SUCCESS;
|
||||
}
|
||||
|
||||
class TestObjLock : public MockTxEnv,
|
||||
public ::testing::Test
|
||||
{
|
||||
@ -572,9 +559,9 @@ TEST_F(TestObjLock, lock_conflict_in_in)
|
||||
ASSERT_EQ(OB_TRY_LOCK_ROW_CONFLICT, ret);
|
||||
} else {
|
||||
// deadlock detect will kill the trans
|
||||
// ASSERT_EQ(OB_ERR_EXCLUSIVE_LOCK_CONFLICT, ret);
|
||||
// ASSERT_EQ(OB_TRY_LOCK_ROW_CONFLICT, ret);
|
||||
// ASSERT_EQ(OB_TRANS_KILLED, ret);
|
||||
ASSERT_EQ((ret == OB_TRANS_KILLED || ret == OB_ERR_EXCLUSIVE_LOCK_CONFLICT), true);
|
||||
ASSERT_EQ((ret == OB_TRANS_KILLED || ret == OB_TRY_LOCK_ROW_CONFLICT), true);
|
||||
}
|
||||
|
||||
// 1.3 clean.
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
#include "storage/memtable/ob_memtable_context.h" // ObMemtableCtx
|
||||
#include "storage/tablelock/ob_mem_ctx_table_lock.h"
|
||||
#include "storage/tablelock/ob_table_lock_common.h"
|
||||
#include "storage/tablelock/ob_table_lock_deadlock.h"
|
||||
#include "storage/tablelock/ob_table_lock_rpc_struct.h"
|
||||
#include "storage/tx/ob_trans_define.h"
|
||||
#include "storage/tx/ob_trans_part_ctx.h"
|
||||
@ -135,6 +136,7 @@ int ObLockMemtable::lock_(
|
||||
ObTableLockMode lock_mode_in_same_trans = 0x0;
|
||||
bool lock_exist = false;
|
||||
ObLockStep succ_step = STEP_BEGIN;
|
||||
bool register_to_deadlock = false;
|
||||
ObTxIDSet conflict_tx_set;
|
||||
|
||||
// 1. record lock myself(check conflict).
|
||||
@ -150,8 +152,8 @@ int ObLockMemtable::lock_(
|
||||
conflict_tx_set.reset();
|
||||
ObMvccWriteGuard guard(true);
|
||||
if (ObClockGenerator::getClock() >= param.expired_time_) {
|
||||
ret = OB_TIMEOUT;
|
||||
LOG_WARN("lock timeout", K(ret), K(param));
|
||||
ret = (ret == OB_TRY_LOCK_ROW_CONFLICT ? OB_ERR_EXCLUSIVE_LOCK_CONFLICT : OB_TIMEOUT);
|
||||
LOG_WARN("lock timeout", K(ret), K(lock_op), K(param));
|
||||
} else if (OB_FAIL(guard.write_auth(ctx))) {
|
||||
LOG_WARN("not allow lock table.", K(ret), K(ctx));
|
||||
} else if (FALSE_IT(mem_ctx = static_cast<ObMemtableCtx *>(ctx.mvcc_acc_ctx_.mem_ctx_))) {
|
||||
@ -181,14 +183,41 @@ int ObLockMemtable::lock_(
|
||||
}
|
||||
LOG_WARN("record lock at mem_ctx failed.", K(ret), K(lock_op));
|
||||
}
|
||||
|
||||
if (OB_FAIL(ret) && succ_step == STEP_IN_LOCK_MGR) {
|
||||
obj_lock_map_.remove_lock_record(lock_op);
|
||||
}
|
||||
if (!need_retry &&
|
||||
ret == OB_TRY_LOCK_ROW_CONFLICT) {
|
||||
if (param.is_try_lock_) {
|
||||
} else if (ctx.mvcc_acc_ctx_.tx_ctx_->is_table_lock_killed()) {
|
||||
// trans is killed by deadlock detect or abort because of
|
||||
// something else.
|
||||
ret = OB_TRANS_KILLED;
|
||||
} else if (lock_op.is_dml_lock_op() /* only dml lock will wait at lock wait mgr */) {
|
||||
// wait at lock wait mgr but not retry at here.
|
||||
} else {
|
||||
// register to deadlock detector.
|
||||
need_retry = true;
|
||||
if (!lock_op.is_dml_lock_op() && !register_to_deadlock) {
|
||||
if (OB_TMP_FAIL(register_into_deadlock_detector_(ctx, lock_op))) {
|
||||
LOG_WARN("register to deadlock detector failed", K(ret), K(lock_op));
|
||||
} else {
|
||||
register_to_deadlock = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (need_retry) {
|
||||
ob_usleep(USLEEP_TIME);
|
||||
}
|
||||
} while (need_retry);
|
||||
if (OB_UNLIKELY(register_to_deadlock)) {
|
||||
if (OB_TMP_FAIL(unregister_from_deadlock_detector_(lock_op))) {
|
||||
LOG_WARN("unregister from deadlock detector failed", K(tmp_ret), K(lock_op));
|
||||
}
|
||||
}
|
||||
// return success if lock twice.
|
||||
if (ret == OB_OBJ_LOCK_EXIST) {
|
||||
ret = OB_SUCCESS;
|
||||
@ -287,6 +316,11 @@ int ObLockMemtable::unlock_(
|
||||
if (OB_FAIL(ret) && succ_step == STEP_IN_LOCK_MGR) {
|
||||
obj_lock_map_.remove_lock_record(unlock_op);
|
||||
}
|
||||
if (!need_retry &&
|
||||
is_need_retry_unlock_error(ret) &&
|
||||
!is_try_lock) {
|
||||
need_retry = true;
|
||||
}
|
||||
}
|
||||
if (need_retry) {
|
||||
ob_usleep(USLEEP_TIME);
|
||||
@ -948,6 +982,62 @@ int ObLockMemtable::replay_lock(
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObLockMemtable::register_into_deadlock_detector_(
|
||||
const ObStoreCtx &ctx,
|
||||
const ObTableLockOp &lock_op)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
int tmp_ret = OB_SUCCESS;
|
||||
ObTransLockPartID tx_lock_part_id;
|
||||
ObAddr parent_addr;
|
||||
const ObLSID &ls_id = ctx.ls_id_;
|
||||
const int64_t priority = ~(ctx.mvcc_acc_ctx_.tx_desc_->get_active_ts());
|
||||
tx_lock_part_id.lock_id_ = lock_op.lock_id_;
|
||||
tx_lock_part_id.trans_id_ = lock_op.create_trans_id_;
|
||||
if (OB_FAIL(ObTableLockDeadlockDetectorHelper::register_trans_lock_part(
|
||||
tx_lock_part_id, ls_id, priority))) {
|
||||
LOG_WARN("register trans lock part failed", K(ret), K(tx_lock_part_id),
|
||||
K(ls_id));
|
||||
} else if (OB_FAIL(ObTransDeadlockDetectorAdapter::get_trans_scheduler_info_on_participant(
|
||||
tx_lock_part_id.trans_id_, ls_id, parent_addr))) {
|
||||
LOG_WARN("get scheduler address failed", K(tx_lock_part_id), K(ls_id));
|
||||
} else if (OB_FAIL(ObTableLockDeadlockDetectorHelper::add_parent(
|
||||
tx_lock_part_id, parent_addr, lock_op.create_trans_id_))) {
|
||||
LOG_WARN("add parent failed", K(ret), K(tx_lock_part_id));
|
||||
} else if (OB_FAIL(ObTableLockDeadlockDetectorHelper::block(tx_lock_part_id,
|
||||
ls_id,
|
||||
lock_op))) {
|
||||
LOG_WARN("add dependency failed", K(ret), K(tx_lock_part_id));
|
||||
} else {
|
||||
LOG_DEBUG("succeed register to the dead lock detector");
|
||||
}
|
||||
if (OB_FAIL(ret)) {
|
||||
if (OB_SUCCESS != (tmp_ret = ObTableLockDeadlockDetectorHelper::
|
||||
unregister_trans_lock_part(tx_lock_part_id))) {
|
||||
if (tmp_ret != OB_ENTRY_NOT_EXIST) {
|
||||
LOG_WARN("unregister from deadlock detector failed", K(ret),
|
||||
K(tx_lock_part_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObLockMemtable::unregister_from_deadlock_detector_(const ObTableLockOp &lock_op)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObTransLockPartID tx_lock_part_id;
|
||||
tx_lock_part_id.lock_id_ = lock_op.lock_id_;
|
||||
tx_lock_part_id.trans_id_ = lock_op.create_trans_id_;
|
||||
if (OB_FAIL(ObTableLockDeadlockDetectorHelper::unregister_trans_lock_part(
|
||||
tx_lock_part_id))) {
|
||||
LOG_WARN("unregister trans lock part failed", K(ret), K(tx_lock_part_id));
|
||||
} else {
|
||||
// do nothing
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // tablelock
|
||||
} // transaction
|
||||
} // oceanbase
|
||||
|
||||
@ -195,6 +195,9 @@ private:
|
||||
const ObTableLockMode &lock_mode,
|
||||
const ObTransID &conflict_tx_id,
|
||||
ObFunction<int(bool &need_wait)> &recheck_f);
|
||||
int register_into_deadlock_detector_(const ObStoreCtx &ctx,
|
||||
const ObTableLockOp &lock_op);
|
||||
int unregister_from_deadlock_detector_(const ObTableLockOp &lock_op);
|
||||
private:
|
||||
typedef common::SpinRWLock RWLock;
|
||||
typedef common::SpinRLockGuard RLockGuard;
|
||||
|
||||
@ -22,7 +22,6 @@
|
||||
#include "storage/tablelock/ob_obj_lock.h"
|
||||
#include "storage/tablelock/ob_table_lock_common.h"
|
||||
#include "storage/tablelock/ob_mem_ctx_table_lock.h"
|
||||
#include "storage/tablelock/ob_table_lock_deadlock.h"
|
||||
#include "storage/tablelock/ob_table_lock_iterator.h"
|
||||
#include "storage/tablelock/ob_table_lock_rpc_struct.h"
|
||||
|
||||
@ -170,7 +169,6 @@ int ObOBJLock::slow_lock(
|
||||
const ObLockParam ¶m,
|
||||
const ObTableLockOp &lock_op,
|
||||
const ObTableLockMode &lock_mode_in_same_trans,
|
||||
bool &need_retry,
|
||||
ObMalloc &allocator,
|
||||
ObTxIDSet &conflict_tx_set)
|
||||
{
|
||||
@ -187,7 +185,6 @@ int ObOBJLock::slow_lock(
|
||||
WRLockGuard guard(rwlock_);
|
||||
if (is_deleted_) {
|
||||
ret = OB_EAGAIN;
|
||||
need_retry = false;
|
||||
} else if (OB_FAIL(check_allow_lock_(lock_op,
|
||||
lock_mode_in_same_trans,
|
||||
conflict_tx_set,
|
||||
@ -222,17 +219,14 @@ int ObOBJLock::slow_lock(
|
||||
op_list, allocator);
|
||||
}
|
||||
// 1. need retry basic conditions
|
||||
if (ret == OB_TRY_LOCK_ROW_CONFLICT && !param.is_try_lock_) {
|
||||
need_retry = true;
|
||||
}
|
||||
// lock conflict and it is not try lock.
|
||||
// 2. need retry second conditions
|
||||
// out trans lock or in trans lock table lock should not retry if
|
||||
// it is conflict with dml lock.
|
||||
if (need_retry) {
|
||||
if (ret == OB_TRY_LOCK_ROW_CONFLICT && !param.is_try_lock_) {
|
||||
if (!lock_op.is_dml_lock_op() &&
|
||||
conflict_with_dml_lock &&
|
||||
param.is_deadlock_avoid_enabled_) {
|
||||
need_retry = false;
|
||||
ret = OB_TRANS_KILLED;
|
||||
}
|
||||
}
|
||||
@ -414,14 +408,12 @@ int ObOBJLock::update_lock_status(const ObTableLockOp &lock_op,
|
||||
int ObOBJLock::try_fast_lock_(
|
||||
const ObTableLockOp &lock_op,
|
||||
const ObTableLockMode &lock_mode_in_same_trans,
|
||||
bool &need_retry,
|
||||
ObTxIDSet &conflict_tx_set)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
bool unused_conflict_with_dml_lock = false;
|
||||
if (is_deleted_) {
|
||||
ret = OB_EAGAIN;
|
||||
need_retry = false;
|
||||
} else if (OB_UNLIKELY(lock_op.need_record_lock_op())) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("this lock op should not do fast lock", KR(ret), K(lock_op));
|
||||
@ -447,7 +439,6 @@ int ObOBJLock::fast_lock(
|
||||
const ObLockParam ¶m,
|
||||
const ObTableLockOp &lock_op,
|
||||
const ObTableLockMode &lock_mode_in_same_trans,
|
||||
bool &need_retry,
|
||||
ObMalloc &allocator,
|
||||
ObTxIDSet &conflict_tx_set)
|
||||
{
|
||||
@ -458,7 +449,6 @@ int ObOBJLock::fast_lock(
|
||||
RDLockGuard guard(rwlock_);
|
||||
if (OB_FAIL(try_fast_lock_(lock_op,
|
||||
lock_mode_in_same_trans,
|
||||
need_retry,
|
||||
conflict_tx_set))) {
|
||||
if (OB_TRY_LOCK_ROW_CONFLICT != ret && OB_EAGAIN != ret) {
|
||||
LOG_WARN("try fast lock failed", KR(ret), K(lock_op));
|
||||
@ -467,10 +457,6 @@ int ObOBJLock::fast_lock(
|
||||
LOG_DEBUG("succeed create lock ", K(lock_op));
|
||||
}
|
||||
}
|
||||
// 1. need retry basic conditions
|
||||
if (ret == OB_TRY_LOCK_ROW_CONFLICT && !param.is_try_lock_) {
|
||||
need_retry = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -485,7 +471,6 @@ int ObOBJLock::lock(
|
||||
int ret = OB_SUCCESS;
|
||||
int tmp_ret = OB_SUCCESS;
|
||||
int64_t USLEEP_TIME = 100; // 0.1 ms
|
||||
bool register_to_deadlock = false;
|
||||
// 1. lock myself.
|
||||
// 2. try to lock.
|
||||
LOG_DEBUG("ObOBJLock::lock ", K(param), K(lock_op));
|
||||
@ -493,14 +478,10 @@ int ObOBJLock::lock(
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
LOG_WARN("invalid argument.", K(ret), K(lock_op));
|
||||
} else {
|
||||
bool need_retry = false;
|
||||
do {
|
||||
need_retry = false;
|
||||
if (OB_LIKELY(!lock_op.need_record_lock_op())) {
|
||||
if (OB_FAIL(fast_lock(param,
|
||||
lock_op,
|
||||
lock_mode_in_same_trans,
|
||||
need_retry,
|
||||
allocator,
|
||||
conflict_tx_set))) {
|
||||
if (ret != OB_TRY_LOCK_ROW_CONFLICT &&
|
||||
@ -511,7 +492,6 @@ int ObOBJLock::lock(
|
||||
} else if (OB_FAIL(slow_lock(param,
|
||||
lock_op,
|
||||
lock_mode_in_same_trans,
|
||||
need_retry,
|
||||
allocator,
|
||||
conflict_tx_set))) {
|
||||
if (ret != OB_TRY_LOCK_ROW_CONFLICT &&
|
||||
@ -520,47 +500,10 @@ int ObOBJLock::lock(
|
||||
}
|
||||
}
|
||||
|
||||
if (need_retry &&
|
||||
ret == OB_TRY_LOCK_ROW_CONFLICT) {
|
||||
need_retry = false;
|
||||
if (param.is_try_lock_) {
|
||||
} else if (OB_UNLIKELY(ObClockGenerator::getClock() >= param.expired_time_)) {
|
||||
ret = OB_ERR_EXCLUSIVE_LOCK_CONFLICT; // TODO: specialize error code with different lock mode
|
||||
LOG_WARN("lock is timeout", K(ret), K(lock_op), K(param));
|
||||
} else if (ctx.mvcc_acc_ctx_.tx_ctx_->is_table_lock_killed()) {
|
||||
// trans is killed by deadlock detect or abort because of
|
||||
// something else.
|
||||
need_retry = false;
|
||||
ret = OB_TRANS_KILLED;
|
||||
} else if (lock_op.is_dml_lock_op() /* only dml lock will wait at lock wait mgr */) {
|
||||
// wait at lock wait mgr but not retry at here.
|
||||
need_retry = false;
|
||||
} else {
|
||||
// register to deadlock detector.
|
||||
need_retry = true;
|
||||
if (!lock_op.is_dml_lock_op() &&
|
||||
!register_to_deadlock) {
|
||||
if (OB_FAIL(register_into_deadlock_detector_(ctx,
|
||||
lock_op))) {
|
||||
LOG_WARN("register to deadlock detector failed", K(ret),
|
||||
K(lock_op));
|
||||
} else {
|
||||
register_to_deadlock = true;
|
||||
}
|
||||
}
|
||||
ob_usleep(USLEEP_TIME);
|
||||
}
|
||||
}
|
||||
if (OB_FAIL(ret) && REACH_TIME_INTERVAL(1 * 1000 * 1000)) {
|
||||
LOG_WARN("ObOBJLock::lock ", K(ret), K(param), K(lock_op));
|
||||
print();
|
||||
}
|
||||
} while (need_retry);
|
||||
if (OB_UNLIKELY(register_to_deadlock)) {
|
||||
if (OB_SUCCESS != (tmp_ret = unregister_from_deadlock_detector_(lock_op))) {
|
||||
LOG_WARN("unregister from deadlock detector failed", K(tmp_ret), K(lock_op));
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("ObOBJLock::lock finish", K(ret), K(conflict_tx_set));
|
||||
return ret;
|
||||
@ -585,9 +528,6 @@ int ObOBJLock::unlock(
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_ERROR("should only slow lock op", K(ret), K(unlock_op));
|
||||
} else {
|
||||
bool need_retry = false;
|
||||
do {
|
||||
need_retry = false;
|
||||
{
|
||||
WRLockGuard guard(rwlock_);
|
||||
if (!is_try_lock && OB_UNLIKELY(ObClockGenerator::getClock() >= expired_time)) {
|
||||
@ -596,24 +536,17 @@ int ObOBJLock::unlock(
|
||||
} else if (is_deleted_) {
|
||||
// need retry from upper layer.
|
||||
ret = OB_EAGAIN;
|
||||
need_retry = false;
|
||||
} else if (OB_FAIL(unlock_(unlock_op, allocator))) {
|
||||
if (is_need_retry_unlock_error(ret) && !is_try_lock) {
|
||||
need_retry = true;
|
||||
} else {
|
||||
if (!is_need_retry_unlock_error(ret)) {
|
||||
LOG_WARN("unlock failed.", K(ret), K(unlock_op));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (need_retry) {
|
||||
ob_usleep(USLEEP_TIME);
|
||||
}
|
||||
if (OB_FAIL(ret) && REACH_TIME_INTERVAL(1 * 1000 * 1000)) {
|
||||
LOG_WARN("ObOBJLock::unlock ", K(ret), K(is_try_lock),
|
||||
K(expired_time), K(unlock_op));
|
||||
print();
|
||||
}
|
||||
} while (need_retry);
|
||||
}
|
||||
LOG_DEBUG("ObOBJLock::unlock finish.", K(ret));
|
||||
|
||||
@ -1587,61 +1520,6 @@ int ObOBJLock::compact_tablelock_(ObMalloc &allocator,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObOBJLock::register_into_deadlock_detector_(const ObStoreCtx &ctx,
|
||||
const ObTableLockOp &lock_op)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
int tmp_ret = OB_SUCCESS;
|
||||
ObTransLockPartID tx_lock_part_id;
|
||||
ObAddr parent_addr;
|
||||
const ObLSID &ls_id = ctx.ls_id_;
|
||||
const int64_t priority = ~(ctx.mvcc_acc_ctx_.tx_desc_->get_active_ts());
|
||||
tx_lock_part_id.lock_id_ = lock_op.lock_id_;
|
||||
tx_lock_part_id.trans_id_ = lock_op.create_trans_id_;
|
||||
if (OB_FAIL(ObTableLockDeadlockDetectorHelper::register_trans_lock_part(
|
||||
tx_lock_part_id, ls_id, priority))) {
|
||||
LOG_WARN("register trans lock part failed", K(ret), K(tx_lock_part_id),
|
||||
K(ls_id));
|
||||
} else if (OB_FAIL(ObTransDeadlockDetectorAdapter::get_trans_scheduler_info_on_participant(
|
||||
tx_lock_part_id.trans_id_, ls_id, parent_addr))) {
|
||||
LOG_WARN("get scheduler address failed", K(tx_lock_part_id), K(ls_id));
|
||||
} else if (OB_FAIL(ObTableLockDeadlockDetectorHelper::add_parent(
|
||||
tx_lock_part_id, parent_addr, lock_op.create_trans_id_))) {
|
||||
LOG_WARN("add parent failed", K(ret), K(tx_lock_part_id));
|
||||
} else if (OB_FAIL(ObTableLockDeadlockDetectorHelper::block(tx_lock_part_id,
|
||||
ls_id,
|
||||
lock_op))) {
|
||||
LOG_WARN("add dependency failed", K(ret), K(tx_lock_part_id));
|
||||
} else {
|
||||
LOG_DEBUG("succeed register to the dead lock detector");
|
||||
}
|
||||
if (OB_FAIL(ret)) {
|
||||
if (OB_SUCCESS != (tmp_ret = ObTableLockDeadlockDetectorHelper::
|
||||
unregister_trans_lock_part(tx_lock_part_id))) {
|
||||
if (tmp_ret != OB_ENTRY_NOT_EXIST) {
|
||||
LOG_WARN("unregister from deadlock detector failed", K(ret),
|
||||
K(tx_lock_part_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObOBJLock::unregister_from_deadlock_detector_(const ObTableLockOp &lock_op)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObTransLockPartID tx_lock_part_id;
|
||||
tx_lock_part_id.lock_id_ = lock_op.lock_id_;
|
||||
tx_lock_part_id.trans_id_ = lock_op.create_trans_id_;
|
||||
if (OB_FAIL(ObTableLockDeadlockDetectorHelper::unregister_trans_lock_part(
|
||||
tx_lock_part_id))) {
|
||||
LOG_WARN("unregister trans lock part failed", K(ret), K(tx_lock_part_id));
|
||||
} else {
|
||||
// do nothing
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObOBJLock::get_or_create_op_list(const ObTableLockMode mode,
|
||||
const uint64_t tenant_id,
|
||||
ObMalloc &allocator,
|
||||
|
||||
@ -162,20 +162,17 @@ private:
|
||||
const ObLockParam ¶m,
|
||||
const ObTableLockOp &lock_op,
|
||||
const ObTableLockMode &lock_mode_in_same_trans,
|
||||
bool &need_retry,
|
||||
ObMalloc &allocator,
|
||||
ObTxIDSet &conflict_tx_set);
|
||||
int slow_lock(
|
||||
const ObLockParam ¶m,
|
||||
const ObTableLockOp &lock_op,
|
||||
const ObTableLockMode &lock_mode_in_same_trans,
|
||||
bool &need_retry,
|
||||
ObMalloc &allocator,
|
||||
ObTxIDSet &conflict_tx_set);
|
||||
int try_fast_lock_(
|
||||
const ObTableLockOp &lock_op,
|
||||
const ObTableLockMode &lock_mode_in_same_trans,
|
||||
bool &need_retry,
|
||||
ObTxIDSet &conflict_tx_set);
|
||||
int unlock_(
|
||||
const ObTableLockOp &unlock_op,
|
||||
@ -238,10 +235,6 @@ private:
|
||||
ObMalloc &allocator,
|
||||
bool &is_compact,
|
||||
const bool is_force = false);
|
||||
int register_into_deadlock_detector_(
|
||||
const storage::ObStoreCtx &ctx,
|
||||
const ObTableLockOp &lock_op);
|
||||
int unregister_from_deadlock_detector_(const ObTableLockOp &lock_op);
|
||||
private:
|
||||
int get_index_by_lock_mode(ObTableLockMode mode);
|
||||
int check_op_allow_lock_from_list_(
|
||||
|
||||
Reference in New Issue
Block a user