Commit 163b34fe authored by Monty's avatar Monty

Optimize flush tables with read lock (FTWRL) to not wait for select's

Part of MDEV-5336 Implement LOCK FOR BACKUP

The idea is that instead of waiting in close_cached_tables() for all
tables to be closed, we instead call flush_tables() that does:
- Flush not used objects in table cache to free memory
- Collect all tables that are open
- Call HA_EXTRA_FLUSH on the objects, to get them into "closed state"
- Added HA_EXTRA_FLUSH support to archive and CSV
- Added multi-user protection to HA_EXTRA_FLUSH in MyISAM and Aria

The benefit compared to old code is:
- FTWRL doesn't have to wait for long running read operations or
  open HANDLER's
parent 306b7a22
...@@ -472,10 +472,7 @@ create table t1 (i int); ...@@ -472,10 +472,7 @@ create table t1 (i int);
create table t2 (i int); create table t2 (i int);
handler t1 open; handler t1 open;
connection con1; connection con1;
# Sending:
flush tables with read lock; flush tables with read lock;
connection con2;
# Wait until FTWRL starts waiting for 't1' to be closed.
connection default; connection default;
# The below statement should not cause deadlock. # The below statement should not cause deadlock.
# Sending: # Sending:
...@@ -483,8 +480,6 @@ insert into t2 values (1); ...@@ -483,8 +480,6 @@ insert into t2 values (1);
connection con2; connection con2;
# Wait until INSERT starts to wait for FTWRL to go away. # Wait until INSERT starts to wait for FTWRL to go away.
connection con1; connection con1;
# FTWRL should be able to continue now.
# Reap FTWRL.
unlock tables; unlock tables;
connection default; connection default;
# Reap INSERT. # Reap INSERT.
......
...@@ -566,17 +566,7 @@ create table t2 (i int); ...@@ -566,17 +566,7 @@ create table t2 (i int);
handler t1 open; handler t1 open;
connection con1; connection con1;
--echo # Sending: flush tables with read lock;
--send flush tables with read lock
connection con2;
--echo # Wait until FTWRL starts waiting for 't1' to be closed.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table flush"
and info = "flush tables with read lock";
--source include/wait_condition.inc
connection default; connection default;
--echo # The below statement should not cause deadlock. --echo # The below statement should not cause deadlock.
--echo # Sending: --echo # Sending:
...@@ -591,9 +581,6 @@ let $wait_condition= ...@@ -591,9 +581,6 @@ let $wait_condition=
--source include/wait_condition.inc --source include/wait_condition.inc
connection con1; connection con1;
--echo # FTWRL should be able to continue now.
--echo # Reap FTWRL.
--reap
unlock tables; unlock tables;
connection default; connection default;
......
...@@ -652,6 +652,7 @@ connection default; ...@@ -652,6 +652,7 @@ connection default;
# 14.2) FLUSH TABLES <list> WITH READ LOCK is not blocked by # 14.2) FLUSH TABLES <list> WITH READ LOCK is not blocked by
# active FTWRL. But since the latter keeps tables open # active FTWRL. But since the latter keeps tables open
# FTWRL is blocked by FLUSH TABLES <list> WITH READ LOCK. # FTWRL is blocked by FLUSH TABLES <list> WITH READ LOCK.
# Fixed by MDEV-5336
flush tables with read lock; flush tables with read lock;
# FT <list> WRL is allowed under FTWRL at the moment. # FT <list> WRL is allowed under FTWRL at the moment.
# It does not make much sense though. # It does not make much sense though.
...@@ -668,12 +669,9 @@ connection default; ...@@ -668,12 +669,9 @@ connection default;
flush tables t1_base, t2_base with read lock; flush tables t1_base, t2_base with read lock;
connection con1; connection con1;
flush tables with read lock; flush tables with read lock;
connection con2;
# Wait until FTWRL is blocked.
connection default; connection default;
unlock tables; unlock tables;
connection con1; connection con1;
# Reap FTWRL.
unlock tables; unlock tables;
connection default; connection default;
# #
...@@ -1677,3 +1675,31 @@ disconnect con1; ...@@ -1677,3 +1675,31 @@ disconnect con1;
disconnect con2; disconnect con2;
disconnect con3; disconnect con3;
set global sql_mode=default; set global sql_mode=default;
#
# Deadlock between FTWRL under open handler and DDL/LOCK TABLES
#
CREATE TABLE t1(a INT);
HANDLER t1 OPEN;
#
connect con1,localhost,root,,;
SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL ready';
LOCK TABLE t1 WRITE;
#
# we need to do it in a separate connection,
# because SET DEBUG_SYNC call open_tables()/mysql_ha_flush() :(
connect con2,localhost,root,,;
SET DEBUG_SYNC= 'now WAIT_FOR ready';
disconnect con2;
#
connection default;
FLUSH TABLES WITH READ LOCK;
#
connection con1;
UNLOCK TABLES;
disconnect con1;
#
connection default;
UNLOCK TABLES;
HANDLER t1 CLOSE;
DROP TABLE t1;
SET DEBUG_SYNC= 'RESET';
...@@ -800,6 +800,8 @@ connection default; ...@@ -800,6 +800,8 @@ connection default;
--echo # 14.2) FLUSH TABLES <list> WITH READ LOCK is not blocked by --echo # 14.2) FLUSH TABLES <list> WITH READ LOCK is not blocked by
--echo # active FTWRL. But since the latter keeps tables open --echo # active FTWRL. But since the latter keeps tables open
--echo # FTWRL is blocked by FLUSH TABLES <list> WITH READ LOCK. --echo # FTWRL is blocked by FLUSH TABLES <list> WITH READ LOCK.
--echo # Fixed by MDEV-5336
flush tables with read lock; flush tables with read lock;
--echo # FT <list> WRL is allowed under FTWRL at the moment. --echo # FT <list> WRL is allowed under FTWRL at the moment.
--echo # It does not make much sense though. --echo # It does not make much sense though.
...@@ -815,19 +817,10 @@ unlock tables; ...@@ -815,19 +817,10 @@ unlock tables;
connection default; connection default;
flush tables t1_base, t2_base with read lock; flush tables t1_base, t2_base with read lock;
connection $con_aux1; connection $con_aux1;
--send flush tables with read lock flush tables with read lock;
connection $con_aux2;
--echo # Wait until FTWRL is blocked.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table flush" and
info = "flush tables with read lock";
--source include/wait_condition.inc
connection default; connection default;
unlock tables; unlock tables;
connection $con_aux1; connection $con_aux1;
--echo # Reap FTWRL.
--reap
unlock tables; unlock tables;
connection default; connection default;
...@@ -2022,3 +2015,40 @@ set global sql_mode=default; ...@@ -2022,3 +2015,40 @@ set global sql_mode=default;
# Check that all connections opened by test cases in this file are really # 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. # gone so execution of other tests won't be affected by their presence.
--source include/wait_until_count_sessions.inc --source include/wait_until_count_sessions.inc
--echo #
--echo # Deadlock between FTWRL under open handler and DDL/LOCK TABLES
--echo #
CREATE TABLE t1(a INT);
HANDLER t1 OPEN;
--echo #
connect (con1,localhost,root,,);
SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL ready';
--send LOCK TABLE t1 WRITE
--echo #
--echo # we need to do it in a separate connection,
--echo # because SET DEBUG_SYNC call open_tables()/mysql_ha_flush() :(
connect (con2,localhost,root,,);
SET DEBUG_SYNC= 'now WAIT_FOR ready';
disconnect con2;
--echo #
connection default;
--send FLUSH TABLES WITH READ LOCK
--echo #
connection con1;
reap;
UNLOCK TABLES;
disconnect con1;
--echo #
connection default;
reap;
UNLOCK TABLES;
HANDLER t1 CLOSE;
DROP TABLE t1;
SET DEBUG_SYNC= 'RESET';
...@@ -3055,7 +3055,7 @@ disconnect con3; ...@@ -3055,7 +3055,7 @@ disconnect con3;
# #
CREATE TABLE t1(a INT) ENGINE=InnoDB; CREATE TABLE t1(a INT) ENGINE=InnoDB;
SET debug_sync='open_tables_after_open_and_process_table SIGNAL ready WAIT_FOR go'; SET debug_sync='open_tables_after_open_and_process_table SIGNAL ready WAIT_FOR go';
SELECT * FROM t1; INSERT INTO t1 values (1);
connect con1,localhost,root,,; connect con1,localhost,root,,;
SET debug_sync='now WAIT_FOR ready'; SET debug_sync='now WAIT_FOR ready';
SET lock_wait_timeout=1; SET lock_wait_timeout=1;
...@@ -3063,7 +3063,21 @@ FLUSH TABLES WITH READ LOCK; ...@@ -3063,7 +3063,21 @@ FLUSH TABLES WITH READ LOCK;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SET debug_sync='now SIGNAL go'; SET debug_sync='now SIGNAL go';
connection default; connection default;
# After MDEV-5536, SELECT will not block FLUSH TABLES
SET debug_sync='RESET';
SET debug_sync='open_tables_after_open_and_process_table SIGNAL ready WAIT_FOR go';
SELECT * FROM t1;
connection con1;
SET debug_sync='now WAIT_FOR ready';
SET lock_wait_timeout=1;
FLUSH TABLES WITH READ LOCK;
SET debug_sync='now SIGNAL go';
connection default;
a a
1
connection con1;
unlock tables;
connection default;
SET debug_sync='RESET'; SET debug_sync='RESET';
DROP TABLE t1; DROP TABLE t1;
disconnect con1; disconnect con1;
...@@ -4079,9 +4079,10 @@ disconnect con3; ...@@ -4079,9 +4079,10 @@ disconnect con3;
--echo # MDEV-12620 - set lock_wait_timeout = 1;flush tables with read lock; --echo # MDEV-12620 - set lock_wait_timeout = 1;flush tables with read lock;
--echo # lock not released after timeout --echo # lock not released after timeout
--echo # --echo #
CREATE TABLE t1(a INT) ENGINE=InnoDB; CREATE TABLE t1(a INT) ENGINE=InnoDB;
SET debug_sync='open_tables_after_open_and_process_table SIGNAL ready WAIT_FOR go'; SET debug_sync='open_tables_after_open_and_process_table SIGNAL ready WAIT_FOR go';
send SELECT * FROM t1; send INSERT INTO t1 values (1);
connect (con1,localhost,root,,); connect (con1,localhost,root,,);
SET debug_sync='now WAIT_FOR ready'; SET debug_sync='now WAIT_FOR ready';
...@@ -4093,12 +4094,31 @@ SET debug_sync='now SIGNAL go'; ...@@ -4093,12 +4094,31 @@ SET debug_sync='now SIGNAL go';
connection default; connection default;
reap; reap;
--echo # After MDEV-5536, SELECT will not block FLUSH TABLES
SET debug_sync='RESET';
SET debug_sync='open_tables_after_open_and_process_table SIGNAL ready WAIT_FOR go';
send SELECT * FROM t1;
connection con1;
SET debug_sync='now WAIT_FOR ready';
# lock_wait_timeout should be 0 in 10.3, so that we don't have to wait at all
SET lock_wait_timeout=1;
FLUSH TABLES WITH READ LOCK;
SET debug_sync='now SIGNAL go';
connection default;
reap;
connection con1;
unlock tables;
connection default;
SET debug_sync='RESET'; SET debug_sync='RESET';
DROP TABLE t1; DROP TABLE t1;
disconnect con1; disconnect con1;
# Check that all connections opened by test cases in this file are really # 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. # gone so execution of other tests won't be affected by their presence.
--source include/wait_until_count_sessions.inc --source include/wait_until_count_sessions.inc
CREATE TABLE t1(a INT) ENGINE=archive;
INSERT INTO t1 VALUES(1);
connect con1, localhost, root;
LOCK TABLE t1 READ;
connection default;
FLUSH TABLES WITH READ LOCK;
UNLOCK TABLES;
# Must return 1 row
SELECT * FROM t2;
a
1
SELECT * FROM t1;
a
1
connection con1;
UNLOCK TABLES;
connection default;
DROP TABLE t1, t2;
--source include/have_archive.inc
let $MYSQLD_DATADIR= `SELECT @@datadir`;
CREATE TABLE t1(a INT) ENGINE=archive;
INSERT INTO t1 VALUES(1);
# Works correct if we uncomment next row
#FLUSH TABLE t1;
connect(con1, localhost, root);
LOCK TABLE t1 READ;
connection default;
FLUSH TABLES WITH READ LOCK;
copy_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/t2.frm;
copy_file $MYSQLD_DATADIR/test/t1.ARZ $MYSQLD_DATADIR/test/t2.ARZ;
UNLOCK TABLES;
--echo # Must return 1 row
SELECT * FROM t2;
SELECT * FROM t1;
connection con1;
UNLOCK TABLES;
connection default;
DROP TABLE t1, t2;
CREATE TABLE t1(a INT NOT NULL) ENGINE=csv;
INSERT INTO t1 VALUES(1);
connect con1, localhost, root;
LOCK TABLE t1 READ;
connection default;
FLUSH TABLES WITH READ LOCK;
UNLOCK TABLES;
# Must return 1 row
SELECT * FROM t2;
a
1
SELECT * FROM t1;
a
1
connection con1;
UNLOCK TABLES;
connection default;
INSERT INTO t2 VALUES(2);
INSERT INTO t2 VALUES(2);
SELECT * from t1,t2;
a a
1 1
1 2
1 2
DROP TABLE t1, t2;
--source include/have_csv.inc
let $MYSQLD_DATADIR= `SELECT @@datadir`;
CREATE TABLE t1(a INT NOT NULL) ENGINE=csv;
INSERT INTO t1 VALUES(1);
# works correct if uncommented
#FLUSH TABLE t1;
connect(con1, localhost, root);
LOCK TABLE t1 READ;
connection default;
FLUSH TABLES WITH READ LOCK;
copy_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/t2.frm;
copy_file $MYSQLD_DATADIR/test/t1.CSV $MYSQLD_DATADIR/test/t2.CSV;
copy_file $MYSQLD_DATADIR/test/t1.CSM $MYSQLD_DATADIR/test/t2.CSM;
UNLOCK TABLES;
--echo # Must return 1 row
SELECT * FROM t2;
SELECT * FROM t1;
connection con1;
UNLOCK TABLES;
connection default;
INSERT INTO t2 VALUES(2);
INSERT INTO t2 VALUES(2);
SELECT * from t1,t2;
DROP TABLE t1, t2;
...@@ -1485,7 +1485,7 @@ handler t2 open; ...@@ -1485,7 +1485,7 @@ handler t2 open;
flush tables with read lock; flush tables with read lock;
handler t1 read next; handler t1 read next;
a b a b
1 1 2 1
select a from t3; select a from t3;
a a
1 1
......
...@@ -1484,7 +1484,7 @@ handler t2 open; ...@@ -1484,7 +1484,7 @@ handler t2 open;
flush tables with read lock; flush tables with read lock;
handler t1 read next; handler t1 read next;
a b a b
1 1 2 1
select a from t3; select a from t3;
a a
1 1
......
...@@ -1489,7 +1489,7 @@ handler t2 open; ...@@ -1489,7 +1489,7 @@ handler t2 open;
flush tables with read lock; flush tables with read lock;
handler t1 read next; handler t1 read next;
a b a b
1 1 2 1
select a from t3; select a from t3;
a a
1 1
......
...@@ -1485,7 +1485,7 @@ handler t2 open; ...@@ -1485,7 +1485,7 @@ handler t2 open;
flush tables with read lock; flush tables with read lock;
handler t1 read next; handler t1 read next;
a b a b
1 1 2 1
select a from t3; select a from t3;
a a
1 1
......
...@@ -77,6 +77,7 @@ ...@@ -77,6 +77,7 @@
#include "sql_base.h" // close_tables_for_reopen #include "sql_base.h" // close_tables_for_reopen
#include "sql_parse.h" // is_log_table_write_query #include "sql_parse.h" // is_log_table_write_query
#include "sql_acl.h" // SUPER_ACL #include "sql_acl.h" // SUPER_ACL
#include "sql_handler.h"
#include <hash.h> #include <hash.h>
#include "wsrep_mysqld.h" #include "wsrep_mysqld.h"
...@@ -1015,6 +1016,8 @@ bool Global_read_lock::lock_global_read_lock(THD *thd) ...@@ -1015,6 +1016,8 @@ bool Global_read_lock::lock_global_read_lock(THD *thd)
{ {
MDL_request mdl_request; MDL_request mdl_request;
mysql_ha_cleanup_no_free(thd);
DBUG_ASSERT(! thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "", DBUG_ASSERT(! thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
MDL_SHARED)); MDL_SHARED));
mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED, MDL_EXPLICIT); mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED, MDL_EXPLICIT);
......
...@@ -349,6 +349,34 @@ static my_bool close_cached_tables_callback(TDC_element *element, ...@@ -349,6 +349,34 @@ static my_bool close_cached_tables_callback(TDC_element *element,
} }
/**
Close all tables that are not in use in table definition cache
@param purge_flag Argument for tc_purge. true if we should force all
shares to be deleted. false if it's enough to just
evict those that are not in use.
*/
void purge_tables(bool purge_flag)
{
/*
Force close of all open tables.
Note that code in TABLE_SHARE::wait_for_old_version() assumes that
incrementing of refresh_version is followed by purge of unused table
shares.
*/
kill_delayed_threads();
/*
Get rid of all unused TABLE and TABLE_SHARE instances. By doing
this we automatically close all tables which were marked as "old".
*/
tc_purge(purge_flag);
/* Free table shares which were not freed implicitly by loop above. */
tdc_purge(true);
}
bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool close_cached_tables(THD *thd, TABLE_LIST *tables,
bool wait_for_refresh, ulong timeout) bool wait_for_refresh, ulong timeout)
{ {
...@@ -361,23 +389,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, ...@@ -361,23 +389,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
refresh_version= tdc_increment_refresh_version(); refresh_version= tdc_increment_refresh_version();
if (!tables) if (!tables)
{ purge_tables(true);
/*
Force close of all open tables.
Note that code in TABLE_SHARE::wait_for_old_version() assumes that
incrementing of refresh_version is followed by purge of unused table
shares.
*/
kill_delayed_threads();
/*
Get rid of all unused TABLE and TABLE_SHARE instances. By doing
this we automatically close all tables which were marked as "old".
*/
tc_purge(true);
/* Free table shares which were not freed implicitly by loop above. */
tdc_purge(true);
}
else else
{ {
bool found=0; bool found=0;
...@@ -501,6 +513,128 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, ...@@ -501,6 +513,128 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
} }
/**
Collect all shares that has open tables
*/
struct tc_collect_arg
{
DYNAMIC_ARRAY shares;
};
static my_bool tc_collect_used_shares(TDC_element *element,
tc_collect_arg *arg)
{
my_bool result= FALSE;
DYNAMIC_ARRAY *shares= &arg->shares;
mysql_mutex_lock(&element->LOCK_table_share);
if (element->ref_count > 0 && !element->share->is_view)
{
DBUG_ASSERT(element->share);
element->ref_count++; // Protect against delete
if (push_dynamic(shares,(uchar*) &element->share))
result= TRUE;
}
mysql_mutex_unlock(&element->LOCK_table_share);
return result;
}
/**
Flush cached table as part of global read lock
@param thd
@param flag What type of tables should be flushed
@return 0 ok
@return 1 error
After we get the list of table shares, we will call flush on all
possible tables, even if some flush fails.
*/
bool flush_tables(THD *thd)
{
bool result= TRUE;
uint open_errors= 0;
tc_collect_arg collect_arg;
TABLE *tmp_table;
DBUG_ENTER("flush_tables");
purge_tables(false); /* Flush unused tables and shares */
/*
Loop over all shares and collect shares that have open tables
TODO:
Optimize this to only collect shares that have been used for
write after last time all tables was closed.
*/
if (!(tmp_table= (TABLE*) my_malloc(sizeof(*tmp_table),
MYF(MY_WME | MY_THREAD_SPECIFIC))))
DBUG_RETURN(1);
my_init_dynamic_array(&collect_arg.shares, sizeof(TABLE_SHARE*), 100, 100,
MYF(0));
if (tdc_iterate(thd, (my_hash_walk_action) tc_collect_used_shares,
&collect_arg, true))
{
/* Release already collected shares */
for (uint i= 0 ; i < collect_arg.shares.elements ; i++)
{
TABLE_SHARE *share= *dynamic_element(&collect_arg.shares, i,
TABLE_SHARE**);
tdc_release_share(share);
}
goto err;
}
/* Call HA_EXTRA_FLUSH on all found shares */
for (uint i= 0 ; i < collect_arg.shares.elements ; i++)
{
TABLE_SHARE *share= *dynamic_element(&collect_arg.shares, i,
TABLE_SHARE**);
TABLE *table= tc_acquire_table(thd, share->tdc);
if (table)
{
(void) table->file->extra(HA_EXTRA_FLUSH);
tc_release_table(table);
}
else
{
/*
HA_OPEN_FOR_ALTER is used to allow us to open the table even if
TABLE_SHARE::incompatible_version is set.
*/
if (!open_table_from_share(thd, share, &empty_clex_str,
HA_OPEN_KEYFILE, 0,
HA_OPEN_FOR_ALTER,
tmp_table, FALSE,
NULL))
{
(void) tmp_table->file->extra(HA_EXTRA_FLUSH);
/*
We don't put the table into the TDC as the table was not fully
opened (we didn't open triggers)
*/
closefrm(tmp_table);
}
else
open_errors++;
}
tdc_release_share(share);
}
result= open_errors ? TRUE : FALSE;
DBUG_PRINT("note", ("open_errors: %u", open_errors));
err:
my_free(tmp_table);
delete_dynamic(&collect_arg.shares);
DBUG_RETURN(result);
}
/** /**
Close all tables which match specified connection string or Close all tables which match specified connection string or
if specified string is NULL, then any table with a connection string. if specified string is NULL, then any table with a connection string.
......
...@@ -290,6 +290,8 @@ void close_log_table(THD *thd, Open_tables_backup *backup); ...@@ -290,6 +290,8 @@ void close_log_table(THD *thd, Open_tables_backup *backup);
bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool close_cached_tables(THD *thd, TABLE_LIST *tables,
bool wait_for_refresh, ulong timeout); bool wait_for_refresh, ulong timeout);
void purge_tables(bool purge_flag);
bool flush_tables(THD *thd);
bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connect_string); bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connect_string);
void close_all_tables_for_name(THD *thd, TABLE_SHARE *share, void close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
ha_extra_function extra, ha_extra_function extra,
......
...@@ -1196,10 +1196,10 @@ void mysql_ha_flush(THD *thd) ...@@ -1196,10 +1196,10 @@ void mysql_ha_flush(THD *thd)
@note Broadcasts refresh if it closed a table with old version. @note Broadcasts refresh if it closed a table with old version.
*/ */
void mysql_ha_cleanup(THD *thd) void mysql_ha_cleanup_no_free(THD *thd)
{ {
SQL_HANDLER *hash_tables; SQL_HANDLER *hash_tables;
DBUG_ENTER("mysql_ha_cleanup"); DBUG_ENTER("mysql_ha_cleanup_no_free");
for (uint i= 0; i < thd->handler_tables_hash.records; i++) for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{ {
...@@ -1207,9 +1207,15 @@ void mysql_ha_cleanup(THD *thd) ...@@ -1207,9 +1207,15 @@ void mysql_ha_cleanup(THD *thd)
if (hash_tables->table) if (hash_tables->table)
mysql_ha_close_table(hash_tables); mysql_ha_close_table(hash_tables);
} }
DBUG_VOID_RETURN;
}
my_hash_free(&thd->handler_tables_hash);
void mysql_ha_cleanup(THD *thd)
{
DBUG_ENTER("mysql_ha_cleanup");
mysql_ha_cleanup_no_free(thd);
my_hash_free(&thd->handler_tables_hash);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
......
...@@ -73,6 +73,7 @@ bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes, const char *, ...@@ -73,6 +73,7 @@ bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes, const char *,
void mysql_ha_flush(THD *thd); void mysql_ha_flush(THD *thd);
void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables); void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables);
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables); void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables);
void mysql_ha_cleanup_no_free(THD *thd);
void mysql_ha_cleanup(THD *thd); void mysql_ha_cleanup(THD *thd);
void mysql_ha_set_explicit_lock_duration(THD *thd); void mysql_ha_set_explicit_lock_duration(THD *thd);
void mysql_ha_rm_temporary_tables(THD *thd); void mysql_ha_rm_temporary_tables(THD *thd);
......
...@@ -2131,6 +2131,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -2131,6 +2131,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
DBUG_EXECUTE_IF("simulate_detached_thread_refresh", debug_simulate= TRUE;); DBUG_EXECUTE_IF("simulate_detached_thread_refresh", debug_simulate= TRUE;);
if (debug_simulate) if (debug_simulate)
{ {
/* This code doesn't work under FTWRL */
DBUG_ASSERT(! (options & REFRESH_READ_LOCK));
/* /*
Simulate a reload without a attached thread session. Simulate a reload without a attached thread session.
Provides a environment similar to that of when the Provides a environment similar to that of when the
......
...@@ -231,6 +231,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, ...@@ -231,6 +231,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
{ {
if ((options & REFRESH_READ_LOCK) && thd) if ((options & REFRESH_READ_LOCK) && thd)
{ {
DBUG_ASSERT(!(options & REFRESH_FAST) && !tables);
/* /*
On the first hand we need write lock on the tables to be flushed, On the first hand we need write lock on the tables to be flushed,
on the other hand we must not try to aspire a global read lock on the other hand we must not try to aspire a global read lock
...@@ -249,9 +250,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, ...@@ -249,9 +250,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
tmp_write_to_binlog= 0; tmp_write_to_binlog= 0;
if (thd->global_read_lock.lock_global_read_lock(thd)) if (thd->global_read_lock.lock_global_read_lock(thd))
return 1; // Killed return 1; // Killed
if (close_cached_tables(thd, tables, if (flush_tables(thd))
((options & REFRESH_FAST) ? FALSE : TRUE),
thd->variables.lock_wait_timeout))
{ {
/* /*
NOTE: my_error() has been already called by reopen_tables() within NOTE: my_error() has been already called by reopen_tables() within
...@@ -274,11 +273,9 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, ...@@ -274,11 +273,9 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
make_global_read_lock_block_commit(thd) above since they could have make_global_read_lock_block_commit(thd) above since they could have
modified the tables too. modified the tables too.
*/ */
if (WSREP(thd) && if (WSREP(thd) && flush_tables(thd))
close_cached_tables(thd, tables, (options & REFRESH_FAST) ? result= 1;
FALSE : TRUE, TRUE)) }
result= 1;
}
else else
{ {
if (thd && thd->locked_tables_mode) if (thd && thd->locked_tables_mode)
......
...@@ -406,7 +406,7 @@ void tc_add_table(THD *thd, TABLE *table) ...@@ -406,7 +406,7 @@ void tc_add_table(THD *thd, TABLE *table)
@return TABLE object, or NULL if no unused objects. @return TABLE object, or NULL if no unused objects.
*/ */
static TABLE *tc_acquire_table(THD *thd, TDC_element *element) TABLE *tc_acquire_table(THD *thd, TDC_element *element)
{ {
uint32 n_instances= uint32 n_instances=
my_atomic_load32_explicit((int32*) &tc_active_instances, my_atomic_load32_explicit((int32*) &tc_active_instances,
...@@ -657,7 +657,7 @@ void tdc_start_shutdown(void) ...@@ -657,7 +657,7 @@ void tdc_start_shutdown(void)
tdc_size= 0; tdc_size= 0;
tc_size= 0; tc_size= 0;
/* Free all cached but unused TABLEs and TABLE_SHAREs. */ /* Free all cached but unused TABLEs and TABLE_SHAREs. */
close_cached_tables(NULL, NULL, FALSE, LONG_TIMEOUT); purge_tables(true);
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
......
...@@ -88,7 +88,6 @@ extern bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, ...@@ -88,7 +88,6 @@ extern bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
const char *db, const char *table_name, const char *db, const char *table_name,
bool kill_delayed_threads); bool kill_delayed_threads);
extern int tdc_wait_for_old_version(THD *thd, const char *db, extern int tdc_wait_for_old_version(THD *thd, const char *db,
const char *table_name, const char *table_name,
ulong wait_timeout, uint deadlock_weight, ulong wait_timeout, uint deadlock_weight,
...@@ -102,6 +101,7 @@ extern uint tc_records(void); ...@@ -102,6 +101,7 @@ extern uint tc_records(void);
extern void tc_purge(bool mark_flushed= false); extern void tc_purge(bool mark_flushed= false);
extern void tc_add_table(THD *thd, TABLE *table); extern void tc_add_table(THD *thd, TABLE *table);
extern void tc_release_table(TABLE *table); extern void tc_release_table(TABLE *table);
extern TABLE *tc_acquire_table(THD *thd, TDC_element *element);
/** /**
Create a table cache key for non-temporary table. Create a table cache key for non-temporary table.
......
...@@ -866,7 +866,10 @@ int azclose (azio_stream *s) ...@@ -866,7 +866,10 @@ int azclose (azio_stream *s)
if (s->mode == 'w') if (s->mode == 'w')
{ {
if (do_flush(s, Z_FINISH) != Z_OK) if (do_flush(s, Z_FINISH) != Z_OK)
return destroy(s); {
destroy(s);
return Z_ERRNO;
}
putLong(s->file, s->crc); putLong(s->file, s->crc);
putLong(s->file, (uLong)(s->in & 0xffffffff)); putLong(s->file, (uLong)(s->in & 0xffffffff));
......
...@@ -1737,6 +1737,20 @@ int ha_archive::info(uint flag) ...@@ -1737,6 +1737,20 @@ int ha_archive::info(uint flag)
} }
int ha_archive::extra(enum ha_extra_function operation)
{
switch (operation) {
case HA_EXTRA_FLUSH:
mysql_mutex_lock(&share->mutex);
share->close_archive_writer();
mysql_mutex_unlock(&share->mutex);
break;
default:
break;
}
return 0;
}
/* /*
This method tells us that a bulk insert operation is about to occur. We set This method tells us that a bulk insert operation is about to occur. We set
a flag which will keep write_row from saying that its data is dirty. This in a flag which will keep write_row from saying that its data is dirty. This in
......
...@@ -148,6 +148,7 @@ class ha_archive: public handler ...@@ -148,6 +148,7 @@ class ha_archive: public handler
int read_data_header(azio_stream *file_to_read); int read_data_header(azio_stream *file_to_read);
void position(const uchar *record); void position(const uchar *record);
int info(uint); int info(uint);
int extra(enum ha_extra_function operation);
void update_create_info(HA_CREATE_INFO *create_info); void update_create_info(HA_CREATE_INFO *create_info);
int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info);
int optimize(THD* thd, HA_CHECK_OPT* check_opt); int optimize(THD* thd, HA_CHECK_OPT* check_opt);
......
...@@ -1311,12 +1311,28 @@ int ha_tina::info(uint flag) ...@@ -1311,12 +1311,28 @@ int ha_tina::info(uint flag)
int ha_tina::extra(enum ha_extra_function operation) int ha_tina::extra(enum ha_extra_function operation)
{ {
DBUG_ENTER("ha_tina::extra"); DBUG_ENTER("ha_tina::extra");
if (operation == HA_EXTRA_MARK_AS_LOG_TABLE) switch (operation) {
{ case HA_EXTRA_MARK_AS_LOG_TABLE:
mysql_mutex_lock(&share->mutex); {
share->is_log_table= TRUE; mysql_mutex_lock(&share->mutex);
mysql_mutex_unlock(&share->mutex); share->is_log_table= TRUE;
} mysql_mutex_unlock(&share->mutex);
}
break;
case HA_EXTRA_FLUSH:
mysql_mutex_lock(&share->mutex);
if (share->tina_write_opened)
{
(void)write_meta_file(share->meta_file, share->rows_recorded,
share->crashed ? TRUE :FALSE);
mysql_file_close(share->tina_write_filedes, MYF(0));
share->tina_write_opened= FALSE;
}
mysql_mutex_unlock(&share->mutex);
break;
default:
break;
}
DBUG_RETURN(0); DBUG_RETURN(0);
} }
......
...@@ -167,6 +167,9 @@ int maria_chk_status(HA_CHECK *param, MARIA_HA *info) ...@@ -167,6 +167,9 @@ int maria_chk_status(HA_CHECK *param, MARIA_HA *info)
{ {
MARIA_SHARE *share= info->s; MARIA_SHARE *share= info->s;
/* Protection for HA_EXTRA_FLUSH */
mysql_mutex_lock(&share->intern_lock);
if (maria_is_crashed_on_repair(info)) if (maria_is_crashed_on_repair(info))
_ma_check_print_warning(param, _ma_check_print_warning(param,
"Table is marked as crashed and last repair failed"); "Table is marked as crashed and last repair failed");
...@@ -189,6 +192,9 @@ int maria_chk_status(HA_CHECK *param, MARIA_HA *info) ...@@ -189,6 +192,9 @@ int maria_chk_status(HA_CHECK *param, MARIA_HA *info)
if (param->testflag & T_UPDATE_STATE) if (param->testflag & T_UPDATE_STATE)
param->warning_printed=save; param->warning_printed=save;
} }
mysql_mutex_unlock(&share->intern_lock);
if (share->state.create_trid > param->max_trid) if (share->state.create_trid > param->max_trid)
{ {
param->wrong_trd_printed= 1; /* Force should run zerofill */ param->wrong_trd_printed= 1; /* Force should run zerofill */
......
...@@ -420,7 +420,11 @@ int maria_extra(MARIA_HA *info, enum ha_extra_function function, ...@@ -420,7 +420,11 @@ int maria_extra(MARIA_HA *info, enum ha_extra_function function,
error= _ma_flush_table_files(info, MARIA_FLUSH_DATA | MARIA_FLUSH_INDEX, error= _ma_flush_table_files(info, MARIA_FLUSH_DATA | MARIA_FLUSH_INDEX,
FLUSH_KEEP, FLUSH_KEEP); FLUSH_KEEP, FLUSH_KEEP);
mysql_mutex_lock(&share->intern_lock);
/* Tell maria_lock_database() that we locked the intern_lock mutex */
info->intern_lock_locked= 1;
_ma_decrement_open_count(info, 1); _ma_decrement_open_count(info, 1);
info->intern_lock_locked= 0;
if (share->not_flushed) if (share->not_flushed)
{ {
share->not_flushed= 0; share->not_flushed= 0;
...@@ -433,6 +437,7 @@ int maria_extra(MARIA_HA *info, enum ha_extra_function function, ...@@ -433,6 +437,7 @@ int maria_extra(MARIA_HA *info, enum ha_extra_function function,
_ma_set_fatal_error(share, HA_ERR_CRASHED); _ma_set_fatal_error(share, HA_ERR_CRASHED);
} }
} }
mysql_mutex_unlock(&share->intern_lock);
break; break;
case HA_EXTRA_NORMAL: /* Theese isn't in use */ case HA_EXTRA_NORMAL: /* Theese isn't in use */
info->quick_mode= 0; info->quick_mode= 0;
......
...@@ -47,7 +47,8 @@ int maria_lock_database(MARIA_HA *info, int lock_type) ...@@ -47,7 +47,8 @@ int maria_lock_database(MARIA_HA *info, int lock_type)
} }
error=0; error=0;
mysql_mutex_lock(&share->intern_lock); if (!info->intern_lock_locked)
mysql_mutex_lock(&share->intern_lock);
if (share->kfile.file >= 0) /* May only be false on windows */ if (share->kfile.file >= 0) /* May only be false on windows */
{ {
switch (lock_type) { switch (lock_type) {
...@@ -234,7 +235,8 @@ int maria_lock_database(MARIA_HA *info, int lock_type) ...@@ -234,7 +235,8 @@ int maria_lock_database(MARIA_HA *info, int lock_type)
} }
} }
#endif #endif
mysql_mutex_unlock(&share->intern_lock); if (!info->intern_lock_locked)
mysql_mutex_unlock(&share->intern_lock);
DBUG_RETURN(error); DBUG_RETURN(error);
} /* maria_lock_database */ } /* maria_lock_database */
......
...@@ -689,6 +689,7 @@ struct st_maria_handler ...@@ -689,6 +689,7 @@ struct st_maria_handler
uint16 last_used_keyseg; /* For MARIAMRG */ uint16 last_used_keyseg; /* For MARIAMRG */
uint8 key_del_used; /* != 0 if key_del is used */ uint8 key_del_used; /* != 0 if key_del is used */
my_bool was_locked; /* Was locked in panic */ my_bool was_locked; /* Was locked in panic */
my_bool intern_lock_locked; /* locked in ma_extra() */
my_bool append_insert_at_end; /* Set if concurrent insert */ my_bool append_insert_at_end; /* Set if concurrent insert */
my_bool quick_mode; my_bool quick_mode;
my_bool in_check_table; /* We are running check tables */ my_bool in_check_table; /* We are running check tables */
......
...@@ -102,6 +102,9 @@ int chk_status(HA_CHECK *param, register MI_INFO *info) ...@@ -102,6 +102,9 @@ int chk_status(HA_CHECK *param, register MI_INFO *info)
{ {
MYISAM_SHARE *share=info->s; MYISAM_SHARE *share=info->s;
/* Protection for HA_EXTRA_FLUSH */
mysql_mutex_lock(&share->intern_lock);
if (mi_is_crashed_on_repair(info)) if (mi_is_crashed_on_repair(info))
mi_check_print_warning(param, mi_check_print_warning(param,
"Table is marked as crashed and last repair failed"); "Table is marked as crashed and last repair failed");
...@@ -121,6 +124,7 @@ int chk_status(HA_CHECK *param, register MI_INFO *info) ...@@ -121,6 +124,7 @@ int chk_status(HA_CHECK *param, register MI_INFO *info)
if (param->testflag & T_UPDATE_STATE) if (param->testflag & T_UPDATE_STATE)
param->warning_printed=save; param->warning_printed=save;
} }
mysql_mutex_unlock(&share->intern_lock);
return 0; return 0;
} }
......
...@@ -332,7 +332,11 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) ...@@ -332,7 +332,11 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg)
if (!share->temporary) if (!share->temporary)
flush_key_blocks(share->key_cache, share->kfile, &share->dirty_part_map, flush_key_blocks(share->key_cache, share->kfile, &share->dirty_part_map,
FLUSH_KEEP); FLUSH_KEEP);
mysql_mutex_lock(&share->intern_lock);
/* Tell mi_lock_database() that we locked the intern_lock mutex */
info->intern_lock_locked= 1;
_mi_decrement_open_count(info); _mi_decrement_open_count(info);
info->intern_lock_locked= 0;
if (share->not_flushed) if (share->not_flushed)
{ {
share->not_flushed=0; share->not_flushed=0;
...@@ -349,6 +353,7 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) ...@@ -349,6 +353,7 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg)
} }
if (share->base.blobs) if (share->base.blobs)
mi_alloc_rec_buff(info, -1, &info->rec_buff); mi_alloc_rec_buff(info, -1, &info->rec_buff);
mysql_mutex_unlock(&share->intern_lock);
break; break;
case HA_EXTRA_NORMAL: /* Theese isn't in use */ case HA_EXTRA_NORMAL: /* Theese isn't in use */
info->quick_mode=0; info->quick_mode=0;
......
...@@ -53,7 +53,8 @@ int mi_lock_database(MI_INFO *info, int lock_type) ...@@ -53,7 +53,8 @@ int mi_lock_database(MI_INFO *info, int lock_type)
error= 0; error= 0;
DBUG_EXECUTE_IF ("mi_lock_database_failure", error= EINVAL;); DBUG_EXECUTE_IF ("mi_lock_database_failure", error= EINVAL;);
mysql_mutex_lock(&share->intern_lock); if (!info->intern_lock_locked)
mysql_mutex_lock(&share->intern_lock);
if (share->kfile >= 0) /* May only be false on windows */ if (share->kfile >= 0) /* May only be false on windows */
{ {
switch (lock_type) { switch (lock_type) {
...@@ -261,7 +262,8 @@ int mi_lock_database(MI_INFO *info, int lock_type) ...@@ -261,7 +262,8 @@ int mi_lock_database(MI_INFO *info, int lock_type)
} }
} }
#endif #endif
mysql_mutex_unlock(&share->intern_lock); if (!info->intern_lock_locked)
mysql_mutex_unlock(&share->intern_lock);
if (mark_crashed) if (mark_crashed)
mi_mark_crashed(info); mi_mark_crashed(info);
DBUG_RETURN(error); DBUG_RETURN(error);
......
...@@ -296,6 +296,7 @@ struct st_myisam_info ...@@ -296,6 +296,7 @@ struct st_myisam_info
uint preload_buff_size; /* When preloading indexes */ uint preload_buff_size; /* When preloading indexes */
myf lock_wait; /* is 0 or MY_SHORT_WAIT */ myf lock_wait; /* is 0 or MY_SHORT_WAIT */
my_bool was_locked; /* Was locked in panic */ my_bool was_locked; /* Was locked in panic */
my_bool intern_lock_locked; /* locked in mi_extra() */
my_bool append_insert_at_end; /* Set if concurrent insert */ my_bool append_insert_at_end; /* Set if concurrent insert */
my_bool quick_mode; my_bool quick_mode;
/* If info->buff can't be used for rnext */ /* If info->buff can't be used for rnext */
......
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