Pessimistic Mode: UPDATE statement implicitly refreshes the snapshot of unrelated columns in Repeatable Read
- 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)
DROP TABLE IF EXISTS mtest;
SET tidb_txn_mode='pessimistic';
CREATE TABLE mtest(x INT DEFAULT 0, c2 DOUBLE DEFAULT NULL, c3 DOUBLE PRIMARY KEY);
INSERT IGNORE INTO mtest(c3, c2, x) VALUES (0.8, 0.13, 99);
/* s1 */SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
/* s1 */BEGIN;
/* s2 */SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
/* s2 */BEGIN;
/* s1 */UPDATE mtest SET c2=0.10 WHERE c2 = 0.13;
/* s1 */COMMIT;
/* s2 */UPDATE mtest SET x = x + 18 WHERE True;
/* s2 */SELECT c3 FROM mtest WHERE c2=0.13; ———
Empty set (0.00 sec)
/* s2 */COMMIT;
### 2. What did you expect to see? (Required)
Under REPEATABLE READ isolation, Session 2 should ideally see the data as it was at the start of the transaction (Snapshot Isolation). The SELECT should return the row where c2=0.13, because in the snapshot of Session 2, c2 has not been changed by Session 1 yet.
### 3. What did you see instead (Required)
The SELECT returns an Empty set.
It seems that the UPDATE ... WHERE True in Session 2 forced a read of the latest committed data (where c2=0.10), and subsequent SELECTs in Session 2 are now seeing this new version instead of the original snapshot.
### 4. Why I think this is counter-intuitive:
I understand that in Pessimistic Mode (similar to MySQL InnoDB), an UPDATE triggers a "Current Read" to fetch the latest committed data. However, this behavior is confusing and potentially dangerous for the following reasons:
① Violation of Snapshot Semantics:
Users choose REPEATABLE READ expecting a stable snapshot. In this case, Session 2 only intended to modify column x. However, after the UPDATE, the transaction silently "pulled in" the changes to column c2 made by Session 1. The snapshot is effectively partially refreshed to the latest version, breaking the "Repeatable Read" promise for the rest of the transaction.
② Implicit Logic Break:
If my application logic relies on c2 remaining constant within the transaction (e.g., checking c2 status before doing another action), this implicit refresh causes a logical inconsistency. The transaction starts seeing c2=0.13, performs an unrelated update on x, and suddenly c2 becomes 0.10.
③ Inconsistency with Optimistic Mode/PostgreSQL:
This behavior differs from strict Snapshot Isolation (e.g., TiDB Optimistic Mode or PostgreSQL), where such a conflict would either be blocked or cause a serialization failure, rather than silently merging the views.
### 5. What is your TiDB version? (Required)
8.0.11-TiDB-v7.5.1
Contributor guide
Assessment
This issue has not been assessed yet.