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

ROW apply recomputes a STORED generated column differently from the primary

    XMLWordPrintable

Details

    • Unexpected results
    • Hide
      MariaDB 12.2.2 can persist a stale STORED generated value on the primary when a multi-table `UPDATE` invokes a `BEFORE UPDATE` trigger that changes `NEW.v`. Under forced `binlog_format=ROW`, the row-applier on the replica recomputes the generated column from the row image instead of preserving the primary's stored value. The committed table rows therefore diverge between primary and replica.
      Show
      MariaDB 12.2.2 can persist a stale STORED generated value on the primary when a multi-table `UPDATE` invokes a `BEFORE UPDATE` trigger that changes `NEW.v`. Under forced `binlog_format=ROW`, the row-applier on the replica recomputes the generated column from the row image instead of preserving the primary's stored value. The committed table rows therefore diverge between primary and replica.

    Description

      Summary

      MariaDB 12.2.2 can persist a stale STORED generated value on the primary when a
      multi-table `UPDATE` invokes a `BEFORE UPDATE` trigger that changes `NEW.v`.
      Under forced `binlog_format=ROW`, the row-applier on the replica recomputes the
      generated column from the row image instead of preserving the primary's stored
      value. The committed table rows therefore diverge between primary and replica.

      Environment

      • Product: MariaDB Server
      • Affected build tested: MariaDB 12.2.2
      • Comparison build: MariaDB 10.6 (same SQL and topology remained primary=replica)
      • Storage engine: InnoDB
      • Topology: one primary and one file/position ROW replica
      • Logging: `SET SESSION binlog_format='ROW'`

      Reproduction

      Run this complete SQL on an empty primary after the replica has caught up with
      the schema. The ordinary file/position replication setup is the only external
      prerequisite; no SQL file or other attachment is needed.

      DROP DATABASE IF EXISTS mdev_row_generated_recompute;
      CREATE DATABASE mdev_row_generated_recompute;
      USE mdev_row_generated_recompute;
       
      CREATE TABLE parent (
        id INT PRIMARY KEY,
        v  INT NOT NULL,
        g  INT GENERATED ALWAYS AS (v * 2) STORED
      ) ENGINE=InnoDB;
       
      CREATE TABLE aux (
        parent_id INT PRIMARY KEY,
        w INT NOT NULL
      ) ENGINE=InnoDB;
       
      INSERT INTO parent VALUES (1, 10, DEFAULT), (2, 20, DEFAULT);
      INSERT INTO aux VALUES (1, 3), (2, 5);
       
      DELIMITER //
      CREATE TRIGGER parent_bu
      BEFORE UPDATE ON parent
      FOR EACH ROW
      BEGIN
        SET NEW.v = NEW.v + 7;
      END//
      DELIMITER ;
       
      SET SESSION binlog_format = 'ROW';
      UPDATE parent AS p
      JOIN aux AS a ON p.id = a.parent_id
      SET p.v = p.v + a.w,
          a.w = a.w + 1;
      

      After the replica has executed the transaction, run this witness separately on
      both endpoints:

      USE mdev_row_generated_recompute;
      SELECT id, v, g FROM parent ORDER BY id;
      SELECT id, w FROM aux ORDER BY id;
      

      Expected result

      Both endpoints should contain the same committed row image, and the generated
      invariant `g = 2*v` should hold:

      parent: (1, 20, 40), (2, 32, 64)
      aux:    (1, 4), (2, 6)
      

      Actual result

      MariaDB 12.2.2, forced ROW replication, reproduced 3/3 times:

      primary parent:  (1, 20, 26), (2, 32, 50)
      replica parent:  (1, 20, 40), (2, 32, 64)
      primary aux:     (1, 4), (2, 6)
      replica aux:     (1, 4), (2, 6)
      

      The primary's `g` values reflect the pre-trigger values (13 and 25), while the
      replica recomputes `g` from the row-event `v` values. Thus the primary itself
      violates `g = 2*v`, and the row-applied replica stores a different value for
      the same committed row event.

      Controls and repetition

      • MariaDB 12.2.2 deterministic case: primary/replica divergence in 3/3 runs.
      • MariaDB 12.2.2 multi-table UPDATE with the trigger removed: primary=replica
        and generated values correct in 3/3 runs.
      • MariaDB 12.2.2 deterministic multi-table trigger under `MIXED`/`STATEMENT`:
        both endpoints retain the same stale value; this isolates the additional
        divergence to ROW row-event application.
      • MariaDB 12.2.2 single-table `UPDATE` with a RAND-mutating trigger: primary
        and replica agree and the generated invariant holds in 3/3 runs.
      • MariaDB 10.6 forced ROW: primary=replica in 3/3 runs (the local stale value
        can still be observed, but the cross-replica recomputation divergence was not
        observed).

      The nondeterministic trigger form (`SET NEW.v=NEW.v+FLOOR(RAND()*10)`) also
      reproduced the row-apply divergence on MariaDB 12.2.2 under MIXED, STATEMENT,
      and ROW logging. These are confirmation variants of the same mechanism, not
      separate issues.

      Attachments

        Issue Links

          Activity

            People

              Deepthi ES Deepthi Eranti Sreenivas
              JaysonL Zhensheng Luo
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Git Integration

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