1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
|
--source include/have_debug_sync.inc
--source include/have_innodb_16k.inc
--source include/count_sessions.inc
# Save the original settings, to be restored at the end of test
SET @innodb_lock_wait_timeout_saved = @@global.innodb_lock_wait_timeout;
# Make sure that transactions will not finish prematurely
SET @@global.innodb_lock_wait_timeout = 20;
--source ../include/prepare_show_locks.inc
# Scenario in which a WAITING lock struct is first "cleared" because locks
# are moved to a different heap_no, and then "reused" for another GRANTED
# lock on a different heap_no, potentially leading to a violation of invariant
# that GRANTED locks should be in front of WAITING locks.
# 1. con1 gets GRANTED GAP+REC S lock on row id=3
# 2. con2 gets WAITING II lock on row id=3
# 3. con3 gets GRANTED X lock on row id=2000
# 4. con4 gets WAITING GAP+REC S lock on row id=2000
# 5. con3 inserts so many rows before row id=2000 that the page has to be split,
# and the old lock struct of con4 is "cleared" and new WAITING lock for con4
# is created on the new page
# 6. con3 commits releasing the lock on row id=2000, so con4 can continue now
# con4 gets GRANTED GAP+REC S lock on row id=3 (and reuses the previously
# "cleared" struct to achieve that)
#
# The queue for row id=3 contains:
# <con1,GRANTED,GAP+REC S>, <con2,WAITING,II>, <con3,GRANTED,GAP_REC S>
# so to bring back the invariant that GRANTED are before WAITING, we need to
# move con3's lock to front.
# The CHAR(200) is just to make records large, so page split comes sooner.
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY, val CHAR(200));
INSERT INTO t1 (id,val) VALUES (1, "a"), (3, "c"), (1998,"x"), (2000, "z");
--connect (con1, localhost, root,,)
CALL register_connection("con1");
BEGIN;
SELECT * FROM t1 WHERE id<=3 FOR SHARE;
--connect (con2, localhost, root,,)
CALL register_connection("con2");
BEGIN;
SET DEBUG_SYNC='lock_wait_will_wait SIGNAL con2_will_wait';
--send INSERT INTO t1 VALUES (2,"b")
--connect (con3, localhost, root,,)
SET DEBUG_SYNC='now WAIT_FOR con2_will_wait';
CALL register_connection("con3");
BEGIN;
SELECT * FROM t1 WHERE id=2000 FOR UPDATE;
--connect (con4, localhost, root,,)
CALL register_connection("con4");
BEGIN;
SET DEBUG_SYNC='lock_wait_will_wait SIGNAL con4_will_wait';
--send SELECT * FROM t1 WHERE 1999<=id FOR SHARE
--connection default
SET DEBUG_SYNC='now WAIT_FOR con4_will_wait';
CALL show_locks();
--connection con3
--let i=10
# 16k page, each record > 200 bytes, so 80 rows should be enough
while($i<100){
--eval INSERT INTO t1 (id,val) VALUES ($i,"x")
--inc $i
}
--connection default
CALL show_locks();
--connection con3
COMMIT;
--connection con4
--reap
SELECT * FROM t1 WHERE id<=3 FOR SHARE;
--connection default
CALL show_locks();
--connection con1
COMMIT;
--connection con4
COMMIT;
--connection con2
--reap
COMMIT;
--connection default
--disconnect con1
--disconnect con2
--disconnect con3
--disconnect con4
DROP TABLE t1;
--source ../include/cleanup_show_locks.inc
# Restore saved settings
SET @@global.innodb_lock_wait_timeout = @innodb_lock_wait_timeout_saved ;
--source include/wait_until_count_sessions.inc
|