Skip to content
/ server Public
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions mysql-test/suite/innodb/r/alter_copy_stats.result
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,17 @@ n_rows database_name lower(table_name)
DROP TABLE t1;
DROP TABLE t2;
#
# MDEV-38667 Assertion in diagnostics area on DDL stats timeout
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a note on MDEV-38667 that the current change further addresses the same issue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean adding a note on the topic on Jira?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. You are altering the test added through that Jira. So it'd be polite to add an update to it so that people watching it will get a notification.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I added a comment there explaining the change.

# MDEV-38822 Lock wait timeout does not happen anymore
#
set @lock_wait_timeout= @@global.innodb_lock_wait_timeout;
SET innodb_lock_wait_timeout = 1;
CREATE TABLE t ENGINE=InnoDB AS SELECT * FROM mysql.innodb_table_stats;
Warnings:
Warning 1088 Error updating stats for table after table rebuild: Lock wait timeout
SET innodb_lock_wait_timeout = @lock_wait_timeout;
SELECT COUNT(*) as n_rows FROM t;
n_rows
1
# For a deterministic test, we wait for the background thread to update the stats for the new table.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw, (not really insisting on it and hope the final reviewer would consider it) you could use the DBUG library to produce the effect: https://mariadb.com/docs/server/clients-and-utilities/testing-tools/mariadb-test/the-debug-sync-facility.

I'd give it a couple of hours to try and reproduce it by dipping in DBUG_SYNC points and running two threads in mariadb-test.

SELECT table_name, n_rows FROM mysql.innodb_table_stats WHERE table_name = 't';
table_name n_rows
t 1
DROP TABLE t;
7 changes: 6 additions & 1 deletion mysql-test/suite/innodb/t/alter_copy_stats.test
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,15 @@ DROP TABLE t1;
DROP TABLE t2;

--echo #
--echo # MDEV-38667 Assertion in diagnostics area on DDL stats timeout
--echo # MDEV-38822 Lock wait timeout does not happen anymore
--echo #
set @lock_wait_timeout= @@global.innodb_lock_wait_timeout;
SET innodb_lock_wait_timeout = 1;
CREATE TABLE t ENGINE=InnoDB AS SELECT * FROM mysql.innodb_table_stats;
SET innodb_lock_wait_timeout = @lock_wait_timeout;
SELECT COUNT(*) as n_rows FROM t;
--echo # For a deterministic test, we wait for the background thread to update the stats for the new table.
--let $wait_condition= SELECT COUNT(*) = 1 FROM mysql.innodb_table_stats WHERE table_name = 't' AND n_rows > 0
--source include/wait_condition.inc
SELECT table_name, n_rows FROM mysql.innodb_table_stats WHERE table_name = 't';
DROP TABLE t;
13 changes: 13 additions & 0 deletions sql/sql_class.cc
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,19 @@ int thd_sql_command(const THD *thd)
return (int) thd->lex->sql_command;
}

extern "C" int thd_sql_is_table_accessed(const MYSQL_THD thd,
const char *db_name,
const char *table_name)
{
for (TABLE *table= thd->open_tables; table; table= table->next)
{
if (!strcmp(table->s->db.str, db_name) &&
!strcmp(table->s->table_name.str, table_name))
return 1;
}
return 0;
}

/*
Returns options used with DDL's, like IF EXISTS etc...
Will returns 'nonsense' if the command was not a DDL.
Expand Down
8 changes: 1 addition & 7 deletions storage/innobase/dict/dict0stats_bg.cc
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,7 @@ static void dict_stats_recalc_pool_deinit()
destroy_background_thd(dict_stats_thd);
}

/*****************************************************************//**
Add a table to the recalc pool, which is processed by the
background stats gathering thread. Only the table id is added to the
list, so the table can be closed after being enqueued and it will be
opened when needed. If the table does not exist later (has been DROPped),
then it will be removed from the pool and skipped. */
static void dict_stats_recalc_pool_add(table_id_t id)
void dict_stats_recalc_pool_add(table_id_t id)
{
ut_ad(!srv_read_only_mode);
ut_ad(id);
Expand Down
24 changes: 23 additions & 1 deletion storage/innobase/handler/ha_innodb.cc
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ TABLE *get_purge_table(THD *thd);
TABLE *open_purge_table(THD *thd, const char *db, size_t dblen,
const char *tb, size_t tblen);
void close_thread_tables(THD* thd);

extern "C" int thd_sql_is_table_accessed(const MYSQL_THD thd,
const char *db_name,
const char *table_name);
#ifdef MYSQL_DYNAMIC_PLUGIN
#define tc_size 400
#endif
Expand Down Expand Up @@ -21299,6 +21301,26 @@ void alter_stats_rebuild(dict_table_t *table, THD *thd)
if (!table->space || !dict_stats_is_persistent_enabled(table))
DBUG_VOID_RETURN;

if (thd && thd_sql_command(thd) == SQLCOM_CREATE_TABLE)
{
LEX_STRING *q= thd_query_string(thd);
if (q->str && q->length > 0)
{

if ((thd_sql_is_table_accessed(thd, "mysql", "innodb_table_stats")) ||
thd_sql_is_table_accessed(thd, "mysql", "innodb_index_stats"))
{
// Avoids a deadlock where a shared lock is held on the
// stats system table, then dict_stats_update tries to acquire
// an exclusive lock on it. An example is "CREATE TABLE t1 AS SELECT *
// FROM innodb_[table/index]_stats".
dict_stats_recalc_pool_add(table->id);

DBUG_VOID_RETURN;
}
}
}

dberr_t ret= dict_stats_update(table, DICT_STATS_RECALC_PERSISTENT);
if (ret != DB_SUCCESS)
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
Expand Down
9 changes: 9 additions & 0 deletions storage/innobase/include/dict0stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,3 +245,12 @@ dict_stats_empty_table(
dict_table_t* table,
bool empty_defrag_stats);
#endif /* dict0stats_h */

/**
* @brief Add a table to the recalc pool, which is processed by the
* background stats gathering thread. Only the table id is added to the
* list, so the table can be closed after being enqueued and it will be
* opened when needed. If the table does not exist later (has been DROPped),
* then it will be removed from the pool and skipped.
*/
void dict_stats_recalc_pool_add(table_id_t id);