Commit a3561354 authored by unknown's avatar unknown

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.


mysql-test/r/query_cache.result:
  Added test for verifying that 'SET query_cache_size= 0' while inserting new
  statements into the query cache won't cause the  server to crash.
mysql-test/t/query_cache.test:
  Added test for verifying that 'SET query_cache_size= 0' while inserting new
  statements into the query cache won't cause the  server to crash.
sql/sql_cache.cc:
  - Applying a block level lock iteration of all query-type blocks prevents
   writers and readers from crashing when the query cache is removed.
parent 54953ec2
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
-- 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;
......
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