Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PessimisticLock: Lock the specified key only if the key exists #561

Merged
merged 29 commits into from
Sep 6, 2022

Conversation

TonsnakeLin
Copy link
Contributor

@TonsnakeLin TonsnakeLin commented Aug 3, 2022

1. current process
Currently, TiKV locks the specified keys for pessimistic lock requests whether it exists or not. TiDB sends a get request to judge whether the keys exists on the TiKV firstly, then sends a pessimistic lock request to TiKV if the key exists, It makes and additional rpc request.

2. what changed
We add a flag LockIfExists for pessimistic lock request and TiKV will check the flag to decide how to execute the lock process.

This PR is depended on the PRs bellow.

@TonsnakeLin TonsnakeLin marked this pull request as draft August 3, 2022 02:49
@TonsnakeLin TonsnakeLin marked this pull request as ready for review August 17, 2022 07:20
@disksing disksing requested review from sticnarf, MyonKeminta and you06 and removed request for sticnarf and MyonKeminta August 17, 2022 08:20
@TonsnakeLin TonsnakeLin force-pushed the optimize_lock_if_exists branch from 1dc10e3 to 6ccabb7 Compare August 18, 2022 13:07
Copy link
Contributor

@MyonKeminta MyonKeminta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe there's better to be some unit tests, which requires the lock_if_exist logic to be implemented in mocktikv

txnkv/transaction/txn.go Outdated Show resolved Hide resolved
TonsnakeLin and others added 15 commits August 31, 2022 21:17
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Co-authored-by: MyonKeminta <9948422+MyonKeminta@users.noreply.github.com>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Signed-off-by: dongxu <i@huangdx.net>

Signed-off-by: dongxu <i@huangdx.net>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Signed-off-by: dongxu <i@huangdx.net>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Signed-off-by: cfzjywxk <lsswxrxr@163.com>

Signed-off-by: cfzjywxk <lsswxrxr@163.com>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
* replace kvproto

Signed-off-by: ekexium <eke@fastmail.com>

* support NeedConflictCheck

Signed-off-by: ekexium <eke@fastmail.com>

* fix mutation encoding

Signed-off-by: ekexium <eke@fastmail.com>

* support temporary flag

Signed-off-by: ekexium <eke@fastmail.com>

* update kvproto

Signed-off-by: ekexium <eke@fastmail.com>

* fix style

Signed-off-by: ekexium <eke@fastmail.com>

* add an option to enable the behavior

Signed-off-by: ekexium <eke@fastmail.com>

* replace AfterCheckPoint with existing canModity

Signed-off-by: ekexium <eke@fastmail.com>

* UpdateFlag do not unset temporary flag

Signed-off-by: ekexium <eke@fastmail.com>

* remove unused function

Signed-off-by: ekexium <eke@fastmail.com>

* update tidb dependency

Signed-off-by: ekexium <eke@fastmail.com>

update tidb dependency

Signed-off-by: ekexium <eke@fastmail.com>

* fix test

Signed-off-by: ekexium <eke@fastmail.com>

* do no unset flag on read

Signed-off-by: ekexium <eke@fastmail.com>

* update tidb dependency

Signed-off-by: ekexium <eke@fastmail.com>

* update comment

Signed-off-by: ekexium <eke@fastmail.com>

Signed-off-by: ekexium <eke@fastmail.com>
@TonsnakeLin TonsnakeLin force-pushed the optimize_lock_if_exists branch from 881615b to f3dc9f9 Compare August 31, 2022 13:22
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Copy link
Collaborator

@sticnarf sticnarf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@sticnarf
Copy link
Collaborator

sticnarf commented Sep 1, 2022

I'm considering this case, if a transaction called lock-if-exist requests in its first statement and all the given keys do not exist, but the lock keys requests are successful, then we set the primary key to an unlocked key, I'm afraid there will be some problems when this happened. See the following timeline:

begin
lock([k1, k2, k3]), TiKV resp with []
set PK as k1

Besides this case, if k1 doesn't exist, but we succeed locking k2 and k3, we'll face the same problem.

If we don't want to introduce an anchor key, we can disable the feature if the primary key is not selected yet.

Signed-off-by: TonsnakeLin <lpbgytong@163.com>
@TonsnakeLin
Copy link
Contributor Author

I'm considering this case, if a transaction called lock-if-exist requests in its first statement and all the given keys do not exist, but the lock keys requests are successful, then we set the primary key to an unlocked key, I'm afraid there will be some problems when this happened. See the following timeline:

begin
lock([k1, k2, k3]), TiKV resp with []
set PK as k1

LockOnlyIfExists request is used only for RC isolation on TiDB, I think the scene above is reasonable,right ?
Of course, I don't limit it in client-go.

@TonsnakeLin
Copy link
Contributor Author

