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

ROW replica recomputes a STORED generated column differently from its MariaDB 12.2 primary after multi-table UPDATE and BEFORE trigger mutation

    XMLWordPrintable

Details

    • Unexpected results
    • Hide
      With MariaDB 12.2.2 file/position replication and {{binlog_format=ROW}}, a
      multi-table UPDATE whose {{BEFORE UPDATE}} trigger changes a base column leaves the primary and replica with different stored values for the same generated column. The primary retains its actual stale generated value; the row-applied replica recomputes the generated value from {{v}}. Consequently ROW replication does not preserve the primary's persisted row.
      Show
      With MariaDB 12.2.2 file/position replication and {{binlog_format=ROW}}, a multi-table UPDATE whose {{BEFORE UPDATE}} trigger changes a base column leaves the primary and replica with different stored values for the same generated column. The primary retains its actual stale generated value; the row-applied replica recomputes the generated value from {{v}}. Consequently ROW replication does not preserve the primary's persisted row.

    Description

      Environment

      • Product: MariaDB Server primary/replica replication
      • Observed affected topology: MariaDB 12.2.2 primary and MariaDB 12.2.2
        replica
      • Replication: file/position; InnoDB; binlog_format=ROW; full row image
      • Observed control: MariaDB 10.6 primary/replica does not diverge in this
        schedule (both sides retain the same stale value); the 12.2.2 divergence is
        therefore version-specific in the tested matrix.

      Replication setup

      Use any ordinary, empty MariaDB 12.2.2 primary/replica pair. The primary must
      have binary logging and binlog_format=ROW enabled, and the replica must be
      running normally before the SQL below is executed. For example, the only
      required server options are equivalent to:

      primary: --server-id=101 --log-bin=mariadb-bin --binlog-format=ROW
      replica: --server-id=102 --relay-log=relay-bin
      

      On the primary, grant a replication user; obtain SHOW MASTER STATUS; then
      on the replica run {{CHANGE MASTER TO MASTER_HOST=..., MASTER_USER=...,
      MASTER_PASSWORD=..., MASTER_LOG_FILE=..., MASTER_LOG_POS=...; START SLAVE;}}.
      Wait until both Slave_IO_Running and Slave_SQL_Running are Yes.
      No data file or attachment is used by this reproduction.

      SQL to run on the primary

      Run this complete SQL only after the replica has caught up with the primary's
      empty mdev_row_generated schema. Then wait for the replica to apply the
      transaction and query the parent table on both servers.

      DROP DATABASE IF EXISTS mdev_row_generated;
      CREATE DATABASE mdev_row_generated;
      USE mdev_row_generated;
       
      CREATE TABLE parent(
        id INT PRIMARY KEY,
        v  INT NOT NULL,
        g  INT GENERATED ALWAYS AS (v * 2) STORED
      ) ENGINE=InnoDB;
       
      CREATE TABLE aux(
        id        INT PRIMARY KEY,
        parent_id INT NOT NULL,
        w         INT NOT NULL,
        FOREIGN KEY (parent_id) REFERENCES parent(id)
      ) ENGINE=InnoDB;
       
      CREATE TABLE audit(
        id        INT PRIMARY KEY,
        parent_id INT,
        old_v     INT,
        new_v     INT,
        new_g     INT
      ) ENGINE=InnoDB;
       
      INSERT INTO parent(id, v) VALUES (1, 10), (2, 20);
      INSERT INTO aux(id, parent_id, w) VALUES (101, 1, 3), (102, 2, 5);
       
      DELIMITER //
      CREATE TRIGGER parent_bu BEFORE UPDATE ON parent FOR EACH ROW
      BEGIN
        SET NEW.v = NEW.v + 7;
      END//
      CREATE TRIGGER parent_au AFTER UPDATE ON parent FOR EACH ROW
      BEGIN
        INSERT INTO audit VALUES (OLD.id, OLD.id, OLD.v, NEW.v, NEW.g);
      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;
       
      SELECT id, v, g, v * 2 AS recomputed_g, g = v * 2 AS invariant_holds
      FROM parent
      ORDER BY id;
      

      Actual result on the 12.2.2 primary and replica

      After replication is caught up, the two endpoint queries return different
      rows:

      primary
      id  v   g   recomputed_g  invariant_holds
      1   20  26  40            0
      2   32  50  64            0
       
      replica
      id  v   g   recomputed_g  invariant_holds
      1   20  40  40            1
      2   32  64  64            1
      

      The primary stores g from the pre-trigger proposals (13 and 25), whereas
      the replica stores g recomputed from the row image's v values (20 and
      32). The two servers therefore no longer contain identical committed rows
      after a forced ROW-logged transaction.

      Expected result

      For row-based replication, the replica should preserve the primary's committed
      row state. It should not silently change g from the source row's stored
      value. At minimum, the endpoint snapshots should be identical after the
      replica applies the transaction.

      Controls and repetition

      • Deterministic ROW case above: 3/3 12.2.2 runs diverged after replication
        caught up.
      • No-trigger multi-table UPDATE control: 3/3 runs had matching endpoint rows
        and valid generated values.
      • Single-table UPDATE with a BEFORE-trigger mutation: 3/3 runs had matching
        endpoint rows and valid generated values.
      • The same deterministic multi-table ROW case on MariaDB 10.6: 3/3 runs had
        no endpoint divergence.

      Nearby reports checked

      I checked MDEV-29723, MDEV-21287, MDEV-40227, and MDEV-38465. They concern
      transaction-precise timestamps, trigger observation, or other replication /
      savepoint mechanisms. None describes MariaDB 12.2 multi-table UPDATE JOIN +
      BEFORE-trigger mutation + STORED generated column where a ROW-applied replica
      recomputes a value differently from the primary's persisted value.

      Attachments

        Activity

          People

            Unassigned Unassigned
            JaysonL Zhensheng Luo
            Votes:
            0 Vote for this issue
            Watchers:
            2 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.