CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
INSERT INTO t2 VALUES (10,1), (20,2), (30,3), (40,4), (50,5);
CREATE TABLE t3 (a VARCHAR(20) PRIMARY KEY, b INT) ENGINE=InnoDB;
INSERT INTO t3 VALUES ('row for T1', 0), ('row for T2', 0), ('row for T3', 0);
include/save_master_gtid.inc
connection slave;
include/sync_with_master_gtid.inc
include/stop_slave.inc
set @@global.slave_parallel_threads= 3;
set @@global.slave_parallel_mode= OPTIMISTIC;
set @@global.innodb_lock_wait_timeout= 20;
connection master;
BEGIN;
UPDATE t3 SET b=b+1 where a="row for T1";
INSERT INTO t1(b, c) SELECT 1, t2.b FROM t2 WHERE a=10;
Warnings:
Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statements writing to a table with an auto-increment column after selecting from another table are unsafe because the order in which rows are retrieved determines what (if any) rows will be written. This order cannot be predicted and may differ on master and the slave
COMMIT;
DELETE FROM t1 WHERE c >= 4 and c < 6;
BEGIN;
UPDATE t3 SET b=b+1 where a="row for T3";
INSERT INTO t1(b, c) SELECT 3, t2.b FROM t2 WHERE a >= 20 AND a <= 40;
Warnings:
Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statements writing to a table with an auto-increment column after selecting from another table are unsafe because the order in which rows are retrieved determines what (if any) rows will be written. This order cannot be predicted and may differ on master and the slave
COMMIT;
include/save_master_gtid.inc
connection slave1;
BEGIN;
SELECT * FROM t3 WHERE a="row for T1" FOR UPDATE;
a b
row for T1 0
connection slave2;
BEGIN;
SELECT * FROM t3 WHERE a="row for T3" FOR UPDATE;
a b
row for T3 0
connection slave3;
BEGIN;
DELETE FROM t2 WHERE a=30;
connection slave;
include/start_slave.inc
connection slave2;
ROLLBACK;
connection slave1;
ROLLBACK;
connection slave3;
ROLLBACK;
connection slave;
include/sync_with_master_gtid.inc
SELECT * FROM t1 ORDER BY a;
a b c
1 0 1
2 0 1
3 0 2
4 0 3
6 0 7
7 0 8
8 1 1
9 3 2
10 3 3
11 3 4
SELECT * FROM t2 ORDER BY a;
a b
10 1
20 2
30 3
40 4
50 5
SELECT * FROM t3 ORDER BY a;
a b
row for T1 1
row for T2 0
row for T3 1
connection master;
CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format");
# Temporarily block T1 to create the scheduling that triggers the bug.
BEGIN;
SELECT*FROMt3WHEREa="row for T1"FORUPDATE;
--connectionslave2
# Temporarily block T3 from starting (so T2 can reach commit).
BEGIN;
SELECT*FROMt3WHEREa="row for T3"FORUPDATE;
--connectionslave3
# This critical step blocks T3 after it has inserted its first row,
# and thus taken the auto-increment lock, but before it has reached
# the point where it gets a row lock wait on T2. Even though
# auto-increment lock waits were not reported due to the bug,
# transitive lock waits (T1 waits on autoinc of T3 which waits on row
# on T2) _were_ reported as T1 waiting on T2, and thus a deadlock kill
# happened and the bug was not triggered.
BEGIN;
DELETEFROMt2WHEREa=30;
--connectionslave
--sourceinclude/start_slave.inc
# First let T2 complete until it is waiting for T1 to commit.
--let$wait_condition=SELECTcount(*)=1FROMinformation_schema.processlistWHEREstate='Waiting for prior transaction to commit'andcommandLIKE'Slave_worker';
--sourceinclude/wait_condition.inc
# Then let T3 reach the point where it has obtained the autoinc lock,
# but it is not yet waiting for a row lock held by T2.
--connectionslave2
ROLLBACK;
--let$wait_condition=SELECTcount(*)=1FROMinformation_schema.processlistWHEREstate='Sending data'andinfoLIKE'INSERT INTO t1(b, c) SELECT 3, t2.b%'andtime_ms>500andcommandLIKE'Slave_worker';
--sourceinclude/wait_condition.inc
# Now let T1 continue, while T3 is holding the autoinc lock but before
# it is waiting for T2. Wait a short while to give the hang a chance to
# happen; T1 needs to get to request the autoinc lock before we let T3
# continue. (There's a small chance the sleep will be too small, which will
# let the test occasionally pass on non-fixed server).
--connectionslave1
ROLLBACK;
--sleep0.5
# Now let T3 continue; the bug was that this lead to an undetected
# deadlock that remained until innodb lock wait timeout.
--connectionslave3
ROLLBACK;
--connectionslave
--let$slave_timeout=`SELECT $lock_wait_timeout/2`
--sourceinclude/sync_with_master_gtid.inc
--let$slave_timeout=
SELECT*FROMt1ORDERBYa;
SELECT*FROMt2ORDERBYa;
SELECT*FROMt3ORDERBYa;
# Cleanup.
--connectionmaster
CALLmtr.add_suppression("Unsafe statement written to the binary log using statement format");