# [Known Issue] FOR UPDATE lock does not block: other session acquires the lock while the first still holds it ([tidb#66571](https://github.com/pingcap/tidb/issues/66571))

Source note: adapted from GitHub issue [tidb#66571](https://github.com/pingcap/tidb/issues/66571) and related fix [client-go#1903](https://github.com/tikv/client-go/pull/1903).

## Issue

- Link: [tidb#66571](https://github.com/pingcap/tidb/issues/66571) — Unexpected lock behavior for point get `FOR UPDATE` on a unique key
- Related: [tidb#66581](https://github.com/pingcap/tidb/issues/66581), client-go fix [client-go#1903](https://github.com/tikv/client-go/pull/1903)

## Phenomenon

- Session 1 runs `SELECT ... FOR UPDATE` on a row and holds the lock without committing.
- Session 2 runs `SELECT ... FOR UPDATE` on the same row.
- Expected: Session 2 blocks until `innodb_lock_wait_timeout` (for example, 50s), then reports `Lock wait timeout exceeded`.
- Actual: Session 2 acquires the lock and returns before `innodb_lock_wait_timeout`. The lock held by Session 1 appears to be released early, so pessimistic lock semantics are violated.

## Root Cause

Under Fair Locking (`tidb_pessimistic_txn_fair_locking=1`), a single transaction can call `LockKeys` multiple times in one flow. For example, a point get may first lock the unique index key and then lock the row key.

In `resetTTLManagerForAggressiveLockingMode` (`tikv/client-go/txnkv/transaction/txn.go`), `ttlManager` can be reset incorrectly on the second or later `LockKeys` call when the chosen primary key differs from `lastPrimaryKey`. The code does not distinguish:

- First round: `lastPrimaryKey` is still `nil`, so it should not be treated as "primary changed".
- Multiple `LockKeys` calls in one flow: for example, point get's two calls may pick different primaries, and resetting `ttlManager` here stops heartbeat for the first lock.

As a result, the lock held by Session 1 stops receiving TTL heartbeats, expires on TiKV, and Session 2 can acquire it.

## Trigger Condition

- `tidb_pessimistic_txn_fair_locking = 1`
- Pessimistic transaction with point get `FOR UPDATE`, or any pattern that triggers multiple `LockKeys` calls in a single fair-locking stage on different keys, such as unique index key + row key
- Affected TiDB version (see `Affected Version`)

## Diagnostic Steps

1. Confirm Fair Locking is enabled:

```sql
SELECT @@global.tidb_pessimistic_txn_fair_locking;  -- 1
```

1. Reproduce with two sessions:

```sql
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;
```

- Session 1:

```sql
BEGIN;
SELECT * FROM t WHERE a=1 FOR UPDATE;
```

- Session 2:

```sql
BEGIN;
SELECT * FROM t WHERE a=1 FOR UPDATE;
```

1. If Session 2 returns a row in about 12 seconds instead of blocking for about 50 seconds and then reporting `Lock wait timeout exceeded`, the issue is present.

1. Optional: in TiDB logs, search for `reset ttlManager in fair locking mode` with `lastPrimaryKeyLen=0` and non-zero `newPrimaryKeyLen` to confirm `ttlManager` was reset when it should not have been:

```text
[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 Fair Locking until a fixed release is used:

```sql
SET GLOBAL tidb_pessimistic_txn_fair_locking=0;
```

Point get `FOR UPDATE` will then block as expected, but Fair Locking benefits are lost.

## Affected Version

- Introduced by: client-go PR #1522
- TiDB: `master` and `release-8.5` (for example, `v8.5.4`)
- Other LTS branches (`6.5`, `7.1`, `7.5`, `8.1`) are not affected

## Fixed Version

- N/A at issue creation time
- Full fix: client-go PR #1903 — Fix the problem that ttl manager may be unset unexpectedly when `LockKeys` is called multiple times in a single fair locking stage
- TiDB side: use a TiDB version that includes the above client-go fix
