List:Commits« Previous MessageNext Message »
From:kpettersson Date:December 7 2007 10:32am
Subject:bk commit into 5.1 tree (thek:1.2649) BUG#30887
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of thek. When thek does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-12-07 11:31:55+01:00, thek@adventure.(none) +3 -0
  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@stripped, 2007-12-07 11:31:53+01:00, thek@adventure.(none) +23 -0
    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@stripped, 2007-12-07 11:31:53+01:00, thek@adventure.(none) +40 -0
    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@stripped, 2007-12-07 11:31:53+01:00, thek@adventure.(none) +55 -1
    - Applying a block level lock iteration of all query-type blocks prevents
     writers and readers from crashing when the query cache is removed. 

diff -Nrup a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result
--- a/mysql-test/r/query_cache.result	2007-10-23 16:28:44 +02:00
+++ b/mysql-test/r/query_cache.result	2007-12-07 11:31:53 +01:00
@@ -1669,4 +1669,27 @@ 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
+use test;
+drop table t1;
 End of 5.1 tests
diff -Nrup a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test
--- a/mysql-test/t/query_cache.test	2007-10-23 16:28:45 +02:00
+++ b/mysql-test/t/query_cache.test	2007-12-07 11:31:53 +01:00
@@ -1316,6 +1316,46 @@ 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;
+use test;
+drop table t1;
 
 --echo End of 5.1 tests
 
diff -Nrup a/sql/sql_cache.cc b/sql/sql_cache.cc
--- a/sql/sql_cache.cc	2007-10-29 15:46:43 +01:00
+++ b/sql/sql_cache.cc	2007-12-07 11:31:53 +01:00
@@ -371,6 +371,31 @@ 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;
+  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,6 +672,9 @@ void query_cache_insert(NET *net, const 
   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);
@@ -667,11 +695,11 @@ void query_cache_insert(NET *net, const 
     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 +715,7 @@ void query_cache_insert(NET *net, const 
     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 +912,31 @@ ulong Query_cache::resize(ulong query_ca
   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;
Thread
bk commit into 5.1 tree (thek:1.2649) BUG#30887kpettersson7 Dec
  • Re: bk commit into 5.1 tree (thek:1.2649) BUG#30887Davi Arnaut7 Dec