flush status; set query_cache_type=DEMAND; set global query_cache_size= 1024*768; 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 0 set global query_cache_size= 0; use test; drop table t1; SET @old_concurrent_insert= @@GLOBAL.concurrent_insert; SET @old_query_cache_size= @@GLOBAL.query_cache_size; DROP TABLE IF EXISTS t1, t2; CREATE TABLE t1 (a INT); CREATE TABLE t2 (a INT); INSERT INTO t1 VALUES (1),(2),(3); SET GLOBAL concurrent_insert= 1; SET GLOBAL query_cache_size= 1024*512; SET GLOBAL query_cache_type= ON; # Switch to connection con1 SET SESSION debug='+d,wait_after_query_cache_invalidate'; # Send concurrent insert, will wait in the query cache table invalidate INSERT INTO t1 VALUES (4); # Switch to connection default # Wait for concurrent insert to reach the debug point # Switch to connection con2 # Send SELECT that shouldn't be cached SELECT * FROM t1; a 1 2 3 # Switch to connection default # Notify the concurrent insert to proceed SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE = 'wait_after_query_cache_invalidate' INTO @thread_id; KILL QUERY @thread_id; # Switch to connection con1 # Gather insert result SHOW STATUS LIKE "Qcache_queries_in_cache"; Variable_name Value Qcache_queries_in_cache 0 # Test that it's cacheable SELECT * FROM t1; a 1 2 3 4 SHOW STATUS LIKE "Qcache_queries_in_cache"; Variable_name Value Qcache_queries_in_cache 1 # Disconnect # Restore defaults RESET QUERY CACHE; DROP TABLE t1,t2; SET GLOBAL concurrent_insert= DEFAULT; SET GLOBAL query_cache_size= DEFAULT; SET GLOBAL query_cache_type= DEFAULT; # # Bug43758 Query cache can lock up threads in 'freeing items' state # FLUSH STATUS; SET GLOBAL query_cache_type=DEMAND; SET GLOBAL query_cache_size= 1024*768; DROP TABLE IF EXISTS t1,t2,t3,t4,t5; CREATE TABLE t1 (a VARCHAR(100)); CREATE TABLE t2 (a VARCHAR(100)); CREATE TABLE t3 (a VARCHAR(100)); CREATE TABLE t4 (a VARCHAR(100)); CREATE TABLE t5 (a VARCHAR(100)); INSERT INTO t1 VALUES ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'); INSERT INTO t2 VALUES ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'); INSERT INTO t3 VALUES ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'); INSERT INTO t4 VALUES ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'); INSERT INTO t5 VALUES ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'); =================================== Connection thd1 ** ** Load Query Cache with a result set and one table. ** SELECT SQL_CACHE * FROM t1; a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ************************************************************************* ** We want to accomplish the following state: ** - Query cache status: TABLE_FLUSH_IN_PROGRESS ** - THD1: invalidate_table_internal (iterating query blocks) ** - THD2: query_cache_insert (cond_wait) ** - THD3: query_cache_insert (cond_wait) ** - No thread should be holding the structure_guard_mutex. ** ** First step is to place a DELETE-statement on the debug hook just ** before the mutex lock in invalidate_table_internal. ** This will allow new result sets to be written into the QC. ** SET SESSION debug='+d,wait_in_query_cache_invalidate1'; SET SESSION debug='+d,wait_in_query_cache_invalidate2'; DELETE FROM t1 WHERE a like '%a%';; =================================== Connection default ** Assert that the expect process status is obtained. ** =================================== Connection thd2 ** On THD2: Insert a result into the cache. This attempt will be blocked ** because of a debug hook placed just before the mutex lock after which ** the first part of the result set is written. SET SESSION debug='+d,wait_in_query_cache_insert'; SELECT SQL_CACHE * FROM t2 UNION SELECT * FROM t3; =================================== Connection thd3 ** On THD3: Insert another result into the cache and block on the same ** debug hook. SET SESSION debug='+d,wait_in_query_cache_insert'; SELECT SQL_CACHE * FROM t4 UNION SELECT * FROM t5;; =================================== Connection default ** Assert that the two SELECT-stmt threads to reach the hook. ** ** ** Signal the DELETE thread, THD1, to continue. It will enter the mutex ** lock and set query cache status to TABLE_FLUSH_IN_PROGRESS and then ** unlock the mutex before stopping on the next debug hook. SELECT SQL_NO_CACHE id FROM information_schema.processlist WHERE state='wait_in_query_cache_invalidate1' LIMIT 1 INTO @flush_thread_id; KILL QUERY @flush_thread_id; ** Assert that we reach the next debug hook. ** ** Signal the remaining debug hooks blocking THD2 and THD3. ** The threads will grab the guard mutex enter the wait condition and ** and finally release the mutex. The threads will continue to wait ** until a broadcast signal reaches them causing both threads to ** come alive and check the condition. SELECT SQL_NO_CACHE id FROM information_schema.processlist WHERE state='wait_in_query_cache_insert' ORDER BY id ASC LIMIT 1 INTO @thread_id; KILL QUERY @thread_id; SELECT SQL_NO_CACHE id FROM information_schema.processlist WHERE state='wait_in_query_cache_insert' ORDER BY id DESC LIMIT 1 INTO @thread_id; KILL QUERY @thread_id; ** ** Finally signal the DELETE statement on THD1 one last time. ** The stmt will complete the query cache invalidation and return ** cache status to NO_FLUSH_IN_PROGRESS. On the status change ** One signal will be sent to the thread group waiting for executing ** invalidations and a broadcast signal will be sent to the thread ** group holding result set writers. SELECT SQL_NO_CACHE id FROM information_schema.processlist WHERE state='wait_in_query_cache_invalidate2' LIMIT 1 INTO @flush_thread_id; KILL QUERY @flush_thread_id; ** ************************************************************************* ** No tables should be locked =================================== Connection thd2 a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb DELETE FROM t1; DELETE FROM t2; DELETE FROM t3; =================================== Connection thd3 a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb DELETE FROM t4; DELETE FROM t5; =================================== Connection thd1 ** Done. SET GLOBAL query_cache_size= 0; # Restore defaults RESET QUERY CACHE; FLUSH STATUS; DROP TABLE t1,t2,t3,t4,t5; SET GLOBAL query_cache_size= DEFAULT; SET GLOBAL query_cache_type= DEFAULT;