From 5bd994b0d56d11bf62717a84172c49ca9ed37de4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= <marko.makela@mariadb.com>
Date: Wed, 3 Mar 2021 10:13:56 +0200
Subject: [PATCH] MDEV-24811 Assertion find(table) failed with
 innodb_evict_tables_on_commit_debug

This is a backport of commit 18535a402817d8a2b8452df0f75c15dda9199acb
from 10.6.

lock_release(): Implement innodb_evict_tables_on_commit_debug.
Before releasing any locks, collect the identifiers of tables to
be evicted. After releasing all locks, look up for the tables and
evict them if it is safe to do so.

trx_commit_in_memory(): Invoke trx_update_mod_tables_timestamp()
before lock_release(), so that our locks will protect the tables
from being evicted.
---
 storage/innobase/lock/lock0lock.cc | 33 ++++++++++++++++++++
 storage/innobase/trx/trx0trx.cc    | 50 +++---------------------------
 2 files changed, 37 insertions(+), 46 deletions(-)

diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc
index da3dba73bee..ee57a493119 100644
--- a/storage/innobase/lock/lock0lock.cc
+++ b/storage/innobase/lock/lock0lock.cc
@@ -4263,6 +4263,19 @@ lock_check_dict_lock(
 and release possible other transactions waiting because of these locks. */
 void lock_release(trx_t* trx)
 {
+#ifdef UNIV_DEBUG
+	std::set<table_id_t> to_evict;
+	if (innodb_evict_tables_on_commit_debug && !trx->is_recovered)
+# if 1 /* if dict_stats_exec_sql() were not playing dirty tricks */
+	if (!mutex_own(&dict_sys->mutex))
+# else /* this would be more proper way to do it */
+	if (!trx->dict_operation_lock_mode && !trx->dict_operation)
+# endif
+	for (trx_mod_tables_t::const_iterator it= trx->mod_tables.begin();
+	     it != trx->mod_tables.end(); ++it)
+		if (!it->first->is_temporary())
+			to_evict.insert(it->first->id);
+#endif
 	ulint		count = 0;
 	trx_id_t	max_trx_id = trx_sys.get_max_trx_id();
 
@@ -4311,6 +4324,26 @@ void lock_release(trx_t* trx)
 	}
 
 	lock_mutex_exit();
+
+#ifdef UNIV_DEBUG
+	if (to_evict.empty()) {
+		return;
+	}
+	mutex_enter(&dict_sys->mutex);
+	lock_mutex_enter();
+	for (std::set<table_id_t>::const_iterator i = to_evict.begin();
+	     i != to_evict.end(); ++i) {
+		if (dict_table_t *table = dict_table_open_on_id(
+			    *i, TRUE, DICT_TABLE_OP_OPEN_ONLY_IF_CACHED)) {
+			if (!table->get_ref_count()
+			    && !UT_LIST_GET_LEN(table->locks)) {
+				dict_table_remove_from_cache_low(table, true);
+			}
+		}
+	}
+	lock_mutex_exit();
+	mutex_exit(&dict_sys->mutex);
+#endif
 }
 
 /* True if a lock mode is S or X */
diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc
index 609ab63ff4c..90ed4141633 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -1276,22 +1276,6 @@ trx_update_mod_tables_timestamp(
 	const time_t now = time(NULL);
 
 	trx_mod_tables_t::const_iterator	end = trx->mod_tables.end();
-#ifdef UNIV_DEBUG
-# if MYSQL_VERSION_ID >= 100405
-#  define dict_sys_mutex dict_sys.mutex
-# else
-#  define dict_sys_mutex dict_sys->mutex
-# endif
-
-	const bool preserve_tables = !innodb_evict_tables_on_commit_debug
-		|| trx->is_recovered /* avoid trouble with XA recovery */
-# if 1 /* if dict_stats_exec_sql() were not playing dirty tricks */
-		|| mutex_own(&dict_sys_mutex)
-# else /* this would be more proper way to do it */
-		|| trx->dict_operation_lock_mode || trx->dict_operation
-# endif
-		;
-#endif
 
 	for (trx_mod_tables_t::const_iterator it = trx->mod_tables.begin();
 	     it != end;
@@ -1307,30 +1291,6 @@ trx_update_mod_tables_timestamp(
 		intrusive. */
 		dict_table_t* table = it->first;
 		table->update_time = now;
-#ifdef UNIV_DEBUG
-		if (preserve_tables || table->get_ref_count()
-		    || UT_LIST_GET_LEN(table->locks)) {
-			/* do not evict when committing DDL operations
-			or if some other transaction is holding the
-			table handle */
-			continue;
-		}
-		/* recheck while holding the mutex that blocks
-		table->acquire() */
-		mutex_enter(&dict_sys_mutex);
-		mutex_enter(&lock_sys.mutex);
-		const bool do_evict = !table->get_ref_count()
-			&& !UT_LIST_GET_LEN(table->locks);
-		mutex_exit(&lock_sys.mutex);
-		if (do_evict) {
-# if MYSQL_VERSION_ID >= 100405
-			dict_sys.remove(table, true);
-# else
-			dict_table_remove_from_cache_low(table, true);
-# endif
-		}
-		mutex_exit(&dict_sys_mutex);
-#endif
 	}
 
 	trx->mod_tables.clear();
@@ -1398,16 +1358,10 @@ trx_commit_in_memory(
 			while (UNIV_UNLIKELY(trx->is_referenced())) {
 				ut_delay(srv_spin_wait_delay);
 			}
-
-			trx->release_locks();
-			trx->id = 0;
 		} else {
 			ut_ad(trx->read_only || !trx->rsegs.m_redo.rseg);
-			trx->release_locks();
 		}
 
-		DEBUG_SYNC_C("after_trx_committed_in_memory");
-
 		if (trx->read_only || !trx->rsegs.m_redo.rseg) {
 			MONITOR_INC(MONITOR_TRX_RO_COMMIT);
 		} else {
@@ -1415,6 +1369,10 @@ trx_commit_in_memory(
 			MONITOR_INC(MONITOR_TRX_RW_COMMIT);
 			trx->is_recovered = false;
 		}
+
+		trx->release_locks();
+		trx->id = 0;
+		DEBUG_SYNC_C("after_trx_committed_in_memory");
 	}
 
 	ut_ad(!trx->rsegs.m_redo.undo);
-- 
2.30.9