I'm considering this case, if a transaction called lock-if-exist requests in its first statement and all the given keys do not exist, but the lock keys requests are successful, then we set the primary key to an unlocked key, I'm afraid there will be some problems when this happened. See the following timeline:

begin
lock([k1, k2, k3]), TiKV resp with []
set PK as k1

Besides this case, if k1 doesn't exist, but we succeed locking k2 and k3, we'll face the same problem.

If we don't want to introduce an anchor key, we can disable the feature if the primary key is not selected yet.

@TonsnakeLin TonsnakeLin closed this Sep 2, 2022
@TonsnakeLin TonsnakeLin reopened this Sep 2, 2022
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
@TonsnakeLin TonsnakeLin requested review from MyonKeminta and removed request for you06 September 2, 2022 07:35
@TonsnakeLin
Copy link
Contributor Author

I'm considering this case, if a transaction called lock-if-exist requests in its first statement and all the given keys do not exist, but the lock keys requests are successful, then we set the primary key to an unlocked key, I'm afraid there will be some problems when this happened. See the following timeline:

begin
lock([k1, k2, k3]), TiKV resp with []
set PK as k1

Besides this case, if k1 doesn't exist, but we succeed locking k2 and k3, we'll face the same problem.

If we don't want to introduce an anchor key, we can disable the feature if the primary key is not selected yet.

If lock failed with LockIfExists flag, I will unset the primary key and reset ttlManager

@TonsnakeLin
Copy link
Contributor Author

@sticnarf @MyonKeminta PTAL, thank you!!

@MyonKeminta
Copy link
Contributor

If lock failed with LockIfExists flag, I will unset the primary key and reset ttlManager

It seems you meant when the pessimistic lock request succeeded but the primary lock is not acquired due to not exist, instead of failing to execute the pessimistic lock request.

Though you unset the primary and cancel ttlManager in the client side (which I think you can actually avoid starting the ttlManager instead of start it and then stop it), there still exists another problem.

Consider the case that a lock request to [k1, k2, k3] where k1 is the primary, and k2, k3 is locked but k1 is untouched due to not exist. In this case, k2 and k3 will still be locked with their primary pointing to k1. This causes other transactions cannot determine the transaction's state correctly through the lock on k2 and k3, which I'm afraid may potentially lead to other correctness issues.

cc @sticnarf @you06 @cfzjywxk

@TonsnakeLin
Copy link
Contributor Author

If lock failed with LockIfExists flag, I will unset the primary key and reset ttlManager

It seems you meant when the pessimistic lock request succeeded but the primary lock is not acquired due to not exist, instead of failing to execute the pessimistic lock request.

Though you unset the primary and cancel ttlManager in the client side (which I think you can actually avoid starting the ttlManager instead of start it and then stop it), there still exists another problem.

Consider the case that a lock request to [k1, k2, k3] where k1 is the primary, and k2, k3 is locked but k1 is untouched due to not exist. In this case, k2 and k3 will still be locked with their primary pointing to k1. This causes other transactions cannot determine the transaction's state correctly through the lock on k2 and k3, which I'm afraid may potentially lead to other correctness issues.

cc @sticnarf @you06 @cfzjywxk

Thanks! As @sticnarf said, I will make the LockIfExists effective only when primary key is selected

@sticnarf
Copy link
Collaborator

sticnarf commented Sep 2, 2022

Consider the case that a lock request to [k1, k2, k3] where k1 is the primary, and k2, k3 is locked but k1 is untouched due to not exist. In this case, k2 and k3 will still be locked with their primary pointing to k1. This causes other transactions cannot determine the transaction's state correctly through the lock on k2 and k3, which I'm afraid may potentially lead to other correctness issues.

If the use case is limited to single key point get, the current solution may suffice, because no other key may refer to a non-existing primary lock.

So, another solution is to check whether only one key is passed to LockKeys.

Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Copy link
Contributor

@MyonKeminta MyonKeminta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rest LGTM

txnkv/transaction/txn.go Outdated Show resolved Hide resolved
txnkv/transaction/txn.go Outdated Show resolved Hide resolved
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
return errors.New("LockOnlyIfExists is set for Lock Context, but ReturnValues is not set")
return &tikverr.ErrLockOnlyIfExistsNoReturnValue{
ForUpdateTs: lockCtx.ForUpdateTS,
LockKey: keysInput[0],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to check len(keysInput) > 0 first?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if len(keysInput) == 0, it prints "{}".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if len(keysInput) == 0, would keysInput[0] cause index out of range?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

error/error.go Outdated Show resolved Hide resolved
error/error.go Outdated Show resolved Hide resolved
error/error.go Show resolved Hide resolved
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
Signed-off-by: TonsnakeLin <lpbgytong@163.com>
@cfzjywxk cfzjywxk merged commit fed87c9 into tikv:master Sep 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants