Different lock-wait point and final state between RC and RR for INSERT IGNORE ... ON DUPLICATE KEY UPDATE
- 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)
This issue can be reproduced on TiDB v8.5.6 under both `READ COMMITTED` and `REPEATABLE READ` in pessimistic transaction mode.
Step 1. Initialiez the DBMS state:
```sql
DROP TABLE IF EXISTS t0;
CREATE TABLE t0(
c0 DECIMAL UNSIGNED ZEROFILL NOT NULL DEFAULT 1134527801,
PRIMARY KEY(c0)
);
INSERT IGNORE INTO t0(c0) VALUES (NULL);
INSERT IGNORE INTO t0(c0) VALUES ('1134527801') ON DUPLICATE KEY UPDATE c0=((t0.c0)OR(BIN(t0.c0)));
INSERT INTO t0(c0) VALUES ('5'), (0.5759862552977539) ON DUPLICATE KEY UPDATE c0=t0.c0;
INSERT IGNORE INTO t0(c0) VALUES ('1134527801');
INSERT INTO t0(c0) VALUES ('0')
ON DUPLICATE KEY UPDATE c0=(
CASE (('') NOT LIKE (t0.c0))
WHEN (
CASE 1134527801
WHEN (+ (t0.c0)) THEN DEFAULT(t0.c0)
WHEN t0.c0 THEN t0.c0
ELSE '0.5759862552977539'
END
)
THEN t0.c0
ELSE t0.c0
END
);
CREATE UNIQUE INDEX i0 ON t0(c0);
```
Use three sessions.
For each session, set the same isolation level. First test with `READ COMMITTED`, then repeat with `REPEATABLE READ`.
-- Transaction 1, with statements:
```sql
[1-0] BEGIN;
[1-1] SELECT t0.c0 FROM t0 FOR UPDATE;
[1-2] DELETE FROM t0 WHERE t0.c0 LIMIT 168467786;
[1-3] COMMIT;
```
-- Transaction 2, with statements:
```sql
[2-0] BEGIN;
[2-1] SELECT t0.c0 FROM t0 FOR UPDATE;
[2-2] UPDATE t0 SET c0=1134527801;
[2-3] INSERT IGNORE INTO t0(c0) VALUES (364809719);
[2-4] DELETE FROM t0 WHERE t0.c0;
[2-5] COMMIT;
```
-- Transaction 3, with statements:
```sql
[3-0] BEGIN;
[3-1] INSERT IGNORE INTO t0(c0) VALUES (1134527801) ON DUPLICATE KEY UPDATE c0=MOD(((CAST(DEFAULT(t0.c0) AS DATE))>=(-2137468134)), false);
[3-2] INSERT IGNORE INTO t0 VALUES (NULL) ON DUPLICATE KEY UPDATE c0=t0.c0;
[3-3] COMMIT;
```
Step 2. Execute the transaction statements in the following order:
```sql
[3-0] BEGIN;
[2-0] BEGIN;
[1-0] BEGIN;
[1-1] SELECT t0.c0 FROM t0 FOR UPDATE;
[2-1] SELECT t0.c0 FROM t0 FOR UPDATE;
-- [2-1] is blocked by Transaction 1
[1-2] DELETE FROM t0 WHERE t0.c0 LIMIT 168467786;
[1-3] COMMIT;
-- [2-1] is unblocked and returns
[2-2] UPDATE t0 SET c0=1134527801;
[3-1] INSERT IGNORE INTO t0(c0) VALUES (1134527801)
ON DUPLICATE KEY UPDATE c0=MOD(((CAST(DEFAULT(t0.c0) AS DATE))>=(-2137468134)), false);
[3-2] INSERT IGNORE INTO t0 VALUES (NULL)
ON DUPLICATE KEY UPDATE c0=t0.c0;
[2-3] INSERT IGNORE INTO t0(c0) VALUES (364809719);
[2-4] DELETE FROM t0 WHERE t0.c0;
[2-5] COMMIT;
[3-3] COMMIT;
```
### 2. What did you expect to see? (Required)
I expected `READ COMMITTED` and `REPEATABLE READ` to show the same lock-wait behavior and produce the same final table state.
This test case does not use a plain `SELECT`. It only uses:
```text
INSERT
INSERT IGNORE ... ON DUPLICATE KEY UPDATE
UPDATE
DELETE
SELECT ... FOR UPDATE
```
These statements should use current-read / pessimistic-locking behavior. Therefore, the different ordinary snapshot-read semantics between `READ COMMITTED` and `REPEATABLE READ` should not explain a difference in this case.
So I expected `[3-1]` to behave consistently under both isolation levels.
### 3. What did you see instead (Required)
The first blocking point is the same under both isolation levels:
```sql
[2-1] SELECT t0.c0 FROM t0 FOR UPDATE;
```
It is blocked by Transaction 1 and is unblocked after `[1-3] COMMIT`.
However, the second blocking point is different:
Under `READ COMMITTED`:
```sql
[3-1] INSERT IGNORE INTO t0(c0) VALUES (1134527801)
ON DUPLICATE KEY UPDATE c0=MOD(((CAST(DEFAULT(t0.c0) AS DATE))>=(-2137468134)), false);
```
is blocked.
Under `REPEATABLE READ`, `[3-1]` is not blocked. Instead, the next statement is blocked:
```sql
[3-2] INSERT IGNORE INTO t0 VALUES (NULL)
ON DUPLICATE KEY UPDATE c0=t0.c0;
```
The final table state is also different.
Under `REPEATABLE READ`, the final result is:
```sql
mysql> SELECT * FROM t0;
+----+
| c0 |
+----+
| 0 |
+----+
```
Under `READ COMMITTED`, the final result is:
```sql
mysql> SELECT * FROM t0;
+------------+
| c0 |
+------------+
| 0 |
| 1134527801 |
+------------+
```
This looks suspicious because the same schedule produces different lock-wait points and different final database states between `READ COMMITTED` and `REPEATABLE READ`, even though the test case does not involve ordinary snapshot `SELECT`.
### 4. What is your TiDB version? (Required)
```sql
mysql> select version();
+--------------------+
| version() |
+--------------------+
| 8.0.11-TiDB-v8.5.6 |
+--------------------+
```
Contributor guide
Assessment
This issue has not been assessed yet.