diff --git a/sql/sql_class.cc b/sql/sql_class.cc index e744bbd5f8fd7..5a7ef672a471f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -5168,8 +5168,11 @@ TABLE *open_purge_table(THD *thd, const char *db, size_t dblen, DBUG_ENTER("open_purge_table"); DBUG_ASSERT(thd->locked_tables_mode < LTM_PRELOCKED); - /* Purge already hold the MDL for the table */ - Open_table_context ot_ctx(thd, MYSQL_OPEN_HAS_MDL_LOCK); + /* Purge already hold the MDL for the table. A kill flag on a purge THD + (for example from a Galera BF abort) is never reset, and purge has no + statement to abort; it must not make every subsequent open_table() fail. */ + Open_table_context ot_ctx(thd, MYSQL_OPEN_HAS_MDL_LOCK | + MYSQL_OPEN_IGNORE_KILLED); TABLE_LIST *tl= (TABLE_LIST*)thd->alloc(sizeof(TABLE_LIST)); LEX_CSTRING db_name= {db, dblen }; LEX_CSTRING table_name= { tb, tblen }; diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index dc9809ad5263e..097176103aa0c 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -3626,6 +3626,19 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx, // Granted thread is either TOI or applying wsrep_handle_granted_bf(request_thd, granted_thd, ticket, key); } + else if (granted_thd->system_thread != NON_SYSTEM_THREAD && + granted_thd->mdl_context.has_explicit_locks()) + { + /* Granted thread is a server-internal background thread holding an + explicit MDL, e.g. the InnoDB purge coordinator holding MDL_SHARED + on a table for the duration of a purge batch. Such a thread has no + wsrep transaction to roll back and never resets THD::killed, so a + BF abort would leave it killed for the rest of the server lifetime + (MDEV-41450). It releases its locks on its own; the requestor waits, + as it did before MDEV-38218. */ + wsrep_mdl_log(WSREP_MDL_DEBUG, "MDL conflict, waiting for system thread", + request_thd, granted_thd, ticket, key); + } else { // Granted thread is not brute-force thread, we can abort it diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index 19cec8f269e26..147d02ab76a3c 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -48,7 +48,7 @@ Created 3/26/1996 Heikki Tuuri TABLE *open_purge_table(THD *thd, const char *db, size_t dblen, const char *tb, size_t tblen, MDL_ticket *mdl_ticket) noexcept; -int close_thread_tables(THD* thd) noexcept; +void reset_thd(MYSQL_THD thd); MDL_ticket *get_mdl_ticket(TABLE *table) noexcept; /** Maximum allowable purge history length. <=0 means 'infinite'. */ @@ -1106,7 +1106,12 @@ static void trx_purge_close_tables(THD *thd, purge_table *last_entry, node->tables.clear(); } - close_thread_tables(thd); + /* Close the tables and free thd->mem_root, which holds the TABLE_LIST + allocated by every open_purge_table() call. Nothing else in the + coordinator's mem_root outlives its open tables, and without this + every batch (and every close_and_reopen() retry) would leak one + TABLE_LIST per opened table. */ + reset_thd(thd); for (auto mdl : mdl_tickets) {