--source include/have_innodb.inc --source include/have_debug.inc --source include/have_debug_sync.inc --source include/count_sessions.inc --disable_query_log call mtr.add_suppression("InnoDB: Transaction was aborted due to "); --enable_query_log connect stop_purge,localhost,root; START TRANSACTION WITH CONSISTENT SNAPSHOT; --connect (con1,localhost,root,,) --connect (con2,localhost,root,,) --connect (con3,localhost,root,,) --connection default CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=0; INSERT INTO t1 (id) VALUES (1); --echo # Simplest scenario: --echo # <con1, S, granted>, --echo # <con1, S, granted>, <con2, X, waiting for con1>, --echo # Before MDEV-34877: --echo # <con1, S, granted>, <con2, X, waiting for con1>, <con1, X, waiting for con1> --echo # After MDEV-34877: --echo # <con1, S, granted>, <con1, X, granted>, <con2, X, waiting for con1> --echo # Expected: instead of deadlocking, the con1's request should ingore con2's --connection con1 BEGIN; SELECT * FROM t1 LOCK IN SHARE MODE; --connection con2 BEGIN; SET DEBUG_SYNC = 'lock_wait_before_suspend SIGNAL con2_will_wait'; --send SELECT * FROM t1 FOR UPDATE --connection con1 SET DEBUG_SYNC = 'now WAIT_FOR con2_will_wait'; SELECT * FROM t1 FOR UPDATE; COMMIT; --connection con2 --reap COMMIT; --echo # The scenario when we bypass X<-S pair: --echo # <con1, S, granted>, --echo # <con1, S, granted>, <con2, X, waiting for con1>, --echo # <con1, S, granted>, <con2, X, waiting for con1>, <con3, S, waiting for con2> --echo # <con1, S, granted>, <con1, X, granted>, <con2, X, waiting for con1>, <con3, S, waiting for con2> --connection con1 BEGIN; SELECT * FROM t1 LOCK IN SHARE MODE; --connection con2 BEGIN; SET DEBUG_SYNC = 'lock_wait_before_suspend SIGNAL con2_will_wait'; --send SELECT * FROM t1 FOR UPDATE --connection con3 SET DEBUG_SYNC = 'now WAIT_FOR con2_will_wait'; BEGIN; SET DEBUG_SYNC = 'lock_wait_before_suspend SIGNAL con3_will_wait'; --send SELECT * FROM t1 LOCK IN SHARE MODE; --connection con1 SET DEBUG_SYNC = 'now WAIT_FOR con3_will_wait'; SELECT * FROM t1 FOR UPDATE; COMMIT; --connection con2 --reap COMMIT; --connection con3 --reap COMMIT; # --echo # A variant of the above scenario: --echo # <con1, X REC_NOT_GAP, granted>, --echo # <con1, X REC_NOT_GAP, granted>, <con2, S, waiting for con1>, --echo # <con1, X REC_NOT_GAP, granted>, <con2, S, waiting for con1>, <con1, INSERT INTENTION, waiting for con1> --echo # Expected: a deadlock, as INSERT INTENTION should not overtake locks on gap, to not slice them --connection con1 BEGIN; SELECT * FROM t1 WHERE id=1 FOR UPDATE; --connection con2 BEGIN; SET DEBUG_SYNC = 'lock_wait_start SIGNAL con2_will_wait'; --send SELECT * FROM t1 LOCK IN SHARE MODE --connection con1 SET DEBUG_SYNC = 'now WAIT_FOR con2_will_wait'; INSERT INTO t1 VALUES (0); ROLLBACK; --connection con2 --error ER_LOCK_DEADLOCK --reap COMMIT; --echo # More complicated scenario: --echo # <con1, S, granted>, --echo # <con1, S, granted>, <con2, S REC_NOT_GAP, granted>, --echo # <con1, S, granted>, <con2, S REC_NOT_GAP, granted>, <con3, X, waiting for con1> --echo # <con1, S, granted>, <con2, S REC_NOT_GAP, granted>, <con3, X, waiting for con1>, <con1, INSERT_INTENTION, waiting for con3> --echo # <con1, S, granted>, <con3, X, waiting for con1>, <con1, INSERT_INTENTION, waiting for con3> --echo # Expected: a deadlock, as INSERT INTENTION should not overtake locks on gap, to not slice them --connection con1 BEGIN; SELECT * FROM t1 LOCK IN SHARE MODE; --connection con2 BEGIN; SELECT * FROM t1 WHERE id=1 LOCK IN SHARE MODE; --connection con3 SET DEBUG_SYNC = 'lock_wait_before_suspend SIGNAL con3_will_wait'; --send SELECT * FROM t1 FOR UPDATE --connection con1 SET DEBUG_SYNC = 'now WAIT_FOR con3_will_wait'; SET DEBUG_SYNC = 'lock_wait_start SIGNAL con1_will_wait'; --send INSERT INTO t1 VALUES (0) --connection con2 SET DEBUG_SYNC = 'now WAIT_FOR con1_will_wait'; COMMIT; --connection con1 --reap ROLLBACK; --connection con3 --error ER_LOCK_DEADLOCK --reap --echo # More complicated scenario. --echo # <con1, S, granted>, --echo # <con1, S, granted>, <con2, S REC_NOT_GAP, granted>, --echo # <con1, S, granted>, <con2, S REC_NOT_GAP, granted>, <con3, X, waiting for con1> --echo # <con1, S, granted>, <con2, S REC_NOT_GAP, granted>, <con3, X, waiting for con1>, <con1, X REC_NOT_GAP, waiting for con2> --echo # Before MDEV-34877: --echo # <con1, S, granted>, <con3, X, waiting for con1>, <con1, X REC_NOT_GAP, waiting for con3> --echo # After MDEV-34877: --echo # <con1, S, granted>, <con1, X REC_NOT_GAP, granted>, <con3, X, waiting for con1> --connection con1 BEGIN; SELECT * FROM t1 LOCK IN SHARE MODE; --connection con2 BEGIN; SELECT * FROM t1 WHERE id=1 LOCK IN SHARE MODE; --connection default --connection con3 SET DEBUG_SYNC = 'lock_wait_before_suspend SIGNAL con3_will_wait'; --send SELECT * FROM t1 FOR UPDATE --connection con1 SET DEBUG_SYNC = 'now WAIT_FOR con3_will_wait'; SET DEBUG_SYNC = 'lock_wait_before_suspend SIGNAL con1_will_wait'; --send SELECT * FROM t1 WHERE id=1 FOR UPDATE --connection con2 SET DEBUG_SYNC = 'now WAIT_FOR con1_will_wait'; COMMIT; --connection con1 --reap COMMIT; --connection con3 --reap COMMIT; --echo # A secenario, where con1 has to bypass two transactions: --echo # <con1, S, granted> --echo # <con1, S, granted> <con2, X, waiting> --echo # <con1, S, granted> <con2, X, waiting> <con3, X, waiting> --echo # Before MDEV-34877: --echo # <con1, S, granted> <con2, X, waiting> <con3, X, waiting> <con1, X REC_NOT_GAP, waiting for con2> --echo # After MDEV-34877: --echo # <con1, S, granted> <con1, X REC_NOT_GAP, granted> <con2, X, waiting> <con3, X, waiting> --connection con1 BEGIN; SELECT * FROM t1 LOCK IN SHARE MODE; --connection con2 SET DEBUG_SYNC = 'lock_wait_before_suspend SIGNAL con2_will_wait'; --send SELECT * FROM t1 FOR UPDATE --connection con3 SET DEBUG_SYNC = 'now WAIT_FOR con2_will_wait'; SET DEBUG_SYNC = 'lock_wait_before_suspend SIGNAL con3_will_wait'; --send SELECT * FROM t1 FOR UPDATE --connection con1 SET DEBUG_SYNC = 'now WAIT_FOR con3_will_wait'; SELECT * FROM t1 WHERE id=1 FOR UPDATE; COMMIT; --connection con2 --reap COMMIT; --connection con3 --reap COMMIT; --connection default --disconnect con1 --disconnect con2 --disconnect con3 --disconnect stop_purge DROP TABLE t1; --source include/wait_until_count_sessions.inc