Commit 5fe1d7d0 authored by Oleksandr Byelkin's avatar Oleksandr Byelkin

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
parent b75d7676
......@@ -220,3 +220,29 @@ RESET QUERY CACHE;
DROP TABLE t1;
SET GLOBAL query_cache_size= DEFAULT;
SET GLOBAL query_cache_type= DEFAULT;
#
# MDEV-14526: MariaDB keeps crashing under load when
# query_cache_type is changed
#
CREATE TABLE t1 (
`id` int(10) NOT NULL AUTO_INCREMENT,
`k` int(10) default '0',
PRIMARY KEY (`id`))
ENGINE=MyISAM;
INSERT IGNORE INTO t1 VALUES
(NULL,1),(NULL,8),(NULL,NULL),(NULL,NULL),(NULL,4),(NULL,9),(NULL,7),
(NULL,3),(NULL,NULL),(NULL,2),(NULL,3),(NULL,NULL),(NULL,2),(NULL,7),
(NULL,1),(NULL,2),(NULL,4),(NULL,NULL),(NULL,1),(NULL,1),(NULL,4);
SET GLOBAL query_cache_size= 1024*1024;
SET GLOBAL query_cache_type= 1;
set debug_sync="wait_in_query_cache_store_query SIGNAL parked WAIT_FOR go";
SELECT DISTINCT id FROM t1 WHERE id BETWEEN 5603 AND 16218 ORDER BY k;
set debug_sync="now WAIT_FOR parked";
SET GLOBAL query_cache_type= 0;
set debug_sync="now SIGNAL go";
id
set debug_sync= 'RESET';
DROP TABLE t1;
SEt GLOBAL query_cache_size= DEFAULT;
SEt GLOBAL query_cache_type= DEFAULT;
# End of 5.5 tests
......@@ -319,3 +319,55 @@ RESET QUERY CACHE;
DROP TABLE t1;
SET GLOBAL query_cache_size= DEFAULT;
SET GLOBAL query_cache_type= DEFAULT;
--echo #
--echo # MDEV-14526: MariaDB keeps crashing under load when
--echo # query_cache_type is changed
--echo #
CREATE TABLE t1 (
`id` int(10) NOT NULL AUTO_INCREMENT,
`k` int(10) default '0',
PRIMARY KEY (`id`))
ENGINE=MyISAM;
INSERT IGNORE INTO t1 VALUES
(NULL,1),(NULL,8),(NULL,NULL),(NULL,NULL),(NULL,4),(NULL,9),(NULL,7),
(NULL,3),(NULL,NULL),(NULL,2),(NULL,3),(NULL,NULL),(NULL,2),(NULL,7),
(NULL,1),(NULL,2),(NULL,4),(NULL,NULL),(NULL,1),(NULL,1),(NULL,4);
SET GLOBAL query_cache_size= 1024*1024;
SET GLOBAL query_cache_type= 1;
--connect (con2,localhost,root,,test)
--connect (con1,localhost,root,,test)
set debug_sync="wait_in_query_cache_store_query SIGNAL parked WAIT_FOR go";
--send
SELECT DISTINCT id FROM t1 WHERE id BETWEEN 5603 AND 16218 ORDER BY k;
--connection default
set debug_sync="now WAIT_FOR parked";
--connection con2
--send
SET GLOBAL query_cache_type= 0;
--connection default
set debug_sync="now SIGNAL go";
--connection con1
--reap
--connection con2
--reap
# Cleanup
--disconnect con1
--disconnect con2
--connection default
set debug_sync= 'RESET';
DROP TABLE t1;
SEt GLOBAL query_cache_size= DEFAULT;
SEt GLOBAL query_cache_type= DEFAULT;
--echo # End of 5.5 tests
......@@ -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);
......
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