Skip to content

Unexpected lock behavior for point get FOR UPDATE read on unique key #66571

Closed

Description

@pcqz7

Bug Report

Please answer these questions before submitting your issue. Thanks!

1. Minimal reproduce step (Required)

create table t(a int, b int, primary key(a) nonclustered);
insert into t values(1,1);
set global tidb_txn_mode='pessimistic';
set global innodb_lock_wait_timeout=50;
set global max_execution_time=0;
set global tidb_pessimistic_txn_fair_locking=1;

Session1

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t where a=1 for update;
+---+------+
| a | b    |
+---+------+
| 1 |    1 |
+---+------+
1 row in set (0.01 sec)

Session2

mysql> begin;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from t where a=1 for update;
+---+------+
| a | b    |
+---+------+
| 1 |    1 |
+---+------+
1 row in set (12.04 sec)

2. What did you expect to see? (Required)

The for update query on Session2 waits for the lock and reports Lock wait timeout exceeded at 50s.

3. What did you see instead (Required)

The query acquired the lock.

4. What is your TiDB version? (Required)

v8.5.4

Activity

  1. jebter commented on Mar 2, 2026

    @jebter
  2. cfzjywxk commented on Mar 2, 2026

    @cfzjywxk
    Contributor

    Root cause

    The cause is the ttl manger is unexpectedly reset in this case.

    For PointGetExec with unique index lock operation:

    From the re-producing logs:

    [2026/03/02 12:19:47.613 +08:00] [INFO] [point_get.go:617] ["point get pessimistic lock"] [conn=3034578952] [txnStartTS=464630627180740609] [lockOnlyIfExists=false] [success=true]
    [2026/03/02 12:19:47.613 +08:00] [INFO] [txn.go:1682] ["reset ttlManager in fair locking mode"] [txnStartTS=464630627180740609] [lastPrimaryKeyLen=0] [newPrimaryKeyLen=28]
    
    

    Workaround

    Disable the tidb_pessimistic_txn_fair_locking.

    Fix

    For quick fix
    Guard against lastPrimaryKey == nil:

      if hasNewLockToAcquire &&
          txn.aggressiveLockingContext.lastPrimaryKey != nil &&
          !bytes.Equal(txn.aggressiveLockingContext.primaryKey,
      txn.aggressiveLockingContext.lastPrimaryKey) {
          txn.committer.reset()
      }
    

    This ensures the heartbeat is only reset when there's an actual primary key change between
    retries, not on the first execution where lastPrimaryKey hasn't been set yet.

    We also need to consider the retry safety for this, reset only when primary is newly selected in this LockKeys call (for example , pass assignedPrimaryKey local bool into resetTTLManagerForAggressiveLockingMode). Otherwise, if primary changed on retry we can still reset repeatedly on later LockKeys calls in the same attempt.

  3. cfzjywxk commented on Mar 2, 2026

    @cfzjywxk
    Contributor

    @MyonKeminta Please check and label the affected versions

  4. MyonKeminta commented on Mar 2, 2026

    @MyonKeminta
    Contributor

    Simply excluding lastPrimaryKey == nil doesn't fix the problem as the case happens after a retry.

  5. MyonKeminta commented on Mar 2, 2026

    @MyonKeminta
    Contributor

    The problem is introduced by tikv/client-go#1522 and is only included in master and release-8.5 .

  6. added 2 commits that reference this issue on Mar 23, 2026
    8c83d3f
    40fcb84
  7. kennedy8312 commented on Sep 22, 2026

    @kennedy8312

    /report customer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions