Commit 7fb9d649 authored by Monty's avatar Monty

Changed FLUSH TABLES to not change share version

Part of MDEV-5336 Implement LOCK FOR BACKUP

Originally both table metadata lock and global read lock protection
were acquired before getting TABLE from table cache. This will be
reordered in a future commit with MDL_BACKUP_XXX locks so that we
first take table metadata lock, then get TABLE from table cache, then
acquire analogue of global read lock.

This patch both simplifies FLUSH TABLES code, makes FLUSH TABLES to
lock less and also enables FLUSH TABLES code to be used with backup
locks.

The usage of FLUSH TABLES changes slightly:
- FLUSH TABLES without any arguments will now only close not used tables
  and tables locked by the FLUSH TABLES connection.  All not used table
  shares will be closed.
  Tables locked by the FLUSH TABLES connection will be reopened and
  re-locked after all others has stoped using the table (as before).
  If there was no locked tables, then FLUSH TABLES is instant and will
  not cause any waits.
  FLUSH TABLES will not wait for any in use table.
- FLUSH TABLES with a table list, will ensure that the tables are closed
  before statement returns. The code is now only using MDL locks and not
  table share versions, which simplices the code greatly. One visible
  change is that the server will wait for the end of the transaction that
  are using the tables. Before FLUSH TABLES only waited for the statements
  to end.
