From 744a53806e9d4e2f31671e361e481308dec1545d Mon Sep 17 00:00:00 2001 From: Monty Date: Thu, 29 Apr 2021 21:08:32 +0300 Subject: [PATCH] Fixed hang in concurrent DROP TABLE and BACKUP LOCK BLOCK_DDL The problem was that tdc_remove_referenced_share() did not take into account that someone could push things into share->free_tables() even if there is a MDL_EXCLUSIVE lock on the table. This can happen if flush_tables() uses the table cache to flush a a non transactional table to disk. --- .../mariabackup/deadlock_drop_table.result | 33 +++++++++++++++++ .../mariabackup/deadlock_drop_table.test | 35 +++++++++++++++++++ sql/sql_base.cc | 2 ++ sql/table_cache.cc | 3 +- 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/mariabackup/deadlock_drop_table.result create mode 100644 mysql-test/suite/mariabackup/deadlock_drop_table.test diff --git a/mysql-test/suite/mariabackup/deadlock_drop_table.result b/mysql-test/suite/mariabackup/deadlock_drop_table.result new file mode 100644 index 0000000000000..7e549157c81fd --- /dev/null +++ b/mysql-test/suite/mariabackup/deadlock_drop_table.result @@ -0,0 +1,33 @@ +create or replace table t1 (a int primary key, b int, c int, key(b),key(c)) engine=myisam; +insert into t1 (a) values(1); +set debug_sync='RESET'; +connect con1, localhost, root,,; +connect con2, localhost, root,,; +connection default; +backup stage start; +backup stage flush; +select * from t1; +a b c +1 NULL NULL +set debug_sync='after_purge_tables SIGNAL parked WAIT_FOR go'; +set debug_sync='before_tc_release_table SIGNAL parked2 WAIT_FOR go2'; +backup stage BLOCK_DDL; +connection con1; +set debug_sync='now WAIT_FOR parked'; +select * from t1; +a b c +1 NULL NULL +set debug_sync='now SIGNAL go'; +set debug_sync='now WAIT_FOR parked2'; +set debug_sync='before_wait_for_refs SIGNAL waiting WAIT_FOR go3'; +drop table t1;; +connection con2; +set debug_sync='now WAIT_FOR waiting'; +set debug_sync='now SIGNAL go2'; +set debug_sync='now SIGNAL go3'; +connection default; +connection con1; +connection default; +disconnect con1; +disconnect con2; +set debug_sync='RESET'; diff --git a/mysql-test/suite/mariabackup/deadlock_drop_table.test b/mysql-test/suite/mariabackup/deadlock_drop_table.test new file mode 100644 index 0000000000000..81350726b7658 --- /dev/null +++ b/mysql-test/suite/mariabackup/deadlock_drop_table.test @@ -0,0 +1,35 @@ +--source include/have_debug.inc + +create or replace table t1 (a int primary key, b int, c int, key(b),key(c)) engine=myisam; +insert into t1 (a) values(1); + +set debug_sync='RESET'; +connect (con1, localhost, root,,); +connect (con2, localhost, root,,); +connection default; + +backup stage start; +backup stage flush; +select * from t1; +set debug_sync='after_purge_tables SIGNAL parked WAIT_FOR go'; +set debug_sync='before_tc_release_table SIGNAL parked2 WAIT_FOR go2'; +--send backup stage BLOCK_DDL +--connection con1 +set debug_sync='now WAIT_FOR parked'; +select * from t1; +set debug_sync='now SIGNAL go'; +set debug_sync='now WAIT_FOR parked2'; +set debug_sync='before_wait_for_refs SIGNAL waiting WAIT_FOR go3'; +--send drop table t1; +--connection con2 +set debug_sync='now WAIT_FOR waiting'; +set debug_sync='now SIGNAL go2'; +set debug_sync='now SIGNAL go3'; +--connection default +--reap +--connection con1 +--reap +connection default; +disconnect con1; +disconnect con2; +set debug_sync='RESET'; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 4a01ecd97dadc..9c07e3de2729b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -554,6 +554,7 @@ bool flush_tables(THD *thd, flush_tables_type flag) DBUG_ENTER("flush_tables"); purge_tables(); /* Flush unused tables and shares */ + DEBUG_SYNC(thd, "after_purge_tables"); /* Loop over all shares and collect shares that have open tables @@ -593,6 +594,7 @@ bool flush_tables(THD *thd, flush_tables_type flag) if (table) { (void) table->file->extra(HA_EXTRA_FLUSH); + DEBUG_SYNC(table->in_use, "before_tc_release_table"); tc_release_table(table); } else diff --git a/sql/table_cache.cc b/sql/table_cache.cc index 62ccfba7e7dbb..2e9fb34c17e71 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -1001,8 +1001,9 @@ void tdc_remove_referenced_share(THD *thd, TABLE_SHARE *share) DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, share->db.str, share->table_name.str, MDL_EXCLUSIVE)); - share->tdc->flush_unused(false); + share->tdc->flush_unused(true); mysql_mutex_lock(&share->tdc->LOCK_table_share); + DEBUG_SYNC(thd, "before_wait_for_refs"); share->tdc->wait_for_refs(1); DBUG_ASSERT(share->tdc->all_tables.is_empty()); share->tdc->ref_count--;