Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-40182

RR misses ERROR 1020 after lock wait recovery with innodb_snapshot_isolation=ON

    XMLWordPrintable

Details

    • Bug
    • Status: Open (View Workflow)
    • Major
    • Resolution: Unresolved
    • 12.2.2
    • None
    • None
    • MariaDB version: 12.2.2
      Storage engine: InnoDB
      innodb_snapshot_isolation=ON
      solation levels tested: `REPEATABLE READ` and `SERIALIZABLE`
    • With innodb_snapshot_isolation=ON, UPDATE under REPEATABLE READ may succeed after lock wait recovery even though the physical row was changed by another transaction. The same case correctly reports ERROR 1020 under SERIALIZABLE.

    Description

      Description

      I found a possible InnoDB snapshot-isolation issue in MariaDB 12.2.2 with `innodb_snapshot_isolation=ON`.

      In the following two-transaction schedule, Transaction 1 updates the only row in the table from `(9)` to `(4)` and holds the lock. Transaction 2 then executes:

      UPDATE t0 SET c0=1 WHERE TRUE;
      

      This statement is blocked by Transaction 1. After Transaction 1 commits, Transaction 2 resumes execution.

      Under `SERIALIZABLE`, Transaction 2 reports the following error at `[2-1]`, which seems correct:

      ERROR 1020 (HY000): Record has changed since last read in table 't0'
      

      However, under `REPEATABLE READ`, the same statement succeeds normally.

      I think the `REPEATABLE READ` behavior is suspicious when `innodb_snapshot_isolation=ON`. At the time `[2-1]` starts waiting, Transaction 2 is waiting on the row `(9)`. After Transaction 1 commits, the physical row has been updated to `(4)`. Therefore, the row that Transaction 2 originally read/waited for has changed while it was waiting.

      This seems similar to the explanation in MDEV-39778, where Shipra Jain wrote:
      > After con1 commits, con2 resumes execution. MariaDB then checks whether the row that con2 originally read is still unchanged. Even though con1 only modified column c0, that does not matter here — the important part is that the physical row itself was updated while con2 was waiting on the lock.

      Based on this explanation and the definition of `innodb_snapshot_isolation=ON`, I expected `REPEATABLE READ` to report `ERROR 1020` as well, because the physical row changed while Transaction 2 was waiting.

      How to repeat

      Create the test table:

      DROP TABLE IF EXISTS t0;
      CREATE TABLE t0(
          c0 INT PRIMARY KEY
      );
      INSERT INTO t0(c0) VALUES (9);
      

      Use two sessions.
      For the `REPEATABLE READ` test, set both sessions to:

      SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
      

      For the `SERIALIZABLE` test, set both sessions to:

      SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
      

      Transaction 1:

      [1-0] BEGIN;
      [1-1] UPDATE t0 SET c0=4 WHERE TRUE;
      [1-2] COMMIT;
      

      Transaction 2:

      [2-0] BEGIN;
      [2-1] UPDATE t0 SET c0=1 WHERE TRUE;
      [2-2] COMMIT;
      

      Execute the statements in the following order:

      [1-0] BEGIN;
      [1-1] UPDATE t0 SET c0=4 WHERE TRUE;
      [2-0] BEGIN;
      [2-1] UPDATE t0 SET c0=1 WHERE TRUE;
            -- blocked
      [1-2] COMMIT;
      [2-1] resumes execution
      [2-2] COMMIT;
      

      Actual result

      Under `SERIALIZABLE`, `[2-1]` reports:

      ERROR 1020 (HY000): Record has changed since last read in table 't0'
      

      Under `REPEATABLE READ`, `[2-1]` succeeds normally.

      Expected result

      I expected `REPEATABLE READ` to also report:

      ERROR 1020 (HY000): Record has changed since last read in table 't0'
      

      The reason is that, with `innodb_snapshot_isolation=ON`, Transaction 2 starts waiting on the physical row `(9)`. After Transaction 1 commits, that physical row has been updated to `(4)`. Therefore, the row that Transaction 2 originally read or waited for is no longer unchanged.

      Since `SERIALIZABLE` correctly detects this condition and reports `ERROR 1020`, I expected `REPEATABLE READ` to behave consistently in this case.

      Additional observation

      This case does not involve deleting the row. Transaction 1 only updates the primary key value from `9` to `4`. However, the physical row is still changed while Transaction 2 is waiting. According to the explanation in MDEV-39778, this should be sufficient to trigger the "record has changed since last read" check.

      Attachments

        Activity

          People

            Unassigned Unassigned
            yousaha yousaha
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:

              Git Integration

                Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.