Permalink
Browse files

MDEV-17772 - 3 way lock : ALTER, MDL, BACKUP STAGE BLOCK_DDL

While waiting for a (potentially long) RO transaction or SELECT, DDL and
LOCK TABLES ... WRITE hold protection against FTWRL and BACKUP STAGE.

This effectively makes FTWRL/BACKUP STAGE indirectly wait for this RO
transaction or SELECT to finish. Which is not great, as otherwise we
could do something useful meanwhile.

With this patch BACKUP lock is attempted to be acquired after TABLE/SCHEMA
locks. If this attempt fails, TABLE/SCHEMA locks gets released and we
start waiting for BACKUP lock. When wait finishes, BACKUP lock is released
(to avoid deadlocks) and we attempt to acquire all locks once again.
  • Loading branch information...
svoj committed Dec 6, 2018
1 parent 85def21 commit e55162ddd011a384382ef9e7ce4f69639a380df0
@@ -52,12 +52,10 @@ backup stage start;
backup stage flush;
SET STATEMENT lock_wait_timeout=0 FOR SELECT * FROM t1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SET STATEMENT lock_wait_timeout=0 FOR backup stage block_ddl;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
backup stage block_ddl;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info;
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_DDL Backup lock
MDL_BACKUP_FLUSH Backup lock
MDL_BACKUP_WAIT_DDL Backup lock
MDL_SHARED_WRITE Table metadata lock test t1
MDL_INTENTION_EXCLUSIVE Schema metadata lock test
backup stage end;
@@ -170,3 +168,31 @@ DROP TABLE t_permanent_myisam, t_permanent_innodb;
DROP TABLE t_con1_innodb, t_con1_myisam;
disconnect con1;
set global lock_wait_timeout=default;
#
# Make sure pending LOCK TABLES doesn't block BACKUP STAGE
#
CREATE TABLE t1(a INT);
LOCK TABLE t1 READ;
#
connect con1,localhost,root,,;
SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL ready';
LOCK TABLE t1 WRITE;
#
connect con2,localhost,root,,;
SET DEBUG_SYNC= 'now WAIT_FOR ready';
BACKUP STAGE START;
BACKUP STAGE FLUSH;
BACKUP STAGE BLOCK_DDL;
BACKUP STAGE END;
disconnect con2;
#
connection default;
UNLOCK TABLES;
#
connection con1;
UNLOCK TABLES;
disconnect con1;
#
connection default;
DROP TABLE t1;
SET DEBUG_SYNC= 'RESET';
@@ -84,8 +84,7 @@ let $wait_condition=
--error ER_LOCK_WAIT_TIMEOUT
SET STATEMENT lock_wait_timeout=0 FOR SELECT * FROM t1;
--error ER_LOCK_WAIT_TIMEOUT
SET STATEMENT lock_wait_timeout=0 FOR backup stage block_ddl;
backup stage block_ddl;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info;
backup stage end;
select user,db,Info from information_schema.processlist where user <> "system user" order by Info;
@@ -138,15 +137,10 @@ SET STATEMENT lock_wait_timeout=0 FOR DROP TABLE t1;
connection con2;
backup stage start;
backup stage flush;
--send backup stage block_ddl
backup stage block_ddl;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for backup lock";
--source include/wait_condition.inc
commit;
connection con2;
--reap
backup stage end;
connection con1;
--reap # DROP TABLE
@@ -232,3 +226,38 @@ DROP TABLE t_permanent_myisam, t_permanent_innodb;
DROP TABLE t_con1_innodb, t_con1_myisam;
--disconnect con1
set global lock_wait_timeout=default;
--echo #
--echo # Make sure pending LOCK TABLES doesn't block BACKUP STAGE
--echo #
CREATE TABLE t1(a INT);
LOCK TABLE t1 READ;
--echo #
connect (con1,localhost,root,,);
SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL ready';
--send LOCK TABLE t1 WRITE
--echo #
connect (con2,localhost,root,,);
SET DEBUG_SYNC= 'now WAIT_FOR ready';
BACKUP STAGE START;
BACKUP STAGE FLUSH;
BACKUP STAGE BLOCK_DDL;
BACKUP STAGE END;
disconnect con2;
--echo #
connection default;
UNLOCK TABLES;
--echo #
connection con1;
reap;
UNLOCK TABLES;
disconnect con1;
--echo #
connection default;
DROP TABLE t1;
SET DEBUG_SYNC= 'RESET';
@@ -1693,13 +1693,39 @@ disconnect con2;
#
connection default;
FLUSH TABLES WITH READ LOCK;
UNLOCK TABLES;
HANDLER t1 CLOSE;
#
connection con1;
UNLOCK TABLES;
disconnect con1;
#
connection default;
DROP TABLE t1;
SET DEBUG_SYNC= 'RESET';
#
# Make sure pending LOCK TABLES doesn't block FTWRL
#
CREATE TABLE t1(a INT);
LOCK TABLE t1 READ;
#
connect con1,localhost,root,,;
SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL ready';
LOCK TABLE t1 WRITE;
#
connect con2,localhost,root,,;
SET DEBUG_SYNC= 'now WAIT_FOR ready';
FLUSH TABLES WITH READ LOCK;
UNLOCK TABLES;
disconnect con2;
#
connection default;
UNLOCK TABLES;
HANDLER t1 CLOSE;
#
connection con1;
UNLOCK TABLES;
disconnect con1;
#
connection default;
DROP TABLE t1;
SET DEBUG_SYNC= 'RESET';
@@ -2037,7 +2037,9 @@ disconnect con2;
--echo #
connection default;
--send FLUSH TABLES WITH READ LOCK
FLUSH TABLES WITH READ LOCK;
UNLOCK TABLES;
HANDLER t1 CLOSE;
--echo #
connection con1;
@@ -2047,8 +2049,39 @@ disconnect con1;
--echo #
connection default;
DROP TABLE t1;
SET DEBUG_SYNC= 'RESET';
--echo #
--echo # Make sure pending LOCK TABLES doesn't block FTWRL
--echo #
CREATE TABLE t1(a INT);
LOCK TABLE t1 READ;
--echo #
connect (con1,localhost,root,,);
SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL ready';
--send LOCK TABLE t1 WRITE
--echo #
connect (con2,localhost,root,,);
SET DEBUG_SYNC= 'now WAIT_FOR ready';
FLUSH TABLES WITH READ LOCK;
UNLOCK TABLES;
disconnect con2;
--echo #
connection default;
UNLOCK TABLES;
--echo #
connection con1;
reap;
UNLOCK TABLES;
HANDLER t1 CLOSE;
disconnect con1;
--echo #
connection default;
DROP TABLE t1;
SET DEBUG_SYNC= 'RESET';
@@ -3124,72 +3124,3 @@ connection default;
SET debug_sync='RESET';
DROP TABLE t1;
disconnect con1;
#
# MDEV-5336 - Implement LOCK FOR BACKUP
#
# Make sure deadlock detector prefers FTWRL connection as a victim
# and FTWRL retries lock attempt. This deadlock was present before
# MDEV-5336.
CREATE TABLE t1(a INT) ENGINE=InnoDB;
CREATE TABLE t2(a INT) ENGINE=InnoDB;
BEGIN;
SELECT * FROM t2;
a
#
connect con1,localhost,root,,;
SET DEBUG_SYNC='mdl_acquire_lock_wait SIGNAL waiting';
LOCK TABLES t2 WRITE;
#
connect con2,localhost,root,,;
SET DEBUG_SYNC='now WAIT_FOR waiting';
SET DEBUG_SYNC='mdl_acquire_lock_wait SIGNAL waiting';
FLUSH TABLES WITH READ LOCK;
#
connection default;
SET DEBUG_SYNC='now WAIT_FOR waiting';
INSERT INTO t1 VALUES(1);
COMMIT;
connection con1;
UNLOCK TABLES;
connection con2;
UNLOCK TABLES;
connection default;
DROP TABLE t1, t2;
SET DEBUG_SYNC='RESET';
disconnect con1;
disconnect con2;
# Make sure deadlock detector prefers FTWRL connection as a victim
# and FTWRL retries lock attempt. This deadlock was found during
# MDEV-5336 review.
CREATE TABLE t1(a INT) ENGINE=InnoDB;
CREATE TABLE t2(a INT) ENGINE=InnoDB;
BEGIN;
INSERT INTO t2 VALUES(1);
SET DEBUG_SYNC='after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR go';
INSERT INTO t1 VALUES(1);
#
connect con1,localhost,root,,;
SET DEBUG_SYNC='now WAIT_FOR table_opened';
SET DEBUG_SYNC='mdl_acquire_lock_wait SIGNAL waiting';
LOCK TABLES t1 WRITE;
#
connect con2,localhost,root,,;
SET DEBUG_SYNC='now WAIT_FOR waiting';
SET DEBUG_SYNC='mdl_acquire_lock_wait SIGNAL waiting';
FLUSH TABLES WITH READ LOCK;
#
connect con3,localhost,root,,;
SET DEBUG_SYNC='now WAIT_FOR waiting';
SET DEBUG_SYNC='now SIGNAL go';
connection default;
COMMIT;
connection con1;
UNLOCK TABLES;
connection con2;
UNLOCK TABLES;
connection default;
DROP TABLE t1, t2;
SET DEBUG_SYNC='RESET';
disconnect con1;
disconnect con2;
disconnect con3;
@@ -4168,91 +4168,6 @@ DROP TABLE t1;
disconnect con1;
--echo #
--echo # MDEV-5336 - Implement LOCK FOR BACKUP
--echo #
--echo # Make sure deadlock detector prefers FTWRL connection as a victim
--echo # and FTWRL retries lock attempt. This deadlock was present before
--echo # MDEV-5336.
CREATE TABLE t1(a INT) ENGINE=InnoDB;
CREATE TABLE t2(a INT) ENGINE=InnoDB;
BEGIN;
SELECT * FROM t2;
--echo #
connect(con1,localhost,root,,);
SET DEBUG_SYNC='mdl_acquire_lock_wait SIGNAL waiting';
send LOCK TABLES t2 WRITE;
--echo #
connect(con2,localhost,root,,);
SET DEBUG_SYNC='now WAIT_FOR waiting';
SET DEBUG_SYNC='mdl_acquire_lock_wait SIGNAL waiting';
send FLUSH TABLES WITH READ LOCK;
--echo #
connection default;
SET DEBUG_SYNC='now WAIT_FOR waiting';
INSERT INTO t1 VALUES(1);
COMMIT;
connection con1;
reap;
UNLOCK TABLES;
connection con2;
reap;
UNLOCK TABLES;
connection default;
DROP TABLE t1, t2;
SET DEBUG_SYNC='RESET';
disconnect con1;
disconnect con2;
--echo # Make sure deadlock detector prefers FTWRL connection as a victim
--echo # and FTWRL retries lock attempt. This deadlock was found during
--echo # MDEV-5336 review.
CREATE TABLE t1(a INT) ENGINE=InnoDB;
CREATE TABLE t2(a INT) ENGINE=InnoDB;
BEGIN;
INSERT INTO t2 VALUES(1);
SET DEBUG_SYNC='after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR go';
send INSERT INTO t1 VALUES(1);
--echo #
connect(con1,localhost,root,,);
SET DEBUG_SYNC='now WAIT_FOR table_opened';
SET DEBUG_SYNC='mdl_acquire_lock_wait SIGNAL waiting';
send LOCK TABLES t1 WRITE;
--echo #
connect(con2,localhost,root,,);
SET DEBUG_SYNC='now WAIT_FOR waiting';
SET DEBUG_SYNC='mdl_acquire_lock_wait SIGNAL waiting';
send FLUSH TABLES WITH READ LOCK;
--echo #
connect(con3,localhost,root,,);
SET DEBUG_SYNC='now WAIT_FOR waiting';
SET DEBUG_SYNC='now SIGNAL go';
connection default;
reap;
COMMIT;
connection con1;
reap;
UNLOCK TABLES;
connection con2;
reap;
UNLOCK TABLES;
connection default;
DROP TABLE t1, t2;
SET DEBUG_SYNC='RESET';
disconnect con1;
disconnect con2;
disconnect con3;
# Check that all connections opened by test cases in this file are really
# gone so execution of other tests won't be affected by their presence.
--source include/wait_until_count_sessions.inc
Oops, something went wrong.

0 comments on commit e55162d

Please sign in to comment.