mirror of
https://github.com/MariaDB/server.git
synced 2025-01-18 04:53:01 +01:00
36051b0106
The problem was that threads waiting on the query cache lock are not easily seen due to the lack of a state indicating that the thread is waiting on the said lock. This made it difficult for users to quickly spot (for example, via SHOW PROCESSLIST) a query cache contention problem. The solution is to update the thread state when the query cache lock needs to be acquired. Whenever the lock is to be acquired, the thread state is updated to "Waiting for query cache lock" and is reset once the lock is granted or the wait is interrupted. The intention is to make query cache related hangs more evident. To further investigate query cache related locking problems, one may use PERFORMANCE_SCHEMA to track the overhead associated with the locking bits and determine which particular lock is being a contention point. sql/sql_cache.cc: Set and reset the thread state whenever a attempt to lock the query cache is made. Use DEBUG_SYNC instead of the now unnecessary wait_for_kill hack.
218 lines
8.5 KiB
Text
218 lines
8.5 KiB
Text
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 debug_sync="wait_in_query_cache_insert SIGNAL parked WAIT_FOR go";
|
|
select SQL_CACHE * from t1;;
|
|
set debug_sync="now WAIT_FOR parked";
|
|
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.
|
|
set debug_sync="now SIGNAL go";
|
|
Show query cache status.
|
|
show status like 'Qcache_queries_in_cache';
|
|
Variable_name Value
|
|
Qcache_queries_in_cache 0
|
|
a
|
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
|
set debug_sync= 'RESET';
|
|
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 DEBUG_SYNC = "wait_after_query_cache_invalidate SIGNAL parked WAIT_FOR go";
|
|
# 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
|
|
SET DEBUG_SYNC = "now WAIT_FOR parked";
|
|
# 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
|
|
SET DEBUG_SYNC = "now SIGNAL go";
|
|
# 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
|
|
SET DEBUG_SYNC= 'RESET';
|
|
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 DEBUG_SYNC="wait_in_query_cache_invalidate1 SIGNAL parked1_1 WAIT_FOR go1_1";
|
|
SET DEBUG_SYNC="wait_in_query_cache_invalidate2 SIGNAL parked1_2 WAIT_FOR go1_2";
|
|
DELETE FROM t1 WHERE a like '%a%';;
|
|
=================================== Connection default
|
|
** Assert that the expect process status is obtained.
|
|
SET DEBUG_SYNC="now WAIT_FOR parked1_1";
|
|
**
|
|
=================================== 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 DEBUG_SYNC="wait_in_query_cache_insert SIGNAL parked2 WAIT_FOR go2";
|
|
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 DEBUG_SYNC="wait_in_query_cache_insert SIGNAL parked3 WAIT_FOR go3";
|
|
SELECT SQL_CACHE * FROM t4 UNION SELECT * FROM t5;;
|
|
=================================== Connection default
|
|
** Assert that the two SELECT-stmt threads to reach the hook.
|
|
SET DEBUG_SYNC="now WAIT_FOR parked2";
|
|
SET DEBUG_SYNC="now WAIT_FOR parked3";
|
|
**
|
|
**
|
|
** 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.
|
|
SET DEBUG_SYNC="now SIGNAL go1_1";
|
|
** Assert that we reach the next debug hook.
|
|
SET DEBUG_SYNC="now WAIT_FOR parked1_2";
|
|
**
|
|
** 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.
|
|
SET DEBUG_SYNC="now SIGNAL go2";
|
|
SET DEBUG_SYNC="now SIGNAL go3";
|
|
**
|
|
** 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.
|
|
SET DEBUG_SYNC="now SIGNAL go1_2";
|
|
**
|
|
*************************************************************************
|
|
** 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 DEBUG_SYNC= 'RESET';
|
|
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;
|
|
#
|
|
# Bug#56822: Add a thread state for sessions waiting on the query cache lock
|
|
#
|
|
SET @old_query_cache_size= @@GLOBAL.query_cache_size;
|
|
DROP TABLE IF EXISTS t1;
|
|
CREATE TABLE t1 (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 DEBUG_SYNC = "wait_in_query_cache_invalidate2 SIGNAL parked WAIT_FOR go";
|
|
# Send INSERT, will wait in the query cache table invalidation
|
|
INSERT INTO t1 VALUES (4);;
|
|
# Switch to connection default
|
|
# Wait for insert to reach the debug point
|
|
SET DEBUG_SYNC = "now WAIT_FOR parked";
|
|
# Switch to connection con2
|
|
# Send a query that should wait on the query cache lock
|
|
RESET QUERY CACHE;
|
|
# Switch to connection default
|
|
# Wait for the state to be reflected in the processlist
|
|
# Signal that the query cache can be unlocked
|
|
SET DEBUG_SYNC="now SIGNAL go";
|
|
# Reap con1 and disconnect
|
|
# Reap con2 and disconnect
|
|
# Restore defaults
|
|
SET DEBUG_SYNC= 'RESET';
|
|
RESET QUERY CACHE;
|
|
DROP TABLE t1;
|
|
SET GLOBAL query_cache_size= DEFAULT;
|
|
SET GLOBAL query_cache_type= DEFAULT;
|