add locks functions#25038
Conversation
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
XuPeng-SH
left a comment
There was a problem hiding this comment.
I re-checked the latest head. The earlier txn-id compatibility and function-id stability concerns look addressed now, but I still see one real correctness issue in the new holder-lookup path.\n\npkg/lockservice/lock_table_remote.go:245-257 refreshes the bind on error via handleError, but if that refresh succeeds (handleError returns nil) it immediately returns (false, nil) instead of retrying doGetLockHolder on the new bind. That means a stale remote binding is surfaced to SQL as "no holder" / NULL even though the lock may still exist on the new CN. With IS_USED_LOCK() now relying on GetLockHolder, this becomes a user-visible false negative during bind migration / CN movement, not just an internal observability gap.\n\nPlease retry the holder lookup after a successful bind refresh, the same way the caller needs the post-refresh result rather than a silent "not found", and add a targeted test that exercises GetLockHolder across a bind-change path.
… into 0617-add-locks
已经修改 |
XuPeng-SH
left a comment
There was a problem hiding this comment.
I re-checked the new head. The previous stale-bind false-NULL issue is not fully fixed yet.\n\npkg/lockservice/lock_table_remote.go:245-257 now does continue after handleError() refreshes the bind, but that loop is still running on the same remoteLockTable instance. doGetLockHolder() always sends req.LockTable = l.bind, and on bind change service.handleBindChanged() replaces the tableGroups entry with a newly created lock-table object (it does not mutate this in-flight l). So after a bind refresh you can still keep retrying on the stale remote table pointer instead of actually querying through the new bind / new local-vs-remote table object.\n\nThe new test does not pin the real production behavior here because its fake bind-changed callback never swaps the lock-table object, and the mock GetLockHolder handler returns success on the second call regardless of which bind was used. Please make the retry reacquire the lock table after bind change (or bubble ErrLockTableBindChanged back so the caller reacquires and retries), and add a test that proves the retry goes through the refreshed bind/object rather than the stale one.
XuPeng-SH
left a comment
There was a problem hiding this comment.
I re-checked the latest head and still see two correctness issues.
-
IS_USED_LOCK()can still ignore statement cancellation when the holder CN is unreachable.
service.GetLockHolder()now re-checksctx.Err()while reacquiring afterErrLockTableBindChanged, butremoteLockTable.getLockHolder()itself still loops forever on transient RPC failures with backoff and no context parameter. In the unhappy path where the remote RPC keeps failing but the allocator keeps returning the same bind,IS_USED_LOCK()can still hang until the process/session is interrupted instead of respecting timeout/cancel.Suggestion: thread the caller context through
getLockHolder/doGetLockHolder, and break the retry loop as soon asctx.Done()fires. -
Remote
GetLockHolderstill does not carry enough data for a cold lookup.
The RPC only sendsGetLockHolder.Row, but the server-side fallback path ingetLocalLockTable()recreates a missing table fromreq.Lock.Rowsandreq.Lock.Options.Sharding. Those fields are unset forMethod_GetLockHolder, so a cold remote lookup can still fail withErrLockTableNotFound/ stale-bind behavior even when a holder exists.Suggestion: either make
getLocalLockTable()method-aware forGetLockHolder(usereq.GetLockHolder.Rowandreq.LockTable.Sharding), or extend theGetLockHolderrequest payload so the handler has the same lookup inputs asLock/GetTxnLock.
Non-blocking coverage note: I still do not see a direct transient-retry test for remoteLockTable.getLockHolder() where RPC fails once, bind does not change, and the retry later succeeds.
已经修改 |
XuPeng-SH
left a comment
There was a problem hiding this comment.
I re-checked the latest head. The previous remote-holder blockers are fixed, but I still see one remaining local stale-bind race in the holder-lookup path.
service.GetLockHolder() and handleRemoteGetLockHolder() still do:
- get/recreate a lock table,
- then call
l.getLockHolder(...)
without holdingbindChangeMuacross those two steps.
If a bind change closes/replaces that local table in between, localLockTable.getLockHolder() returns (false, nil) for mu.closed, which is indistinguishable from “lock does not exist”. The caller therefore does not retry against the new authoritative table and can transiently report NULL / no owner even though the bind has moved and the new table may still have a holder.
You can see the pieces in the current head:
service.handleBindChanged()swaps the table underbindChangeMuand closes the old onelocalLockTable.getLockHolder()still returnspb.WaitTxn{}, false, nilwhenl.mu.closed- unlike
Lock, the holder-lookup path does not holdbindChangeMu.RLock()across table acquisition + lookup
Concrete suggestions
- Mirror the
Lockpath and holdbindChangeMu.RLock()acrossgetLockTableWithCreate/getLocalLockTableandgetLockHolder. - Or, if you prefer the retry pattern already used elsewhere, make
localLockTable.getLockHolder()surfaceErrLockTableBindChangedwhen it is closed by rebinding so the caller can reacquire and retry on the current table. - Add one focused test for the local race (not the remote bind-changed case already covered): acquire a local table, rebind/close it before
getLockHolder, and assert the lookup retries on the new table instead of returning(false, nil).
已经按照建议修改了 |
XuPeng-SH
left a comment
There was a problem hiding this comment.
Re-checked the latest head. The remaining local stale-bind holder-lookup race is now fixed: GetLockHolder() holds bindChangeMu.RLock() across table acquisition and getLockHolder(...), so a rebinding-close can no longer be misreported as a clean "no holder" result. Together with the earlier remote ctx/sharding/bind-refresh fixes and the new lockservice tests, I do not see a remaining correctness issue that should keep this blocked.
|
Queued — the merge queue status continues in this comment ↓. |
Merge Queue Status
This pull request spent 22 seconds in the queue, including 2 seconds running CI. Required conditions to merge
|
What type of PR is this?
Which issue(s) this PR fixes:
issue #24486
What this PR does / why we need it:
增加user level locks函数: is_used_lock, release_all_locks.
• IS_USED_LOCK:
RELEASE_ALL_LOCKS: