Details
-
Bug
-
Status: Closed (View Workflow)
-
Major
-
Resolution: Duplicate
-
None
-
Unexpected results
-
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
- duplicates
-
MDEV-40483 ROW replica recomputes a STORED generated column differently from its MariaDB 12.2 primary after multi-table UPDATE and BEFORE trigger mutation
-
- Confirmed
-