mirror of
https://github.com/MariaDB/server.git
synced 2025-01-19 13:32:33 +01:00
Bug#30887 Server crashes on SET GLOBAL query_cache_size=0
Reseting the query cache by issuing a SET GLOBAL query_cache_size=0 caused the server to crash if a the server concurrently was saving a new result set to the query cache. The reason for this was that the invalidation wasn't waiting on the result writers to release the block level locks on the query cache.
This commit is contained in:
parent
b1428496df
commit
96146a392f
5 changed files with 195 additions and 2 deletions
35
mysql-test/r/bdb_notembedded.result
Normal file
35
mysql-test/r/bdb_notembedded.result
Normal file
|
@ -0,0 +1,35 @@
|
|||
set autocommit=1;
|
||||
reset master;
|
||||
create table bug16206 (a int);
|
||||
insert into bug16206 values(1);
|
||||
start transaction;
|
||||
insert into bug16206 values(2);
|
||||
commit;
|
||||
show binlog events;
|
||||
Log_name Pos Event_type Server_id End_log_pos Info
|
||||
f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4
|
||||
f n Query 1 n use `test`; create table bug16206 (a int)
|
||||
f n Query 1 n use `test`; insert into bug16206 values(1)
|
||||
f n Query 1 n use `test`; insert into bug16206 values(2)
|
||||
drop table bug16206;
|
||||
reset master;
|
||||
create table bug16206 (a int) engine= bdb;
|
||||
insert into bug16206 values(0);
|
||||
insert into bug16206 values(1);
|
||||
start transaction;
|
||||
insert into bug16206 values(2);
|
||||
commit;
|
||||
insert into bug16206 values(3);
|
||||
show binlog events;
|
||||
Log_name Pos Event_type Server_id End_log_pos Info
|
||||
f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4
|
||||
f n Query 1 n use `test`; create table bug16206 (a int) engine= bdb
|
||||
f n Query 1 n use `test`; insert into bug16206 values(0)
|
||||
f n Query 1 n use `test`; insert into bug16206 values(1)
|
||||
f n Query 1 n use `test`; BEGIN
|
||||
f n Query 1 n use `test`; insert into bug16206 values(2)
|
||||
f n Query 1 n use `test`; COMMIT
|
||||
f n Query 1 n use `test`; insert into bug16206 values(3)
|
||||
drop table bug16206;
|
||||
set autocommit=0;
|
||||
End of 5.0 tests
|
|
@ -1669,4 +1669,28 @@ SELECT 1 FROM t1 GROUP BY
|
|||
1
|
||||
1
|
||||
DROP TABLE t1;
|
||||
flush status;
|
||||
set query_cache_type=DEMAND;
|
||||
set global query_cache_size= 1024*1024*512;
|
||||
drop table if exists t1;
|
||||
create table t1 (a varchar(100));
|
||||
insert into t1 values ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb');
|
||||
Activate debug hook and attempt to retrieve the statement from the cache.
|
||||
set session debug='+d,wait_in_query_cache_insert';
|
||||
select SQL_CACHE * from t1;;
|
||||
On a second connection; clear the query cache.
|
||||
show status like 'Qcache_queries_in_cache';
|
||||
Variable_name Value
|
||||
Qcache_queries_in_cache 1
|
||||
set global query_cache_size= 0;;
|
||||
Signal the debug hook to release the lock.
|
||||
select id from information_schema.processlist where state='wait_in_query_cache_insert' into @thread_id;
|
||||
kill query @thread_id;
|
||||
Show query cache status.
|
||||
show status like 'Qcache_queries_in_cache';
|
||||
Variable_name Value
|
||||
Qcache_queries_in_cache 1
|
||||
set global query_cache_size= 0;
|
||||
use test;
|
||||
drop table t1;
|
||||
End of 5.1 tests
|
||||
|
|
38
mysql-test/t/bdb_notembedded.test
Normal file
38
mysql-test/t/bdb_notembedded.test
Normal file
|
@ -0,0 +1,38 @@
|
|||
-- source include/not_embedded.inc
|
||||
-- source include/have_bdb.inc
|
||||
|
||||
#
|
||||
# Bug #16206: Superfluous COMMIT event in binlog when updating BDB in autocommit mode
|
||||
#
|
||||
set autocommit=1;
|
||||
|
||||
let $VERSION=`select version()`;
|
||||
|
||||
reset master;
|
||||
create table bug16206 (a int);
|
||||
insert into bug16206 values(1);
|
||||
start transaction;
|
||||
insert into bug16206 values(2);
|
||||
commit;
|
||||
--replace_result $VERSION VERSION
|
||||
--replace_column 1 f 2 n 5 n
|
||||
show binlog events;
|
||||
drop table bug16206;
|
||||
|
||||
reset master;
|
||||
create table bug16206 (a int) engine= bdb;
|
||||
insert into bug16206 values(0);
|
||||
insert into bug16206 values(1);
|
||||
start transaction;
|
||||
insert into bug16206 values(2);
|
||||
commit;
|
||||
insert into bug16206 values(3);
|
||||
--replace_result $VERSION VERSION
|
||||
--replace_column 1 f 2 n 5 n
|
||||
show binlog events;
|
||||
drop table bug16206;
|
||||
|
||||
set autocommit=0;
|
||||
|
||||
|
||||
--echo End of 5.0 tests
|
|
@ -1316,6 +1316,47 @@ SELECT 1 FROM t1 GROUP BY
|
|||
(SELECT LAST_INSERT_ID() FROM t1 ORDER BY MIN(a) ASC LIMIT 1);
|
||||
DROP TABLE t1;
|
||||
|
||||
#
|
||||
# Bug #30887 Server crashes on SET GLOBAL query_cache_size=0
|
||||
#
|
||||
flush status;
|
||||
set query_cache_type=DEMAND;
|
||||
set global query_cache_size= 1024*1024*512;
|
||||
--disable_warnings
|
||||
drop table if exists t1;
|
||||
--enable_warnings
|
||||
create table t1 (a varchar(100));
|
||||
insert into t1 values ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb');
|
||||
connect (bug30887con1, localhost, root, ,test);
|
||||
connect (bug30887con2, localhost, root, ,test);
|
||||
|
||||
connection bug30887con1;
|
||||
--echo Activate debug hook and attempt to retrieve the statement from the cache.
|
||||
set session debug='+d,wait_in_query_cache_insert';
|
||||
--send select SQL_CACHE * from t1;
|
||||
|
||||
connection default;
|
||||
let $wait_condition= select count(*)= 1 from information_schema.processlist where state= 'wait_in_query_cache_insert';
|
||||
--source include/wait_condition.inc
|
||||
|
||||
connection bug30887con2;
|
||||
--echo On a second connection; clear the query cache.
|
||||
show status like 'Qcache_queries_in_cache';
|
||||
--send set global query_cache_size= 0;
|
||||
|
||||
connection default;
|
||||
--echo Signal the debug hook to release the lock.
|
||||
select id from information_schema.processlist where state='wait_in_query_cache_insert' into @thread_id;
|
||||
kill query @thread_id;
|
||||
|
||||
--echo Show query cache status.
|
||||
show status like 'Qcache_queries_in_cache';
|
||||
|
||||
disconnect bug30887con1;
|
||||
disconnect bug30887con2;
|
||||
set global query_cache_size= 0;
|
||||
use test;
|
||||
drop table t1;
|
||||
|
||||
--echo End of 5.1 tests
|
||||
|
||||
|
|
|
@ -371,6 +371,32 @@ TODO list:
|
|||
__LINE__,(ulong)(B)));B->query()->unlock_reading();}
|
||||
#define DUMP(C) DBUG_EXECUTE("qcache", {\
|
||||
(C)->cache_dump(); (C)->queries_dump();(C)->tables_dump();})
|
||||
|
||||
|
||||
/**
|
||||
Causes the thread to wait in a spin lock for a query kill signal.
|
||||
This function is used by the test frame work to identify race conditions.
|
||||
|
||||
The signal is caught and ignored and the thread is not killed.
|
||||
*/
|
||||
|
||||
static void debug_wait_for_kill(const char *info)
|
||||
{
|
||||
DBUG_ENTER("debug_wait_for_kill");
|
||||
const char *prev_info;
|
||||
THD *thd;
|
||||
thd= current_thd;
|
||||
prev_info= thd->proc_info;
|
||||
thd->proc_info= info;
|
||||
sql_print_information(info);
|
||||
while(!thd->killed)
|
||||
my_sleep(1000);
|
||||
thd->killed= THD::NOT_KILLED;
|
||||
sql_print_information("Exit debug_wait_for_kill");
|
||||
thd->proc_info= prev_info;
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
#else
|
||||
#define MUTEX_LOCK(M) pthread_mutex_lock(M)
|
||||
#define MUTEX_UNLOCK(M) pthread_mutex_unlock(M)
|
||||
|
@ -647,13 +673,16 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
|
|||
if (net->query_cache_query == 0)
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
DBUG_EXECUTE_IF("wait_in_query_cache_insert",
|
||||
debug_wait_for_kill("wait_in_query_cache_insert"); );
|
||||
|
||||
STRUCT_LOCK(&query_cache.structure_guard_mutex);
|
||||
bool interrupt;
|
||||
query_cache.wait_while_table_flush_is_in_progress(&interrupt);
|
||||
if (interrupt)
|
||||
{
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
return;
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query;
|
||||
|
@ -667,11 +696,11 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
|
|||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
BLOCK_LOCK_WR(query_block);
|
||||
Query_cache_query *header= query_block->query();
|
||||
Query_cache_block *result= header->result();
|
||||
|
||||
DUMP(&query_cache);
|
||||
BLOCK_LOCK_WR(query_block);
|
||||
DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
|
||||
|
||||
/*
|
||||
|
@ -687,6 +716,7 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
|
|||
DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block));
|
||||
// The following call will remove the lock on query_block
|
||||
query_cache.free_query(query_block);
|
||||
query_cache.refused++;
|
||||
// append_result_data no success => we need unlock
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
DBUG_VOID_RETURN;
|
||||
|
@ -883,6 +913,31 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
|
|||
m_cache_status= Query_cache::FLUSH_IN_PROGRESS;
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
|
||||
/*
|
||||
Wait for all readers and writers to exit. When the list of all queries
|
||||
is iterated over with a block level lock, we are done.
|
||||
*/
|
||||
Query_cache_block *block= queries_blocks;
|
||||
if (block)
|
||||
{
|
||||
do
|
||||
{
|
||||
BLOCK_LOCK_WR(block);
|
||||
Query_cache_query *query= block->query();
|
||||
if (query && query->writer())
|
||||
{
|
||||
/*
|
||||
Drop the writer; this will cancel any attempts to store
|
||||
the processed statement associated with this writer.
|
||||
*/
|
||||
query->writer()->query_cache_query= 0;
|
||||
query->writer(0);
|
||||
refused++;
|
||||
}
|
||||
BLOCK_UNLOCK_WR(block);
|
||||
block= block->next;
|
||||
} while (block != queries_blocks);
|
||||
}
|
||||
free_cache();
|
||||
|
||||
query_cache_size= query_cache_size_arg;
|
||||
|
|
Loading…
Reference in a new issue