Commit 3792693f authored by Sergei Golubchik's avatar Sergei Golubchik

merge MySQL-5.6 bugfix "Bug#17862905: MYSQLDUMP CREATES USELESS METADATA LOCKS"

revno: 5716
committer: Praveenkumar Hulakund <praveenkumar.hulakund@oracle.com>
branch nick: mysql_5_6
timestamp: Sat 2013-12-28 22:08:40 +0530
message:
  Bug#17862905: MYSQLDUMP CREATES USELESS METADATA LOCKS
parent f90dca1a
...@@ -4406,6 +4406,12 @@ static int dump_all_tables_in_db(char *database) ...@@ -4406,6 +4406,12 @@ static int dump_all_tables_in_db(char *database)
else else
verbose_msg("-- dump_all_tables_in_db : logs flushed successfully!\n"); verbose_msg("-- dump_all_tables_in_db : logs flushed successfully!\n");
} }
if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500)
{
verbose_msg("-- Setting savepoint...\n");
if (mysql_query_with_error_report(mysql, 0, "SAVEPOINT sp"))
DBUG_RETURN(1);
}
while ((table= getTableName(0))) while ((table= getTableName(0)))
{ {
char *end= strmov(afterdot, table); char *end= strmov(afterdot, table);
...@@ -4423,6 +4429,23 @@ static int dump_all_tables_in_db(char *database) ...@@ -4423,6 +4429,23 @@ static int dump_all_tables_in_db(char *database)
maybe_exit(EX_MYSQLERR); maybe_exit(EX_MYSQLERR);
} }
} }
/**
ROLLBACK TO SAVEPOINT in --single-transaction mode to release metadata
lock on table which was already dumped. This allows to avoid blocking
concurrent DDL on this table without sacrificing correctness, as we
won't access table second time and dumps created by --single-transaction
mode have validity point at the start of transaction anyway.
Note that this doesn't make --single-transaction mode with concurrent
DDL safe in general case. It just improves situation for people for whom
it might be working.
*/
if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500)
{
verbose_msg("-- Rolling back to savepoint sp...\n");
if (mysql_query_with_error_report(mysql, 0, "ROLLBACK TO SAVEPOINT sp"))
maybe_exit(EX_MYSQLERR);
}
} }
else else
{ {
...@@ -4445,6 +4468,14 @@ static int dump_all_tables_in_db(char *database) ...@@ -4445,6 +4468,14 @@ static int dump_all_tables_in_db(char *database)
} }
} }
} }
if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500)
{
verbose_msg("-- Releasing savepoint...\n");
if (mysql_query_with_error_report(mysql, 0, "RELEASE SAVEPOINT sp"))
DBUG_RETURN(1);
}
if (opt_events && mysql_get_server_version(mysql) >= 50106) if (opt_events && mysql_get_server_version(mysql) >= 50106)
{ {
DBUG_PRINT("info", ("Dumping events for database %s", database)); DBUG_PRINT("info", ("Dumping events for database %s", database));
...@@ -4687,6 +4718,13 @@ static int dump_selected_tables(char *db, char **table_names, int tables) ...@@ -4687,6 +4718,13 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
if (opt_xml) if (opt_xml)
print_xml_tag(md_result_file, "", "\n", "database", "name=", db, NullS); print_xml_tag(md_result_file, "", "\n", "database", "name=", db, NullS);
if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500)
{
verbose_msg("-- Setting savepoint...\n");
if (mysql_query_with_error_report(mysql, 0, "SAVEPOINT sp"))
DBUG_RETURN(1);
}
/* Dump each selected table */ /* Dump each selected table */
for (pos= dump_tables; pos < end; pos++) for (pos= dump_tables; pos < end; pos++)
{ {
...@@ -4702,6 +4740,31 @@ static int dump_selected_tables(char *db, char **table_names, int tables) ...@@ -4702,6 +4740,31 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
maybe_exit(EX_MYSQLERR); maybe_exit(EX_MYSQLERR);
} }
} }
/**
ROLLBACK TO SAVEPOINT in --single-transaction mode to release metadata
lock on table which was already dumped. This allows to avoid blocking
concurrent DDL on this table without sacrificing correctness, as we
won't access table second time and dumps created by --single-transaction
mode have validity point at the start of transaction anyway.
Note that this doesn't make --single-transaction mode with concurrent
DDL safe in general case. It just improves situation for people for whom
it might be working.
*/
if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500)
{
verbose_msg("-- Rolling back to savepoint sp...\n");
if (mysql_query_with_error_report(mysql, 0, "ROLLBACK TO SAVEPOINT sp"))
maybe_exit(EX_MYSQLERR);
}
}
if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500)
{
verbose_msg("-- Releasing savepoint...\n");
if (mysql_query_with_error_report(mysql, 0, "RELEASE SAVEPOINT sp"))
DBUG_RETURN(1);
} }
/* Dump each selected view */ /* Dump each selected view */
......
...@@ -5206,12 +5206,16 @@ INSERT INTO b12809202_db.t2 VALUES (1), (2), (3); ...@@ -5206,12 +5206,16 @@ INSERT INTO b12809202_db.t2 VALUES (1), (2), (3);
-- Connecting to localhost... -- Connecting to localhost...
-- main : logs flushed successfully! -- main : logs flushed successfully!
-- Starting transaction... -- Starting transaction...
-- Setting savepoint...
-- Retrieving table structure for table t1... -- Retrieving table structure for table t1...
-- Sending SELECT query... -- Sending SELECT query...
-- Retrieving rows... -- Retrieving rows...
-- Rolling back to savepoint sp...
-- Retrieving table structure for table t2... -- Retrieving table structure for table t2...
-- Sending SELECT query... -- Sending SELECT query...
-- Retrieving rows... -- Retrieving rows...
-- Rolling back to savepoint sp...
-- Releasing savepoint...
-- Disconnecting from localhost... -- Disconnecting from localhost...
#### Dump ends here #### #### Dump ends here ####
......
...@@ -1978,6 +1978,41 @@ int ha_release_temporary_latches(THD *thd) ...@@ -1978,6 +1978,41 @@ int ha_release_temporary_latches(THD *thd)
return 0; return 0;
} }
/**
Check if all storage engines used in transaction agree that after
rollback to savepoint it is safe to release MDL locks acquired after
savepoint creation.
@param thd The client thread that executes the transaction.
@return true - It is safe to release MDL locks.
false - If it is not.
*/
bool ha_rollback_to_savepoint_can_release_mdl(THD *thd)
{
Ha_trx_info *ha_info;
THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
&thd->transaction.all);
DBUG_ENTER("ha_rollback_to_savepoint_can_release_mdl");
/**
Checking whether it is safe to release metadata locks after rollback to
savepoint in all the storage engines that are part of the transaction.
*/
for (ha_info= trans->ha_list; ha_info; ha_info= ha_info->next())
{
handlerton *ht= ha_info->ht();
DBUG_ASSERT(ht);
if (ht->savepoint_rollback_can_release_mdl == 0 ||
ht->savepoint_rollback_can_release_mdl(ht, thd) == false)
DBUG_RETURN(false);
}
DBUG_RETURN(true);
}
int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv) int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
{ {
int error=0; int error=0;
......
...@@ -1025,6 +1025,13 @@ struct handlerton ...@@ -1025,6 +1025,13 @@ struct handlerton
to the savepoint_set call to the savepoint_set call
*/ */
int (*savepoint_rollback)(handlerton *hton, THD *thd, void *sv); int (*savepoint_rollback)(handlerton *hton, THD *thd, void *sv);
/**
Check if storage engine allows to release metadata locks which were
acquired after the savepoint if rollback to savepoint is done.
@return true - If it is safe to release MDL locks.
false - If it is not.
*/
bool (*savepoint_rollback_can_release_mdl)(handlerton *hton, THD *thd);
int (*savepoint_release)(handlerton *hton, THD *thd, void *sv); int (*savepoint_release)(handlerton *hton, THD *thd, void *sv);
/* /*
'all' is true if it's a real commit, that makes persistent changes 'all' is true if it's a real commit, that makes persistent changes
...@@ -4046,6 +4053,7 @@ int ha_enable_transaction(THD *thd, bool on); ...@@ -4046,6 +4053,7 @@ int ha_enable_transaction(THD *thd, bool on);
/* savepoints */ /* savepoints */
int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv); int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv);
bool ha_rollback_to_savepoint_can_release_mdl(THD *thd);
int ha_savepoint(THD *thd, SAVEPOINT *sv); int ha_savepoint(THD *thd, SAVEPOINT *sv);
int ha_release_savepoint(THD *thd, SAVEPOINT *sv); int ha_release_savepoint(THD *thd, SAVEPOINT *sv);
......
...@@ -73,6 +73,8 @@ static int binlog_init(void *p); ...@@ -73,6 +73,8 @@ static int binlog_init(void *p);
static int binlog_close_connection(handlerton *hton, THD *thd); static int binlog_close_connection(handlerton *hton, THD *thd);
static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv); static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv);
static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv); static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv);
static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton,
THD *thd);
static int binlog_commit(handlerton *hton, THD *thd, bool all); static int binlog_commit(handlerton *hton, THD *thd, bool all);
static int binlog_rollback(handlerton *hton, THD *thd, bool all); static int binlog_rollback(handlerton *hton, THD *thd, bool all);
static int binlog_prepare(handlerton *hton, THD *thd, bool all); static int binlog_prepare(handlerton *hton, THD *thd, bool all);
...@@ -1629,6 +1631,8 @@ int binlog_init(void *p) ...@@ -1629,6 +1631,8 @@ int binlog_init(void *p)
binlog_hton->close_connection= binlog_close_connection; binlog_hton->close_connection= binlog_close_connection;
binlog_hton->savepoint_set= binlog_savepoint_set; binlog_hton->savepoint_set= binlog_savepoint_set;
binlog_hton->savepoint_rollback= binlog_savepoint_rollback; binlog_hton->savepoint_rollback= binlog_savepoint_rollback;
binlog_hton->savepoint_rollback_can_release_mdl=
binlog_savepoint_rollback_can_release_mdl;
binlog_hton->commit= binlog_commit; binlog_hton->commit= binlog_commit;
binlog_hton->rollback= binlog_rollback; binlog_hton->rollback= binlog_rollback;
binlog_hton->prepare= binlog_prepare; binlog_hton->prepare= binlog_prepare;
...@@ -1879,6 +1883,32 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all) ...@@ -1879,6 +1883,32 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all)
return 0; return 0;
} }
/*
We flush the cache wrapped in a beging/rollback if:
. aborting a single or multi-statement transaction and;
. the OPTION_KEEP_LOG is active or;
. the format is STMT and a non-trans table was updated or;
. the format is MIXED and a temporary non-trans table was
updated or;
. the format is MIXED, non-trans table was updated and
aborting a single statement transaction;
*/
static bool trans_cannot_safely_rollback(THD *thd, bool all)
{
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
return ((thd->variables.option_bits & OPTION_KEEP_LOG) ||
(trans_has_updated_non_trans_table(thd) &&
thd->variables.binlog_format == BINLOG_FORMAT_STMT) ||
(cache_mngr->trx_cache.changes_to_non_trans_temp_table() &&
thd->variables.binlog_format == BINLOG_FORMAT_MIXED) ||
(trans_has_updated_non_trans_table(thd) &&
ending_single_stmt_trans(thd,all) &&
thd->variables.binlog_format == BINLOG_FORMAT_MIXED));
}
/** /**
This function is called once after each statement. This function is called once after each statement.
...@@ -1999,25 +2029,7 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) ...@@ -1999,25 +2029,7 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
} }
else if (!error) else if (!error)
{ {
/* if (ending_trans(thd, all) && trans_cannot_safely_rollback(thd, all))
We flush the cache wrapped in a beging/rollback if:
. aborting a single or multi-statement transaction and;
. the OPTION_KEEP_LOG is active or;
. the format is STMT and a non-trans table was updated or;
. the format is MIXED and a temporary non-trans table was
updated or;
. the format is MIXED, non-trans table was updated and
aborting a single statement transaction;
*/
if (ending_trans(thd, all) &&
((thd->variables.option_bits & OPTION_KEEP_LOG) ||
(trans_has_updated_non_trans_table(thd) &&
thd->variables.binlog_format == BINLOG_FORMAT_STMT) ||
(cache_mngr->trx_cache.changes_to_non_trans_temp_table() &&
thd->variables.binlog_format == BINLOG_FORMAT_MIXED) ||
(trans_has_updated_non_trans_table(thd) &&
ending_single_stmt_trans(thd,all) &&
thd->variables.binlog_format == BINLOG_FORMAT_MIXED)))
error= binlog_rollback_flush_trx_cache(thd, all, cache_mngr); error= binlog_rollback_flush_trx_cache(thd, all, cache_mngr);
/* /*
Truncate the cache if: Truncate the cache if:
...@@ -2197,6 +2209,30 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv) ...@@ -2197,6 +2209,30 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
} }
/**
Check whether binlog state allows to safely release MDL locks after
rollback to savepoint.
@param hton The binlog handlerton.
@param thd The client thread that executes the transaction.
@return true - It is safe to release MDL locks.
false - If it is not.
*/
static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton,
THD *thd)
{
DBUG_ENTER("binlog_savepoint_rollback_can_release_mdl");
/*
If we have not updated any non-transactional tables rollback
to savepoint will simply truncate binlog cache starting from
SAVEPOINT command. So it should be safe to release MDL acquired
after SAVEPOINT command in this case.
*/
DBUG_RETURN(!trans_cannot_safely_rollback(thd, true));
}
int check_binlog_magic(IO_CACHE* log, const char** errmsg) int check_binlog_magic(IO_CACHE* log, const char** errmsg)
{ {
uchar magic[4]; uchar magic[4];
......
...@@ -574,6 +574,32 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name) ...@@ -574,6 +574,32 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name)
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
/**
Checking whether it is safe to release metadata locks acquired after
savepoint, if rollback to savepoint is successful.
Whether it is safe to release MDL after rollback to savepoint depends
on storage engines participating in transaction:
- InnoDB doesn't release any row-locks on rollback to savepoint so it
is probably a bad idea to release MDL as well.
- Binary log implementation in some cases (e.g when non-transactional
tables involved) may choose not to remove events added after savepoint
from transactional cache, but instead will write them to binary
log accompanied with ROLLBACK TO SAVEPOINT statement. Since the real
write happens at the end of transaction releasing MDL on tables
mentioned in these events (i.e. acquired after savepoint and before
rollback ot it) can break replication, as concurrent DROP TABLES
statements will be able to drop these tables before events will get
into binary log,
For backward-compatibility reasons we always release MDL if binary
logging is off.
*/
bool mdl_can_safely_rollback_to_savepoint=
(!(mysql_bin_log.is_open() && thd->variables.sql_log_bin) ||
ha_rollback_to_savepoint_can_release_mdl(thd));
if (ha_rollback_to_savepoint(thd, sv)) if (ha_rollback_to_savepoint(thd, sv))
res= TRUE; res= TRUE;
else if (((thd->variables.option_bits & OPTION_KEEP_LOG) || else if (((thd->variables.option_bits & OPTION_KEEP_LOG) ||
...@@ -585,14 +611,7 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name) ...@@ -585,14 +611,7 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name)
thd->transaction.savepoints= sv; thd->transaction.savepoints= sv;
/* if (!res && mdl_can_safely_rollback_to_savepoint)
Release metadata locks that were acquired during this savepoint unit
unless binlogging is on. Releasing locks with binlogging on can break
replication as it allows other connections to drop these tables before
rollback to savepoint is written to the binlog.
*/
bool binlog_on= mysql_bin_log.is_open() && thd->variables.sql_log_bin;
if (!res && !binlog_on)
thd->mdl_context.rollback_to_savepoint(sv->mdl_savepoint); thd->mdl_context.rollback_to_savepoint(sv->mdl_savepoint);
DBUG_RETURN(MY_TEST(res)); DBUG_RETURN(MY_TEST(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