pingcap / pingcap/tidb

SELECT FOR UPDATE does not form expected deadlock with concurrent full-table UPDATE under RC/RR

Open
#69,004 2 comments 0 reactions 0 assignees View on GitHub
contribution may-affects-7.5 may-affects-8.1 may-affects-8.5 severity/major sig/transaction type/bug
Dominant language
Go
Stars
40.5k
Forks
6.2k
PR merge metrics
PR metrics pending

Description

## Bug Report

Please answer these questions before submitting your issue. Thanks!

### 1. Minimal reproduce step (Required)
Run the following test on TiDB v8.5.6. The issue can be reproduced under both READ COMMITTED and REPEATABLE READ.
Step1: Create the initial DBMS state:
```sql
DROP TABLE IF EXISTS t0;
CREATE TABLE t0(
c0 BOOLEAN UNIQUE,
c1 REAL ZEROFILL NOT NULL,
c2 BIGINT,
c3 VARCHAR(100) UNIQUE NOT NULL
);
INSERT INTO t0 VALUES (NULL, 0.71535, 807633510, '');
INSERT INTO t0 VALUES (1, 0.12098, 1600742384, '0.5007103978381064');
INSERT INTO t0 VALUES (2, 0.198, 16007384, '0.50071081064');
```
Step2: Execute the transaction statements in the given order:
Use two sessions.
Session 102:
```sql
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
SELECT * FROM t0 WHERE TRUE FOR UPDATE;
COMMIT;
```
Session 103:
```sql
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
UPDATE t0 SET c2 = 3141 WHERE c0 = 2;
UPDATE t0 SET c2 = DEFAULT, c0 = DEFAULT WHERE TRUE;
COMMIT;
```
Execute the statements in this order:
```sql
[102-0] BEGIN;
[103-0] BEGIN;
[103-1] UPDATE t0 SET c2 = 3141 WHERE c0 = 2;
[102-1] SELECT * FROM t0 WHERE TRUE FOR UPDATE;
-- This statement becomes blocked.
[103-2] UPDATE t0 SET c2 = DEFAULT, c0 = DEFAULT WHERE TRUE;
[102-2] COMMIT;
[102-1] SELECT * FROM t0 WHERE TRUE FOR UPDATE;
-- This statement becomes resumed.
[103-3] COMMIT;
```
Repeat the same schedule with:
```sql
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
```
### 2. What did you expect to see? (Required)

I expected TiDB to report a deadlock.
The expected lock dependency is:
Transaction 103 first updates and locks the row where c0 = 2.

Transaction 102 executes:
```sql
SELECT * FROM t0 WHERE TRUE FOR UPDATE;
```
It should lock other rows and then wait for Transaction 103's locked row.

Transaction 103 then executes:
```sql
UPDATE t0 SET c2 = DEFAULT, c0 = DEFAULT WHERE TRUE;
```
It should try to update the rows locked by Transaction 102.

Therefore:
Transaction 102 waits for Transaction 103.
Transaction 103 waits for Transaction 102.
A deadlock should be detected.

Expected error:
```sql
[103-2] UPDATE t0 SET c2 = DEFAULT, c0 = DEFAULT WHERE TRUE; -- when the update statement is executed
[102-1] SELECT * FROM t0 WHERE TRUE FOR UPDATE; -- the deadlock happened
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
```
This is also the behavior observed on MySQL 8.4.9 and MariaDB 12.2.2 under both READ COMMITTED and REPEATABLE READ.

### 3. What did you see instead (Required)
In TiDB v8.5.6, under both READ COMMITTED and REPEATABLE READ, no deadlock is reported.
After executing:
```sql
UPDATE t0 SET c2 = DEFAULT, c0 = DEFAULT WHERE TRUE;
COMMIT;
```
the blocked statement:
```sql
mysql> SELECT * FROM t0 WHERE TRUE FOR UPDATE;
+------+---------+------+--------------------+
| c0 | c1 | c2 | c3 |
+------+---------+------+--------------------+
| NULL | 0.71535 | NULL | |
| NULL | 0.12098 | NULL | 0.5007103978381064 |
| NULL | 0.198 | NULL | 0.50071081064 |
+------+---------+------+--------------------+
```
is unblocked and returns 3 rows normally.

This is inconsistent with MySQL 8.4.9, where the same schedule reports a deadlock.

### 4. What is your TiDB version? (Required)
```sql
mysql> select version();
+--------------------+
| version() |
+--------------------+
| 8.0.11-TiDB-v8.5.6 |
+--------------------+
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.