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

binlog_format=MIXED primary/replica row divergence with trigger-called stored function after savepoint rollback

    XMLWordPrintable

Details

    • Unexpected results
    • Hide
      In {{binlog_format=MIXED}}, an {{AFTER INSERT}} trigger that evaluates a stored
      function can leave the replica with different committed rows from the primary
      after {{ROLLBACK TO SAVEPOINT}} and a later ordinary witness {{INSERT}}. The
      function writes two {{AUTO_INCREMENT}} side-effect tables, and the first
      side-effect table has an FK to the source table.
      Show
      In {{binlog_format=MIXED}}, an {{AFTER INSERT}} trigger that evaluates a stored function can leave the replica with different committed rows from the primary after {{ROLLBACK TO SAVEPOINT}} and a later ordinary witness {{INSERT}}. The function writes two {{AUTO_INCREMENT}} side-effect tables, and the first side-effect table has an FK to the source table.

    Description

      Summary

      binlog_format=MIXED can produce primary/replica row divergence when an
      AFTER INSERT trigger evaluates a stored function that writes two
      AUTO_INCREMENT side-effect tables, one side-effect table has an FK to the
      source table, and the transaction uses ROLLBACK TO SAVEPOINT before a later
      witness insert.

      Environment

      • Product: MariaDB Server primary/replica replication
      • Replication mode: file/position
      • binlog_format: MIXED
      • Observed affected versions: MariaDB 10.3, 10.6, 12.2
      • Clean controls: MariaDB 10.3/10.6/12.2 with ROW; MySQL
        5.6/5.7/8.0 with MIXED
      • Storage engine: InnoDB

      Steps To Reproduce

      1. Start a MariaDB primary/replica pair with binlog_format=MIXED.
      2. Run the SQL in the Minimal Reproduction SQL section on the primary.
      3. Wait for the replica to catch up.
      4. Run this query on both primary and replica:

      SELECT seq, src_id, frame_v, note
      FROM acl_repl_ai_func_min_frame
      ORDER BY seq;
      

      Minimal Reproduction SQL

      -- MariaDB primary/replica repro. Configure binlog_format=MIXED.
       
      CREATE TABLE acl_repl_ai_func_min_src(
        id INT PRIMARY KEY,
        v INT NOT NULL,
        note VARCHAR(32) NOT NULL
      ) ENGINE=InnoDB;
       
      CREATE TABLE acl_repl_ai_func_min_audit(
        seq INT AUTO_INCREMENT PRIMARY KEY,
        src_id INT NOT NULL,
        seen_v INT NOT NULL,
        note VARCHAR(32) NOT NULL,
        KEY acl_repl_ai_func_min_audit_src_idx(src_id),
        CONSTRAINT acl_repl_ai_func_min_audit_fk
          FOREIGN KEY(src_id) REFERENCES acl_repl_ai_func_min_src(id)
          ON UPDATE CASCADE ON DELETE CASCADE
      ) ENGINE=InnoDB;
       
      CREATE TABLE acl_repl_ai_func_min_frame(
        seq INT AUTO_INCREMENT PRIMARY KEY,
        src_id INT NOT NULL,
        frame_v INT NOT NULL,
        note VARCHAR(64) NOT NULL
      ) ENGINE=InnoDB;
       
      DELIMITER //
      CREATE FUNCTION acl_repl_ai_func_min_eval(
        p_src_id INT,
        p_seen_v INT,
        p_note VARCHAR(32)
      )
      RETURNS INT DETERMINISTIC MODIFIES SQL DATA
      BEGIN
        INSERT INTO acl_repl_ai_func_min_audit(src_id, seen_v, note)
        VALUES (p_src_id, p_seen_v, p_note);
       
        INSERT INTO acl_repl_ai_func_min_frame(src_id, frame_v, note)
        VALUES (p_src_id, p_seen_v + 9000, CONCAT('func-', p_note));
       
        RETURN p_seen_v;
      END//
       
      CREATE TRIGGER acl_repl_ai_func_min_ai
      AFTER INSERT ON acl_repl_ai_func_min_src
      FOR EACH ROW
      BEGIN
        SET @acl_repl_func_result =
          acl_repl_ai_func_min_eval(NEW.id, NEW.v, NEW.note);
      END//
      DELIMITER ;
       
      INSERT INTO acl_repl_ai_func_min_src(id, v, note)
      VALUES (1, 10, 'seed');
       
      START TRANSACTION;
      INSERT INTO acl_repl_ai_func_min_src(id, v, note)
      VALUES (2, 20, 'prefix');
      SAVEPOINT acl_repl_ai_func_min_sp;
      INSERT INTO acl_repl_ai_func_min_src(id, v, note)
      VALUES (3, 30, 'suffix');
      ROLLBACK TO SAVEPOINT acl_repl_ai_func_min_sp;
      COMMIT;
       
      INSERT INTO acl_repl_ai_func_min_src(id, v, note)
      VALUES (4, 40, 'witness');
       
      SELECT seq, src_id, frame_v, note
      FROM acl_repl_ai_func_min_frame
      ORDER BY seq;
      

      Expected Result

      The primary and replica should return the same committed rows.

      Expected rows:

      1, 1, 9010, func-seed
      2, 2, 9020, func-prefix
      4, 4, 9040, func-witness
      

      Actual Result

      Primary:

      1, 1, 9010, func-seed
      2, 2, 9020, func-prefix
      4, 4, 9040, func-witness
      

      Replica:

      1, 1, 9010, func-seed
      2, 2, 9020, func-prefix
      3, 4, 9040, func-witness
      

      Separate Verification Request

      Please verify this stored-function form separately from the stored-procedure form. Stored functions and stored procedures can use different unsafe-statement classification paths under mixed binary logging.

      Attachments

        Issue Links

          Activity

            People

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