136 lines
4.9 KiB
Plaintext
136 lines
4.9 KiB
Plaintext
--source include/have_debug_sync.inc
|
|
|
|
--echo # Bug #28523042
|
|
--echo # INNODB: ASSERTION FAILURE: LOCK0LOCK.CC:7034 IN DEADLOCKCHECKER::SEARCH*
|
|
|
|
# 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 = 100000;
|
|
|
|
# The original scenario from bug report, simplified a little, to still produce
|
|
# assertion failure on old code, but not involve XA and nondeterministic
|
|
# ouput.
|
|
CREATE TABLE t1(f1 INT KEY,f2 INT);
|
|
INSERT INTO t1 VALUES(2,2);
|
|
# We need SERIALIZABLE level, as in REPEATABLE READ, SELECT .. FOR SHARE
|
|
# does not take any locks on DD tables.
|
|
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
|
|
BEGIN;
|
|
# This SELECT places an S,REC_NOT_GAP on a single row of innodb_table_stats
|
|
SELECT COUNT(1) FROM mysql.innodb_table_stats
|
|
WHERE database_name='test' AND table_name='t1' FOR SHARE;
|
|
# This INSERT causes a background update of stats for test.t1,
|
|
# which has to wait because we hold S,REC_NOT_GAP on this particular record
|
|
INSERT INTO t1 VALUES(1,2);
|
|
# This SELECT will attempt to get X,REC_NOT_GAP lock on the same record,
|
|
# but has to wait for the background thread due to FCFS nature of queue,
|
|
# which causes a deadlock cycle
|
|
SELECT count(1) FROM mysql.innodb_table_stats
|
|
WHERE database_name='test' AND table_name='t1' FOR UPDATE;
|
|
# The background thread will be chosen as a victim, which will cause an ERROR
|
|
# to be reported to the error log:
|
|
call mtr.add_suppression(
|
|
"Cannot save table statistics for table `test`.`t1`: Deadlock"
|
|
);
|
|
COMMIT;
|
|
DROP TABLE t1;
|
|
|
|
# The test below intends to check how our code reacts to a deadlock in an
|
|
# InnoDB table from mysql.* database schema. In particular, we don't want
|
|
# any assertion failures.
|
|
# Make a list of names of tables to test.
|
|
# These are the InnoDB tables visible to the end user in mysql.* database.
|
|
# We exclude some table names from the list:
|
|
# - ndb_binlog_index is only available on builds with NDB Cluster
|
|
CREATE TABLE t1 (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
name TEXT
|
|
) ENGINE=InnoDB
|
|
SELECT table_name AS name
|
|
FROM information_schema.tables
|
|
WHERE table_schema="mysql" AND engine="InnoDB"
|
|
AND table_name NOT IN (
|
|
"ndb_binlog_index"
|
|
)
|
|
ORDER BY table_name;
|
|
|
|
--let $i = 1
|
|
--let $n = `SELECT COUNT(*) FROM t1`
|
|
|
|
while($i <= $n)
|
|
{
|
|
--let $name = `SELECT name FROM t1 WHERE id = $i`
|
|
--echo Preparing deadlock scenario for mysql.$name
|
|
--let $cnt = `SELECT COUNT(*) FROM mysql.$name`
|
|
--disable_query_log
|
|
--disable_result_log
|
|
|
|
# We plan to create a deadlock cycle in a quite simple way:
|
|
# 1. c1: locks at least one row in $name FOR SHARE (let's call the row "A")
|
|
# 2. c2: locks the row with id=10 in t1 FOR SHARE (let's call the row "B")
|
|
# 3. c2: waits for a lock on the row "A" FOR UPDATE
|
|
# 4. c1: now tries to lock the row "B" FOR UPDATE
|
|
# However, if the table $name is initially empty, we need to first insert row "A",
|
|
# to have something to work with.
|
|
# This requires us to guess plauisible default values for all columns of "A".
|
|
# We exploit that in sql-mode="" we can simply pass 0 for each column.
|
|
# We simply count number of columns and prepare a string with that many
|
|
# zeros.
|
|
# Also, to force the c2 to be chosen as a victim, we ensure that c1 has
|
|
# greater weight, by allowing it to lock more rows in t1 in step 4.
|
|
# I am aware that these tests are fragile, so if it is costly to maintain them
|
|
# please feel free to remove them - they were useful during development, but
|
|
# probably not worth spending resources to keep them up-to-date.
|
|
--connect (c1, localhost, root,,)
|
|
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
|
|
BEGIN;
|
|
if(!$cnt)
|
|
{
|
|
SET SESSION sql_mode = "";
|
|
let $zeros= `
|
|
SELECT REPEAT("0,", COUNT(*) - 1)
|
|
FROM information_schema.columns
|
|
WHERE table_schema = 'mysql'
|
|
AND table_name = '$name'
|
|
`;
|
|
|
|
--eval INSERT INTO mysql.$name VALUES ($zeros 0)
|
|
}
|
|
# 1. c1: locks at least one row in $name FOR SHARE (let's call the row "A")
|
|
--eval SELECT COUNT(*) FROM mysql.$name FOR SHARE
|
|
|
|
--connect (c2, localhost, root,,)
|
|
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
|
|
BEGIN;
|
|
# 2. c2: locks the row with id=10 in t1 FOR SHARE (let's call the row "B")
|
|
SELECT id FROM t1 WHERE id=10 FOR SHARE;
|
|
SET DEBUG_SYNC = 'lock_wait_will_wait SIGNAL c2_will_wait';
|
|
# 3. c2: waits for a lock on the row "A" FOR UPDATE
|
|
--send_eval SELECT COUNT(*) FROM mysql.$name FOR UPDATE
|
|
|
|
--connection c1
|
|
SET DEBUG_SYNC = 'now WAIT_FOR c2_will_wait';
|
|
# 4. c1: now tries to lock the row "B" FOR UPDATE
|
|
SELECT COUNT(*) FROM t1 FOR UPDATE;
|
|
--enable_query_log
|
|
--enable_result_log
|
|
ROLLBACK;
|
|
|
|
--connection c2
|
|
--error ER_LOCK_DEADLOCK
|
|
--reap
|
|
ROLLBACK;
|
|
|
|
--connection default
|
|
--disconnect c1
|
|
--disconnect c2
|
|
|
|
--inc $i
|
|
}
|
|
|
|
DROP TABLE t1;
|
|
|
|
SET @@global.innodb_lock_wait_timeout = @innodb_lock_wait_timeout_saved;
|