diff options
author | Oleksandr Byelkin <sanja@mariadb.com> | 2018-01-12 18:17:55 +0100 |
---|---|---|
committer | Oleksandr Byelkin <sanja@mariadb.com> | 2018-01-14 10:49:56 +0100 |
commit | 5fe1d7d07611855963ea62ca39461289d8f8d25e (patch) | |
tree | cacc2b5537b2f8fa10db368a25fd3c8c5a2ac49b /sql/sql_cache.cc | |
parent | b75d767689aff33f4d6963169bb7044165494885 (diff) | |
download | mariadb-git-5fe1d7d07611855963ea62ca39461289d8f8d25e.tar.gz |
MDEV-14526: MariaDB keeps crashing under load when query_cache_type is changed
The problem was in such scenario:
T1 - starts registering query and locked QC
T2 - starts disabling QC and wait for UNLOCK
T1 - unlock QC
T2 - disable QC and destroy signals without waiting for query unlock
T1 a) - not yet unlocked query in qc and crash on attempt to unlock because
QC signals are destroyed
b) if above was done before destruction, it execute end_of results first
time at exit on after try_lock which see QC disables and return TRUE.
But it do not reset query_cache_tls->first_query_block which lead to
second call of end_of_result when diagnostic arena has already
inappropriate status (not is_eof()).
Fix is:
1) wait for all queries unlocked before destroying them by locking and
unlocking
2) remove query_cache_tls->first_query_block if QC disabled
Diffstat (limited to 'sql/sql_cache.cc')
-rw-r--r-- | sql/sql_cache.cc | 19 |
1 files changed, 18 insertions, 1 deletions
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 2ec870f314f..3fc1edfcb56 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1184,7 +1184,11 @@ void Query_cache::end_of_result(THD *thd) #endif if (try_lock(thd, Query_cache::WAIT)) + { + if (is_disabled()) + query_cache_tls->first_query_block= NULL; // do not try again with QC DBUG_VOID_RETURN; + } query_block= query_cache_tls->first_query_block; if (query_block) @@ -1556,6 +1560,8 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", unlock(); + DEBUG_SYNC(thd, "wait_in_query_cache_store_query"); + // init_n_lock make query block locked BLOCK_UNLOCK_WR(query_block); } @@ -2679,13 +2685,17 @@ void Query_cache::make_disabled() This function frees all resources allocated by the cache. You have to call init_cache() before using the cache again. This function - requires the structure_guard_mutex to be locked. + requires the cache to be locked (LOCKED_NO_WAIT, lock_and_suspend) or + disabling. */ void Query_cache::free_cache() { DBUG_ENTER("Query_cache::free_cache"); + DBUG_ASSERT(m_cache_lock_status == LOCKED_NO_WAIT || + m_cache_status == DISABLE_REQUEST); + /* Destroy locks */ Query_cache_block *block= queries_blocks; if (block) @@ -2693,6 +2703,13 @@ void Query_cache::free_cache() do { Query_cache_query *query= block->query(); + /* + There will not be new requests but some maybe not finished yet, + so wait for them by trying lock/unlock + */ + BLOCK_LOCK_WR(block); + BLOCK_UNLOCK_WR(block); + mysql_rwlock_destroy(&query->lock); block= block->next; } while (block != queries_blocks); |