Signed-off-by: default avatarMonty <monty@mariadb.org>
parent 7bb3a522
......@@ -364,16 +364,19 @@ flush table t1;
connection default;
# Let flush table sync in.
select * from t1;
a
connection con1;
select * from t1;
a
unlock tables;
connection default;
select count(*) from information_schema.processlist where state = "Waiting for table metadata lock";
count(*)
1
commit;
connection con2;
# Reaping 'flush table t1'...
connection default;
# Reaping 'select * from t1'...
a
commit;
#
# Repeat the same test but with FLUSH TABLES
#
......@@ -386,13 +389,10 @@ connection con1;
#
lock table t1 read;
connection con2;
#
# FLUSH TABLES expels the table definition from the cache.
# Sending 'flush tables'...
flush tables;
connection default;
# Let flush table sync in.
select * from t1;
a
connection con1;
select * from t1;
a
......@@ -400,8 +400,6 @@ unlock tables;
connection con2;
# Reaping 'flush tables'...
connection default;
# Reaping 'select * from t1'...
a
commit;
# Cleanup
connection con1;
......
......@@ -449,24 +449,20 @@ connection default;
--echo # Let flush table sync in.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table flush"
where state = "Waiting for table metadata lock"
and info = "flush table t1";
--source include/wait_condition.inc
send select * from t1;
select * from t1;
connection con1;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table flush"
and info = "select * from t1";
select * from t1;
unlock tables;
connection default;
select count(*) from information_schema.processlist where state = "Waiting for table metadata lock";
commit;
connection con2;
--echo # Reaping 'flush table t1'...
reap;
connection default;
--echo # Reaping 'select * from t1'...
reap;
commit;
--echo #
--echo # Repeat the same test but with FLUSH TABLES
......@@ -480,31 +476,16 @@ connection con1;
--echo #
lock table t1 read;
connection con2;
--echo #
--echo # FLUSH TABLES expels the table definition from the cache.
--echo # Sending 'flush tables'...
send flush tables;
connection default;
--echo # Let flush table sync in.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table flush"
and info = "flush tables";
--source include/wait_condition.inc
send select * from t1;
select * from t1;
connection con1;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table flush"
and info = "select * from t1";
select * from t1;
unlock tables;
connection con2;
--echo # Reaping 'flush tables'...
reap;
connection default;
--echo # Reaping 'select * from t1'...
reap;
commit;
--echo # Cleanup
......
......@@ -1957,6 +1957,7 @@ lock table t1 read;
connect con1, localhost, root,,;
connection con1;
flush tables;
flush tables t1;
connection default;
select * from information_schema.views;
TABLE_CATALOG def
......
......@@ -1622,12 +1622,13 @@ alter table t1 change b c int;
lock table t1 read;
connect(con1, localhost, root,,);
connection con1;
send flush tables;
flush tables;
send flush tables t1;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table flush" and
info = "flush tables";
where state = "Waiting for table metadata lock" and
info = "flush tables t1";
--source include/wait_condition.inc
--vertical_results
select * from information_schema.views;
......
......@@ -324,7 +324,7 @@ connection blocker;
lock tables t1 read;
connection ddl;
# Let us mark locked table t1 as old
flush tables;
flush tables t1;
connection dml;
select * from t1;
connection default;
......
......@@ -538,18 +538,18 @@ connection blocker;
lock tables t1 read;
connection ddl;
--echo # Let us mark locked table t1 as old
--send flush tables
--send flush tables t1
connection dml;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table flush" and
info = "flush tables";
where state = "Waiting for table metadata lock" and
info = "flush tables t1";
--source include/wait_condition.inc
--send select * from t1
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table flush" and
where state = "Waiting for table metadata lock" and
info = "select * from t1";
--source include/wait_condition.inc
--replace_result $ID2 ID2
......
......@@ -500,7 +500,7 @@ connect con1,localhost,root,,test;
LOCK TABLE t2 WRITE;
SET lock_wait_timeout= 1;
FLUSH TABLES;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
FLUSH TABLES t2;
UNLOCK TABLES;
disconnect con1;
connection default;
......
......@@ -609,8 +609,8 @@ LOCK TABLE t1 READ;
--connect (con1,localhost,root,,test)
LOCK TABLE t2 WRITE;
SET lock_wait_timeout= 1;
--error ER_LOCK_WAIT_TIMEOUT
FLUSH TABLES;
FLUSH TABLES t2;
# Cleanup
UNLOCK TABLES;
......
......@@ -531,8 +531,9 @@ connect con3, localhost, root;
connection default;
LOCK TABLE t1 READ;
connection con3;
# Sending:
FLUSH TABLES;
# Sending:
FLUSH TABLES t1;
connection con2;
SELECT * FROM t1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
......
......@@ -931,13 +931,19 @@ connection default;
LOCK TABLE t1 READ;
connection con3;
# first test that flush tables doesn't block
FLUSH TABLES;
# Check the FLUSH TABLES t1 waits until table lock is released
--echo # Sending:
--send FLUSH TABLES
--send FLUSH TABLES t1
connection con2;
let $wait_condition=
SELECT COUNT(*) = 1 FROM information_schema.processlist
WHERE state = "Waiting for table flush" AND info = "FLUSH TABLES";
WHERE state = "Waiting for table metadata lock" AND info = "FLUSH TABLES t1";
--source include/wait_condition.inc
--error ER_LOCK_WAIT_TIMEOUT
SELECT * FROM t1;
......
......@@ -782,7 +782,7 @@ SET DEBUG_SYNC= 'now WAIT_FOR opened';
SET DEBUG_SYNC= 'now SIGNAL dropped';
SET DEBUG_SYNC= 'now WAIT_FOR opened';
# Sending:
FLUSH TABLES;
FLUSH TABLES t1;
connection default;
# Waiting for FLUSH TABLES to be blocked.
SET DEBUG_SYNC= 'now SIGNAL dropped';
......
......@@ -974,12 +974,12 @@ SET DEBUG_SYNC= 'now WAIT_FOR opened';
SET DEBUG_SYNC= 'now SIGNAL dropped';
SET DEBUG_SYNC= 'now WAIT_FOR opened';
--echo # Sending:
--send FLUSH TABLES
--send FLUSH TABLES t1
connection default;
--echo # Waiting for FLUSH TABLES to be blocked.
let $wait_condition= SELECT COUNT(*)=1 FROM information_schema.processlist
WHERE state= 'Waiting for table flush' AND info= 'FLUSH TABLES';
WHERE state= 'Waiting for table metadata lock' AND info= 'FLUSH TABLES t1';
--source include/wait_condition.inc
SET DEBUG_SYNC= 'now SIGNAL dropped';
......
......@@ -2146,10 +2146,11 @@ flush tables t1, t2 with read lock;
connection con1;
# Wait till FLUSH TABLES <list> WITH READ LOCK stops.
set debug_sync='now WAIT_FOR parked';
flush tables;
# Start a statement which will flush all tables and thus
# invalidate table t1 open by FLUSH TABLES <list> WITH READ LOCK.
# Sending:
flush tables;
flush tables t1;
connection default;
# Wait till the above FLUSH TABLES blocks.
# Resume FLUSH TABLES <list> WITH READ LOCK, so it tries to open t2
......
......@@ -2690,17 +2690,20 @@ connection con1;
--echo # Wait till FLUSH TABLES <list> WITH READ LOCK stops.
set debug_sync='now WAIT_FOR parked';
# Simple flush tables should not block
flush tables;
--echo # Start a statement which will flush all tables and thus
--echo # invalidate table t1 open by FLUSH TABLES <list> WITH READ LOCK.
--echo # Sending:
send flush tables;
send flush tables t1;
connection default;
--echo # Wait till the above FLUSH TABLES blocks.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table flush" and
info = "flush tables";
where state = "Waiting for table metadata lock" and
info = "flush tables t1";
--source include/wait_condition.inc
--echo # Resume FLUSH TABLES <list> WITH READ LOCK, so it tries to open t2
......
......@@ -40,7 +40,7 @@ TRUNCATE TABLE m1;
connection con2;
SET DEBUG_SYNC= 'now WAIT_FOR opened';
# Sending:
FLUSH TABLES;
FLUSH TABLES m1;
connection default;
# Waiting for FLUSH TABLES to be blocked.
SET DEBUG_SYNC= 'now SIGNAL dropped';
......
......@@ -81,12 +81,12 @@ SET DEBUG_SYNC= 'open_tables_after_open_and_process_table SIGNAL opened WAIT_FOR
connection con2;
SET DEBUG_SYNC= 'now WAIT_FOR opened';
--echo # Sending:
--send FLUSH TABLES
--send FLUSH TABLES m1
connection default;
--echo # Waiting for FLUSH TABLES to be blocked.
let $wait_condition= SELECT COUNT(*)=1 FROM information_schema.processlist
WHERE state= 'Waiting for table flush' AND info= 'FLUSH TABLES';
WHERE state= 'Waiting for table metadata lock' AND info= 'FLUSH TABLES m1';
--source include/wait_condition.inc
SET DEBUG_SYNC= 'now SIGNAL dropped';
......
......@@ -175,6 +175,7 @@ c1
connect flush,localhost,root,,;
connection flush;
flush tables;
flush table t1;
connect waiter,localhost,root,,;
connection waiter;
connection default;
......@@ -258,10 +259,11 @@ a b
flush tables;
handler t1 read a next;
a b
0 a
2 c
flush tables t1;
handler t1 read a next;
a b
1 b
0 a
flush tables with read lock;
handler t1 read a next;
a b
......
......@@ -179,12 +179,13 @@ handler t1 open;
handler t1 read first;
connect (flush,localhost,root,,);
connection flush;
send flush tables;
flush tables;
send flush table t1;
connect (waiter,localhost,root,,);
connection waiter;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table flush";
where state = "Waiting for table metadata lock";
--source include/wait_condition.inc
connection default;
handler t2 open;
......@@ -282,6 +283,7 @@ handler t1 read a first;
handler t1 read a next;
flush tables;
handler t1 read a next;
flush tables t1;
handler t1 read a next;
flush tables with read lock;
handler t1 read a next;
......
......@@ -3,6 +3,7 @@ LOCK TABLE t1 WRITE;
connect con1,localhost,root,,test;
SET lock_wait_timeout= 2;
FLUSH TABLES;
FLUSH TABLES t1;
connection default;
CALL non_existing_sp;
ERROR 42000: PROCEDURE test.non_existing_sp does not exist
......
......@@ -12,8 +12,8 @@ LOCK TABLE t1 WRITE;
--connect (con1,localhost,root,,test)
SET lock_wait_timeout= 2;
--send
FLUSH TABLES;
FLUSH TABLES;
--send FLUSH TABLES t1
--connection default
--error ER_SP_DOES_NOT_EXIST
CALL non_existing_sp;
......
......@@ -15,7 +15,7 @@
-- source include/master-slave.inc
eval create table t1 (a int) engine=$engine_type;
flush tables;
flush tables t1;
let $MYSQLD_DATADIR= `select @@datadir`;
remove_file $MYSQLD_DATADIR/test/t1.MYI ;
drop table if exists t1;
......
......@@ -147,7 +147,7 @@ let $start_pos= `select @binlog_start_pos`;
connection master;
SET SESSION binlog_annotate_row_events = ON;
INSERT DELAYED INTO test1.t4 VALUES (1,1);
FLUSH TABLES;
FLUSH TABLES test1.t4;
SELECT * FROM test1.t4 ORDER BY a;
sync_slave_with_master;
......
......@@ -10,7 +10,7 @@ eval create table t1(a int not null primary key) engine=$engine_type;
insert delayed into t1 values (1);
insert delayed into t1 values (2);
insert delayed into t1 values (3);
flush tables;
flush tables t1;
SELECT * FROM t1 ORDER BY a;
sync_slave_with_master;
......
include/master-slave.inc
[connection master]
create table t1 (a int) engine=myisam;
flush tables;
flush tables t1;
drop table if exists t1;
Warnings:
Warning 1017 Can't find file: './test/t1.MYI' (errno: 2 "No such file or directory")
......
......@@ -180,7 +180,7 @@ slave-bin.000001 # Rotate 2 # slave-bin.000002;pos=4
connection master;
SET SESSION binlog_annotate_row_events = ON;
INSERT DELAYED INTO test1.t4 VALUES (1,1);
FLUSH TABLES;
FLUSH TABLES test1.t4;
SELECT * FROM test1.t4 ORDER BY a;
a b
1 1
......
......@@ -160,7 +160,7 @@ slave-bin.000001 # Rotate 2 # slave-bin.000002;pos=4
connection master;
SET SESSION binlog_annotate_row_events = ON;
INSERT DELAYED INTO test1.t4 VALUES (1,1);
FLUSH TABLES;
FLUSH TABLES test1.t4;
SELECT * FROM test1.t4 ORDER BY a;
a b
1 1
......
......@@ -5,7 +5,7 @@ create table t1(a int not null primary key) engine=myisam;
insert delayed into t1 values (1);
insert delayed into t1 values (2);
insert delayed into t1 values (3);
flush tables;
flush tables t1;
SELECT * FROM t1 ORDER BY a;
a
1
......
......@@ -122,7 +122,7 @@ select * from t;
a b c d e
10 5 5 5 5
replace delayed t (a,b,d) values (10,6,6);
flush tables;
flush tables t;
check table t;
Table Op Msg_type Msg_text
test.t check status OK
......@@ -130,7 +130,7 @@ select * from t;
a b c d e
10 6 6 6 6
insert delayed t(a,b,d) values (10,6,6) on duplicate key update b=7, d=7;
flush tables;
flush tables t;
check table t;
Table Op Msg_type Msg_text
test.t check status OK
......
......@@ -124,7 +124,7 @@ select * from t;
a b c d e
10 5 5 5 5
replace delayed t (a,b,d) values (10,6,6);
flush tables;
flush tables t;
check table t;
Table Op Msg_type Msg_text
test.t check status OK
......@@ -132,7 +132,7 @@ select * from t;
a b c d e
10 6 6 6 6
insert delayed t(a,b,d) values (10,6,6) on duplicate key update b=7, d=7;
flush tables;
flush tables t;
check table t;
Table Op Msg_type Msg_text
test.t check status OK
......@@ -304,7 +304,7 @@ select * from t;
a b c d e
10 5 5 5 5
replace delayed t (a,b,d) values (10,6,6);
flush tables;
flush tables t;
check table t;
Table Op Msg_type Msg_text
test.t check status OK
......@@ -312,7 +312,7 @@ select * from t;
a b c d e
10 6 6 6 6
insert delayed t(a,b,d) values (10,6,6) on duplicate key update b=7, d=7;
flush tables;
flush tables t;
check table t;
Table Op Msg_type Msg_text
test.t check status OK
......
......@@ -182,16 +182,36 @@ a b c
2 3 y
0 1 y,n
drop table t1,t2;
CREATE TABLE t1 (
SET @old_debug= @@global.debug;
SET @old_debug= @@global.debug;
SET GLOBAL debug_dbug= "+d,write_delay_wakeup";
CREATE TABLE t1 (a int,
ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
tsv TIMESTAMP AS (ADDDATE(ts, INTERVAL 1 DAY)) VIRTUAL
) ENGINE=MyISAM;
INSERT INTO t1 (tsv) VALUES (DEFAULT);
INSERT DELAYED INTO t1 (tsv) VALUES (DEFAULT);
# First test FLUSH TABLES
INSERT INTO t1 (a,tsv) VALUES (1,DEFAULT);
INSERT DELAYED INTO t1 (a,tsv) VALUES (2,DEFAULT);
FLUSH TABLES;
SELECT COUNT(*) > 0 FROM t1;
COUNT(*) > 0
1
# Then test FLUSH TABLES t1;
INSERT INTO t1 (a,tsv) VALUES (3,DEFAULT);
INSERT DELAYED INTO t1 (a,tsv) VALUES (4,DEFAULT);
FLUSH TABLES t1;
SELECT COUNT(*) FROM t1;
COUNT(*)
4
# Then test FLUSH TABLES WITH READ LOCK;
INSERT INTO t1 (a,tsv) VALUES (5,DEFAULT);
INSERT DELAYED INTO t1 (a,tsv) VALUES (6,DEFAULT);
FLUSH TABLES WITH READ LOCK;
SELECT COUNT(*) FROM t1;
COUNT(*)
2
6
set GLOBAL debug_dbug= @old_debug;
unlock tables;
DROP TABLE t1;
#
# MDEV-4823 Server crashes in Item_func_not::fix_fields on
......
......@@ -93,10 +93,10 @@ check table t; select * from t;
insert t(a,b,d) select 10,4,4 on duplicate key update b=5, d=5;
check table t; select * from t;
replace delayed t (a,b,d) values (10,6,6);
flush tables;
flush tables t;
check table t; select * from t;
insert delayed t(a,b,d) values (10,6,6) on duplicate key update b=7, d=7;
flush tables;
flush tables t;
check table t; select * from t;
--write_file $MYSQLTEST_VARDIR/tmp/vblobs.txt
10 8 foo 8 foo
......
--source include/have_ucs2.inc
--source include/have_debug.inc
let $MYSQLD_DATADIR= `select @@datadir`;
......@@ -184,19 +185,35 @@ drop table t1,t2;
# Bug mdev-3938: INSERT DELAYED for a table with virtual columns
#
CREATE TABLE t1 (
SET @old_debug= @@global.debug;
SET @old_debug= @@global.debug;
SET GLOBAL debug_dbug= "+d,write_delay_wakeup";
CREATE TABLE t1 (a int,
ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
tsv TIMESTAMP AS (ADDDATE(ts, INTERVAL 1 DAY)) VIRTUAL
) ENGINE=MyISAM;
INSERT INTO t1 (tsv) VALUES (DEFAULT);
INSERT DELAYED INTO t1 (tsv) VALUES (DEFAULT);
--echo # First test FLUSH TABLES
INSERT INTO t1 (a,tsv) VALUES (1,DEFAULT);
INSERT DELAYED INTO t1 (a,tsv) VALUES (2,DEFAULT);
FLUSH TABLES;
# Count may be 1 or 2, depending on FLUSH happened before or after delayed
SELECT COUNT(*) > 0 FROM t1;
--echo # Then test FLUSH TABLES t1;
INSERT INTO t1 (a,tsv) VALUES (3,DEFAULT);
INSERT DELAYED INTO t1 (a,tsv) VALUES (4,DEFAULT);
FLUSH TABLES t1;
SELECT COUNT(*) FROM t1;
--echo # Then test FLUSH TABLES WITH READ LOCK;
INSERT INTO t1 (a,tsv) VALUES (5,DEFAULT);
INSERT DELAYED INTO t1 (a,tsv) VALUES (6,DEFAULT);
FLUSH TABLES WITH READ LOCK;
SELECT COUNT(*) FROM t1;
set GLOBAL debug_dbug= @old_debug;
unlock tables;
DROP TABLE t1;
--echo #
......
......@@ -1862,11 +1862,8 @@ MDL_context::try_acquire_lock_impl(MDL_request *mdl_request,
MDL_ticket *ticket;
enum_mdl_duration found_duration;
DBUG_ASSERT(mdl_request->type != MDL_EXCLUSIVE ||
is_lock_owner(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE));
DBUG_ASSERT(mdl_request->ticket == NULL);
/* Don't take chances in production. */
DBUG_ASSERT(mdl_request->ticket == NULL);
mdl_request->ticket= NULL;
/*
......
......@@ -306,49 +306,6 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
}
/*
Close all tables which aren't in use by any thread
@param thd Thread context
@param tables List of tables to remove from the cache
@param wait_for_refresh Wait for a impending flush
@param timeout Timeout for waiting for flush to be completed.
@note THD can be NULL, but then wait_for_refresh must be FALSE
and tables must be NULL.
@note When called as part of FLUSH TABLES WITH READ LOCK this function
ignores metadata locks held by other threads. In order to avoid
situation when FLUSH TABLES WITH READ LOCK sneaks in at the moment
when some write-locked table is being reopened (by FLUSH TABLES or
ALTER TABLE) we have to rely on additional global shared metadata
lock taken by thread trying to obtain global read lock.
*/
struct close_cached_tables_arg
{
tdc_version_t refresh_version;
TDC_element *element;
};
static my_bool close_cached_tables_callback(TDC_element *element,
close_cached_tables_arg *arg)
{
mysql_mutex_lock(&element->LOCK_table_share);
if (element->share && element->flushed &&
element->version < arg->refresh_version)
{
/* wait_for_old_version() will unlock mutex and free share */
arg->element= element;
return TRUE;
}
mysql_mutex_unlock(&element->LOCK_table_share);
return FALSE;
}
/**
Close all tables that are not in use in table definition cache
......@@ -377,38 +334,37 @@ void purge_tables(bool purge_flag)
}
/**
close_cached_tables
This function has two separate usages:
1) Close not used tables in the table cache to free memory
2) Close a list of tables and wait until they are not used anymore. This
is used mainly when preparing a table for export.
If there are locked tables, they are closed and reopened before
function returns. This is done to ensure that table files will be closed
by all threads and thus external copyable when FLUSH TABLES returns.
*/
bool close_cached_tables(THD *thd, TABLE_LIST *tables,
bool wait_for_refresh, ulong timeout)
{
bool result= FALSE;
struct timespec abstime;
tdc_version_t refresh_version;
DBUG_ENTER("close_cached_tables");
DBUG_ASSERT(thd || (!wait_for_refresh && !tables));
refresh_version= tdc_increment_refresh_version();
DBUG_ASSERT(wait_for_refresh || !tables);
if (!tables)
purge_tables(true);
else
{
bool found=0;
for (TABLE_LIST *table= tables; table; table= table->next_local)
{
/* tdc_remove_table() also sets TABLE_SHARE::version to 0. */
found|= tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db.str,
table->table_name.str, TRUE);
}
if (!found)
wait_for_refresh=0; // Nothing to wait for
/* Free tables that are not used */
purge_tables(false);
if (!wait_for_refresh)
DBUG_RETURN(false);
}
DBUG_PRINT("info", ("open table definitions: %d",
(int) tdc_records()));
if (!wait_for_refresh)
DBUG_RETURN(result);
if (thd->locked_tables_mode)
{
/*
......@@ -419,8 +375,9 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
*/
TABLE_LIST *tables_to_reopen= (tables ? tables :
thd->locked_tables_list.locked_tables());
bool result= false;
/* Close open HANDLER instances to avoid self-deadlock. */
/* close open HANDLER for this thread to allow table to be closed */
mysql_ha_flush_tables(thd, tables_to_reopen);
for (TABLE_LIST *table_list= tables_to_reopen; table_list;
......@@ -435,64 +392,15 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
if (! table)
continue;
if (wait_while_table_is_used(thd, table,
HA_EXTRA_PREPARE_FOR_FORCED_CLOSE))
{
result= TRUE;
goto err_with_reopen;
}
close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
}
}
/* Wait until all threads have closed all the tables we are flushing. */
DBUG_PRINT("info", ("Waiting for other threads to close their open tables"));
/*
To a self-deadlock or deadlocks with other FLUSH threads
waiting on our open HANDLERs, we have to flush them.
*/
mysql_ha_flush(thd);
DEBUG_SYNC(thd, "after_flush_unlock");
if (!tables)
{
int r= 0;
close_cached_tables_arg argument;
argument.refresh_version= refresh_version;
set_timespec(abstime, timeout);
while (!thd->killed &&
(r= tdc_iterate(thd,
(my_hash_walk_action) close_cached_tables_callback,
&argument)) == 1 &&
!argument.element->share->wait_for_old_version(thd, &abstime,
MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))
/* no-op */;
if (r)
result= TRUE;
}
else
{
for (TABLE_LIST *table= tables; table; table= table->next_local)
{
if (thd->killed)
break;
if (tdc_wait_for_old_version(thd, table->db.str, table->table_name.str,
timeout,
MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL,
refresh_version))
if (thd->mdl_context.upgrade_shared_lock(table->mdl_ticket, MDL_EXCLUSIVE,
timeout))
{
result= TRUE;
result= true;
break;
}
table->file->extra(HA_EXTRA_PREPARE_FOR_FORCED_CLOSE);
close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
}
}
err_with_reopen:
if (thd->locked_tables_mode)
{
/*
No other thread has the locked tables open; reopen them and get the
old locks. This should always succeed (unless some external process
......@@ -508,8 +416,40 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
*/
for (TABLE *tab= thd->open_tables; tab; tab= tab->next)
tab->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
DBUG_RETURN(result);
}
DBUG_RETURN(result);
else if (tables)
{
/*
Get an explicit MDL lock for all requested tables to ensure they are
not used by any other thread
*/
MDL_request_list mdl_requests;
DBUG_PRINT("info", ("Waiting for other threads to close their open tables"));
DEBUG_SYNC(thd, "after_flush_unlock");
/* close open HANDLER for this thread to allow table to be closed */
mysql_ha_flush_tables(thd, tables);
for (TABLE_LIST *table= tables; table; table= table->next_local)
{
MDL_request *mdl_request= new (thd->mem_root) MDL_request;
if (mdl_request == NULL)
DBUG_RETURN(true);
mdl_request->init(&table->mdl_request.key, MDL_EXCLUSIVE, MDL_STATEMENT);
mdl_requests.push_front(mdl_request);
}
if (thd->mdl_context.acquire_locks(&mdl_requests, timeout))
DBUG_RETURN(true);
for (TABLE_LIST *table= tables; table; table= table->next_local)
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db.str,
table->table_name.str, false);
}
DBUG_RETURN(false);
}
......@@ -688,8 +628,17 @@ static my_bool close_cached_connection_tables_callback(
}
/**
Close cached connections
@return false ok
@return true If there was an error from closed_cached_connection_tables or
if there was any open connections that we had to force closed
*/
bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connection)
{
bool res= false;
close_cached_connection_tables_arg argument;
DBUG_ENTER("close_cached_connections");
DBUG_ASSERT(thd);
......@@ -703,9 +652,13 @@ bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connection)
&argument))
DBUG_RETURN(true);
DBUG_RETURN(argument.tables ?
close_cached_tables(thd, argument.tables, FALSE, LONG_TIMEOUT) :
false);
for (TABLE_LIST *table= argument.tables; table; table= table->next_local)
res|= tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
table->db.str,
table->table_name.str, TRUE);
/* Return true if we found any open connections */
DBUG_RETURN(res);
}
......
......@@ -329,25 +329,21 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
}
#ifdef WITH_WSREP
if (thd && thd->wsrep_applier)
/* In case of applier thread, do not call flush tables */
if (!thd || !thd->wsrep_applier)
#endif /* WITH_WSREP */
{
/*
In case of applier thread, do not wait for table share(s) to be
removed from table definition cache.
*/
options|= REFRESH_FAST;
}
#endif
if (close_cached_tables(thd, tables,
((options & REFRESH_FAST) ? FALSE : TRUE),
(thd ? thd->variables.lock_wait_timeout :
LONG_TIMEOUT)))
{
/*
NOTE: my_error() has been already called by reopen_tables() within
close_cached_tables().
*/
result= 1;
if (close_cached_tables(thd, tables,
((options & REFRESH_FAST) ? FALSE : TRUE),
(thd ? thd->variables.lock_wait_timeout :
LONG_TIMEOUT)))
{
/*
NOTE: my_error() has been already called by reopen_tables() within
close_cached_tables().
*/
result= 1;
}
}
}
my_dbopt_cleanup();
......
......@@ -1094,6 +1094,7 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
TABLE *table;
TDC_element *element;
uint my_refs= 1;
bool res= false;
DBUG_ENTER("tdc_remove_table");
DBUG_PRINT("enter",("name: %s remove_type: %d", table_name, remove_type));
......@@ -1101,7 +1102,6 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
MDL_EXCLUSIVE));
mysql_mutex_lock(&LOCK_unused_shares);
if (!(element= tdc_lock_share(thd, db, table_name)))
{
......@@ -1123,7 +1123,7 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
mysql_mutex_unlock(&LOCK_unused_shares);
tdc_delete_share_from_hash(element);
DBUG_RETURN(true);
DBUG_RETURN(false);
}
mysql_mutex_unlock(&LOCK_unused_shares);
......@@ -1189,10 +1189,16 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
#endif
mysql_mutex_unlock(&element->LOCK_table_share);
}
else
{
mysql_mutex_lock(&element->LOCK_table_share);
res= element->ref_count > 1;
mysql_mutex_unlock(&element->LOCK_table_share);
}
tdc_release_share(element->share);
DBUG_RETURN(true);
DBUG_RETURN(res);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment