Skip to content

Commit 5fe1d7d

Browse files
committed
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
1 parent b75d767 commit 5fe1d7d

File tree

3 files changed

+96
-1
lines changed

3 files changed

+96
-1
lines changed

mysql-test/r/query_cache_debug.result

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,29 @@ RESET QUERY CACHE;
220220
DROP TABLE t1;
221221
SET GLOBAL query_cache_size= DEFAULT;
222222
SET GLOBAL query_cache_type= DEFAULT;
223+
#
224+
# MDEV-14526: MariaDB keeps crashing under load when
225+
# query_cache_type is changed
226+
#
227+
CREATE TABLE t1 (
228+
`id` int(10) NOT NULL AUTO_INCREMENT,
229+
`k` int(10) default '0',
230+
PRIMARY KEY (`id`))
231+
ENGINE=MyISAM;
232+
INSERT IGNORE INTO t1 VALUES
233+
(NULL,1),(NULL,8),(NULL,NULL),(NULL,NULL),(NULL,4),(NULL,9),(NULL,7),
234+
(NULL,3),(NULL,NULL),(NULL,2),(NULL,3),(NULL,NULL),(NULL,2),(NULL,7),
235+
(NULL,1),(NULL,2),(NULL,4),(NULL,NULL),(NULL,1),(NULL,1),(NULL,4);
236+
SET GLOBAL query_cache_size= 1024*1024;
237+
SET GLOBAL query_cache_type= 1;
238+
set debug_sync="wait_in_query_cache_store_query SIGNAL parked WAIT_FOR go";
239+
SELECT DISTINCT id FROM t1 WHERE id BETWEEN 5603 AND 16218 ORDER BY k;
240+
set debug_sync="now WAIT_FOR parked";
241+
SET GLOBAL query_cache_type= 0;
242+
set debug_sync="now SIGNAL go";
243+
id
244+
set debug_sync= 'RESET';
245+
DROP TABLE t1;
246+
SEt GLOBAL query_cache_size= DEFAULT;
247+
SEt GLOBAL query_cache_type= DEFAULT;
248+
# End of 5.5 tests

mysql-test/t/query_cache_debug.test

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,3 +319,55 @@ RESET QUERY CACHE;
319319
DROP TABLE t1;
320320
SET GLOBAL query_cache_size= DEFAULT;
321321
SET GLOBAL query_cache_type= DEFAULT;
322+
323+
--echo #
324+
--echo # MDEV-14526: MariaDB keeps crashing under load when
325+
--echo # query_cache_type is changed
326+
--echo #
327+
328+
CREATE TABLE t1 (
329+
`id` int(10) NOT NULL AUTO_INCREMENT,
330+
`k` int(10) default '0',
331+
PRIMARY KEY (`id`))
332+
ENGINE=MyISAM;
333+
334+
INSERT IGNORE INTO t1 VALUES
335+
(NULL,1),(NULL,8),(NULL,NULL),(NULL,NULL),(NULL,4),(NULL,9),(NULL,7),
336+
(NULL,3),(NULL,NULL),(NULL,2),(NULL,3),(NULL,NULL),(NULL,2),(NULL,7),
337+
(NULL,1),(NULL,2),(NULL,4),(NULL,NULL),(NULL,1),(NULL,1),(NULL,4);
338+
339+
SET GLOBAL query_cache_size= 1024*1024;
340+
SET GLOBAL query_cache_type= 1;
341+
342+
--connect (con2,localhost,root,,test)
343+
--connect (con1,localhost,root,,test)
344+
set debug_sync="wait_in_query_cache_store_query SIGNAL parked WAIT_FOR go";
345+
--send
346+
347+
SELECT DISTINCT id FROM t1 WHERE id BETWEEN 5603 AND 16218 ORDER BY k;
348+
349+
--connection default
350+
351+
set debug_sync="now WAIT_FOR parked";
352+
--connection con2
353+
--send
354+
SET GLOBAL query_cache_type= 0;
355+
356+
--connection default
357+
set debug_sync="now SIGNAL go";
358+
359+
--connection con1
360+
--reap
361+
--connection con2
362+
--reap
363+
364+
# Cleanup
365+
--disconnect con1
366+
--disconnect con2
367+
--connection default
368+
set debug_sync= 'RESET';
369+
DROP TABLE t1;
370+
SEt GLOBAL query_cache_size= DEFAULT;
371+
SEt GLOBAL query_cache_type= DEFAULT;
372+
373+
--echo # End of 5.5 tests

sql/sql_cache.cc

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1184,7 +1184,11 @@ void Query_cache::end_of_result(THD *thd)
11841184
#endif
11851185

11861186
if (try_lock(thd, Query_cache::WAIT))
1187+
{
1188+
if (is_disabled())
1189+
query_cache_tls->first_query_block= NULL; // do not try again with QC
11871190
DBUG_VOID_RETURN;
1191+
}
11881192

11891193
query_block= query_cache_tls->first_query_block;
11901194
if (query_block)
@@ -1556,6 +1560,8 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
15561560

15571561
unlock();
15581562

1563+
DEBUG_SYNC(thd, "wait_in_query_cache_store_query");
1564+
15591565
// init_n_lock make query block locked
15601566
BLOCK_UNLOCK_WR(query_block);
15611567
}
@@ -2679,20 +2685,31 @@ void Query_cache::make_disabled()
26792685
26802686
This function frees all resources allocated by the cache. You
26812687
have to call init_cache() before using the cache again. This function
2682-
requires the structure_guard_mutex to be locked.
2688+
requires the cache to be locked (LOCKED_NO_WAIT, lock_and_suspend) or
2689+
disabling.
26832690
*/
26842691

26852692
void Query_cache::free_cache()
26862693
{
26872694
DBUG_ENTER("Query_cache::free_cache");
26882695

2696+
DBUG_ASSERT(m_cache_lock_status == LOCKED_NO_WAIT ||
2697+
m_cache_status == DISABLE_REQUEST);
2698+
26892699
/* Destroy locks */
26902700
Query_cache_block *block= queries_blocks;
26912701
if (block)
26922702
{
26932703
do
26942704
{
26952705
Query_cache_query *query= block->query();
2706+
/*
2707+
There will not be new requests but some maybe not finished yet,
2708+
so wait for them by trying lock/unlock
2709+
*/
2710+
BLOCK_LOCK_WR(block);
2711+
BLOCK_UNLOCK_WR(block);
2712+
26962713
mysql_rwlock_destroy(&query->lock);
26972714
block= block->next;
26982715
} while (block != queries_blocks);

0 commit comments

Comments
 (0)