diff --git a/mysql-test/r/query_cache_debug.result b/mysql-test/r/query_cache_debug.result index c90165368e3..b03a71d3fec 100644 --- a/mysql-test/r/query_cache_debug.result +++ b/mysql-test/r/query_cache_debug.result @@ -22,3 +22,52 @@ 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; diff --git a/mysql-test/suite/rpl/r/rpl_row_until.result b/mysql-test/suite/rpl/r/rpl_row_until.result index be1ec51f74a..ad54450af74 100644 --- a/mysql-test/suite/rpl/r/rpl_row_until.result +++ b/mysql-test/suite/rpl/r/rpl_row_until.result @@ -4,16 +4,17 @@ reset master; reset slave; drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; start slave; +CREATE TABLE t1(n INT NOT NULL AUTO_INCREMENT PRIMARY KEY); +INSERT INTO t1 VALUES (1),(2),(3),(4); +DROP TABLE t1; +CREATE TABLE t2(n INT NOT NULL AUTO_INCREMENT PRIMARY KEY); +INSERT INTO t2 VALUES (1),(2); +INSERT INTO t2 VALUES (3),(4); +DROP TABLE t2; include/stop_slave.inc -create table t1(n int not null auto_increment primary key); -insert into t1 values (1),(2),(3),(4); -drop table t1; -create table t2(n int not null auto_increment primary key); -insert into t2 values (1),(2); -insert into t2 values (3),(4); -drop table t2; -start slave until master_log_file='master-bin.000001', master_log_pos=311; -select * from t1; +RESET SLAVE; +START SLAVE UNTIL MASTER_LOG_FILE='master-bin.000001', MASTER_LOG_POS=master_pos_drop_t1 +SELECT * FROM t1; n 1 2 @@ -23,10 +24,10 @@ SHOW SLAVE STATUS; Slave_IO_State # Master_Host 127.0.0.1 Master_User root -Master_Port MASTER_PORT +Master_Port # Connect_Retry 1 Master_Log_File master-bin.000001 -Read_Master_Log_Pos 1161 +Read_Master_Log_Pos # Relay_Log_File # Relay_Log_Pos # Relay_Master_Log_File master-bin.000001 @@ -41,11 +42,11 @@ Replicate_Wild_Ignore_Table Last_Errno 0 Last_Error Skip_Counter 0 -Exec_Master_Log_Pos 454 +Exec_Master_Log_Pos MASTER_POS_DROP_T1 Relay_Log_Space # Until_Condition Master Until_Log_File master-bin.000001 -Until_Log_Pos 311 +Until_Log_Pos MASTER_POS_DROP_T1 Master_SSL_Allowed No Master_SSL_CA_File Master_SSL_CA_Path @@ -58,8 +59,8 @@ Last_IO_Errno # Last_IO_Error # Last_SQL_Errno 0 Last_SQL_Error -start slave until master_log_file='master-no-such-bin.000001', master_log_pos=291; -select * from t1; +START SLAVE UNTIL MASTER_LOG_FILE='master-no-such-bin.000001', MASTER_LOG_POS=291; +SELECT * FROM t1; n 1 2 @@ -69,10 +70,10 @@ SHOW SLAVE STATUS; Slave_IO_State # Master_Host 127.0.0.1 Master_User root -Master_Port MASTER_PORT +Master_Port # Connect_Retry 1 Master_Log_File master-bin.000001 -Read_Master_Log_Pos 1161 +Read_Master_Log_Pos # Relay_Log_File # Relay_Log_Pos # Relay_Master_Log_File master-bin.000001 @@ -87,7 +88,7 @@ Replicate_Wild_Ignore_Table Last_Errno 0 Last_Error Skip_Counter 0 -Exec_Master_Log_Pos 454 +Exec_Master_Log_Pos MASTER_POS_DROP_T1 Relay_Log_Space # Until_Condition Master Until_Log_File master-no-such-bin.000001 @@ -104,8 +105,8 @@ Last_IO_Errno # Last_IO_Error # Last_SQL_Errno 0 Last_SQL_Error -start slave until relay_log_file='slave-relay-bin.000004', relay_log_pos=1014; -select * from t2; +START SLAVE UNTIL RELAY_LOG_FILE='slave-relay-bin.000002', RELAY_LOG_POS=relay_pos_insert1_t2 +SELECT * FROM t2; n 1 2 @@ -113,10 +114,10 @@ SHOW SLAVE STATUS; Slave_IO_State # Master_Host 127.0.0.1 Master_User root -Master_Port MASTER_PORT +Master_Port # Connect_Retry 1 Master_Log_File master-bin.000001 -Read_Master_Log_Pos 1161 +Read_Master_Log_Pos # Relay_Log_File # Relay_Log_Pos # Relay_Master_Log_File master-bin.000001 @@ -131,11 +132,11 @@ Replicate_Wild_Ignore_Table Last_Errno 0 Last_Error Skip_Counter 0 -Exec_Master_Log_Pos 868 +Exec_Master_Log_Pos MASTER_POS_INSERT1_T2 Relay_Log_Space # Until_Condition Relay -Until_Log_File slave-relay-bin.000004 -Until_Log_Pos 1014 +Until_Log_File slave-relay-bin.000002 +Until_Log_Pos RELAY_POS_INSERT1_T2 Master_SSL_Allowed No Master_SSL_CA_File Master_SSL_CA_Path @@ -148,17 +149,17 @@ Last_IO_Errno # Last_IO_Error # Last_SQL_Errno 0 Last_SQL_Error -start slave; +START SLAVE; include/stop_slave.inc -start slave sql_thread until master_log_file='master-bin.000001', master_log_pos=740; +START SLAVE SQL_THREAD UNTIL MASTER_LOG_FILE='master-bin.000001', MASTER_LOG_POS=master_pos_create_t2 SHOW SLAVE STATUS; Slave_IO_State # Master_Host 127.0.0.1 Master_User root -Master_Port MASTER_PORT +Master_Port # Connect_Retry 1 Master_Log_File master-bin.000001 -Read_Master_Log_Pos 1161 +Read_Master_Log_Pos # Relay_Log_File # Relay_Log_Pos # Relay_Master_Log_File master-bin.000001 @@ -173,11 +174,11 @@ Replicate_Wild_Ignore_Table Last_Errno 0 Last_Error Skip_Counter 0 -Exec_Master_Log_Pos 1161 +Exec_Master_Log_Pos MASTER_POS_DROP_T2 Relay_Log_Space # Until_Condition Master Until_Log_File master-bin.000001 -Until_Log_Pos 740 +Until_Log_Pos MASTER_POS_CREATE_T2 Master_SSL_Allowed No Master_SSL_CA_File Master_SSL_CA_Path @@ -190,17 +191,17 @@ Last_IO_Errno # Last_IO_Error # Last_SQL_Errno 0 Last_SQL_Error -start slave until master_log_file='master-bin', master_log_pos=561; +START SLAVE UNTIL MASTER_LOG_FILE='master-bin', MASTER_LOG_POS=561; ERROR HY000: Incorrect parameter or combination of parameters for START SLAVE UNTIL -start slave until master_log_file='master-bin.000001', master_log_pos=561, relay_log_pos=12; +START SLAVE UNTIL MASTER_LOG_FILE='master-bin.000001', MASTER_LOG_POS=561, RELAY_LOG_POS=12; ERROR HY000: Incorrect parameter or combination of parameters for START SLAVE UNTIL -start slave until master_log_file='master-bin.000001'; +START SLAVE UNTIL MASTER_LOG_FILE='master-bin.000001'; ERROR HY000: Incorrect parameter or combination of parameters for START SLAVE UNTIL -start slave until relay_log_file='slave-relay-bin.000002'; +START SLAVE UNTIL RELAY_LOG_FILE='slave-relay-bin.000009'; ERROR HY000: Incorrect parameter or combination of parameters for START SLAVE UNTIL -start slave until relay_log_file='slave-relay-bin.000002', master_log_pos=561; +START SLAVE UNTIL RELAY_LOG_FILE='slave-relay-bin.000002', MASTER_LOG_POS=561; ERROR HY000: Incorrect parameter or combination of parameters for START SLAVE UNTIL -start slave; -start slave until master_log_file='master-bin.000001', master_log_pos=740; +START SLAVE; +START SLAVE UNTIL MASTER_LOG_FILE='master-bin.000001', MASTER_LOG_POS=740; Warnings: Note 1254 Slave is already running diff --git a/mysql-test/suite/rpl/t/rpl_row_until.test b/mysql-test/suite/rpl/t/rpl_row_until.test index d89de7d9ebd..fe859218ed3 100644 --- a/mysql-test/suite/rpl/t/rpl_row_until.test +++ b/mysql-test/suite/rpl/t/rpl_row_until.test @@ -2,76 +2,115 @@ -- source include/have_binlog_format_row.inc -- source include/master-slave.inc -# Test is dependent on binlog positions +# Note: The test is dependent on binlog positions # prepare version for substitutions let $VERSION=`select version()`; -# stop slave before he will start replication also sync with master -# for avoiding undetermenistic behaviour +# Create some events on master +connection master; +CREATE TABLE t1(n INT NOT NULL AUTO_INCREMENT PRIMARY KEY); +INSERT INTO t1 VALUES (1),(2),(3),(4); +DROP TABLE t1; +# Save master log postion for query DROP TABLE t1 +save_master_pos; +let $master_pos_drop_t1= query_get_value(SHOW BINLOG EVENTS, Pos, 7); + +CREATE TABLE t2(n INT NOT NULL AUTO_INCREMENT PRIMARY KEY); +# Save master log postion for query CREATE TABLE t2 +save_master_pos; +let $master_pos_create_t2= query_get_value(SHOW BINLOG EVENTS, Pos, 8); + +INSERT INTO t2 VALUES (1),(2); +save_master_pos; +# Save master log postion for query INSERT INTO t2 VALUES (1),(2); +let $master_pos_insert1_t2= query_get_value(SHOW BINLOG EVENTS, End_log_pos, 12); +sync_slave_with_master; + +# Save relay log postion for query INSERT INTO t2 VALUES (1),(2); +let $relay_pos_insert1_t2= query_get_value(show slave status, Relay_Log_Pos, 1); + +connection master; +INSERT INTO t2 VALUES (3),(4); +DROP TABLE t2; +# Save master log postion for query INSERT INTO t2 VALUES (1),(2); +let $master_pos_drop_t2= query_get_value(SHOW BINLOG EVENTS, End_log_pos, 17); +sync_slave_with_master; + +--source include/stop_slave.inc +# Reset slave. +RESET SLAVE; +--disable_query_log +eval CHANGE MASTER TO MASTER_USER='root', MASTER_CONNECT_RETRY=1, MASTER_HOST='127.0.0.1', MASTER_PORT=$MASTER_MYPORT; +--enable_query_log + +# Try to replicate all queries until drop of t1 +connection slave; +echo START SLAVE UNTIL MASTER_LOG_FILE='master-bin.000001', MASTER_LOG_POS=master_pos_drop_t1; +--disable_query_log +eval START SLAVE UNTIL MASTER_LOG_FILE='master-bin.000001', MASTER_LOG_POS=$master_pos_drop_t1; +--enable_query_log +--source include/wait_for_slave_sql_to_stop.inc + +# Here table should be still not deleted +SELECT * FROM t1; +--replace_result $master_pos_drop_t1 MASTER_POS_DROP_T1 +--replace_column 1 # 4 # 7 # 8 # 9 # 16 # 23 # 33 # 35 # 36 # +query_vertical SHOW SLAVE STATUS; + +# This should fail right after start +START SLAVE UNTIL MASTER_LOG_FILE='master-no-such-bin.000001', MASTER_LOG_POS=291; +--source include/wait_for_slave_sql_to_stop.inc +# again this table should be still not deleted +SELECT * FROM t1; +--replace_result $master_pos_drop_t1 MASTER_POS_DROP_T1 +--replace_column 1 # 4 # 7 # 8 # 9 # 16 # 23 # 33 # 35 # 36 # +query_vertical SHOW SLAVE STATUS; + +# Try replicate all up to and not including the second insert to t2; +echo START SLAVE UNTIL RELAY_LOG_FILE='slave-relay-bin.000002', RELAY_LOG_POS=relay_pos_insert1_t2; +--disable_query_log +eval START SLAVE UNTIL RELAY_LOG_FILE='slave-relay-bin.000002', RELAY_LOG_POS=$relay_pos_insert1_t2; +--enable_query_log +--source include/wait_for_slave_sql_to_stop.inc +SELECT * FROM t2; +--replace_result $relay_pos_insert1_t2 RELAY_POS_INSERT1_T2 $master_pos_insert1_t2 MASTER_POS_INSERT1_T2 +--replace_column 1 # 4 # 7 # 8 # 9 # 16 # 23 # 33 # 35 # 36 # +query_vertical SHOW SLAVE STATUS; + +# clean up +START SLAVE; +--source include/wait_for_slave_to_start.inc +connection master; sync_slave_with_master; --source include/stop_slave.inc -connection master; -# create some events on master -create table t1(n int not null auto_increment primary key); -insert into t1 values (1),(2),(3),(4); -drop table t1; -create table t2(n int not null auto_increment primary key); -insert into t2 values (1),(2); -insert into t2 values (3),(4); -drop table t2; - -# try to replicate all queries until drop of t1 -connection slave; -start slave until master_log_file='master-bin.000001', master_log_pos=311; ---source include/wait_for_slave_sql_to_stop.inc -# here table should be still not deleted -select * from t1; -source include/show_slave_status.inc; - -# this should fail right after start -start slave until master_log_file='master-no-such-bin.000001', master_log_pos=291; ---source include/wait_for_slave_sql_to_stop.inc -# again this table should be still not deleted -select * from t1; -source include/show_slave_status.inc; - -# try replicate all up to and not including the second insert to t2; -start slave until relay_log_file='slave-relay-bin.000004', relay_log_pos=1014; ---source include/wait_for_slave_sql_to_stop.inc -select * from t2; -source include/show_slave_status.inc; - -# clean up -start slave; -connection master; -save_master_pos; -connection slave; -sync_with_master; ---source include/stop_slave.inc - -# this should stop immediately as we are already there -start slave sql_thread until master_log_file='master-bin.000001', master_log_pos=740; ---let $slave_param= Until_Log_Pos ---let $slave_param_value= 740 +# This should stop immediately as we are already there +echo START SLAVE SQL_THREAD UNTIL MASTER_LOG_FILE='master-bin.000001', MASTER_LOG_POS=master_pos_create_t2; +--disable_query_log +eval START SLAVE SQL_THREAD UNTIL MASTER_LOG_FILE='master-bin.000001', MASTER_LOG_POS=$master_pos_create_t2; +--enable_query_log +let $slave_param= Until_Log_Pos; +let $slave_param_value= $master_pos_create_t2; --source include/wait_for_slave_param.inc --source include/wait_for_slave_sql_to_stop.inc # here the sql slave thread should be stopped --replace_result bin.000005 bin.000004 bin.000006 bin.000004 bin.000007 bin.000004 -source include/show_slave_status.inc; +--replace_result $master_pos_create_t2 MASTER_POS_CREATE_T2 $master_pos_drop_t2 MASTER_POS_DROP_T2 +--replace_column 1 # 4 # 7 # 8 # 9 # 16 # 23 # 33 # 35 # 36 # +query_vertical SHOW SLAVE STATUS; #testing various error conditions --error 1277 -start slave until master_log_file='master-bin', master_log_pos=561; +START SLAVE UNTIL MASTER_LOG_FILE='master-bin', MASTER_LOG_POS=561; --error 1277 -start slave until master_log_file='master-bin.000001', master_log_pos=561, relay_log_pos=12; +START SLAVE UNTIL MASTER_LOG_FILE='master-bin.000001', MASTER_LOG_POS=561, RELAY_LOG_POS=12; --error 1277 -start slave until master_log_file='master-bin.000001'; +START SLAVE UNTIL MASTER_LOG_FILE='master-bin.000001'; --error 1277 -start slave until relay_log_file='slave-relay-bin.000002'; +START SLAVE UNTIL RELAY_LOG_FILE='slave-relay-bin.000009'; --error 1277 -start slave until relay_log_file='slave-relay-bin.000002', master_log_pos=561; +START SLAVE UNTIL RELAY_LOG_FILE='slave-relay-bin.000002', MASTER_LOG_POS=561; # Warning should be given for second command -start slave; -start slave until master_log_file='master-bin.000001', master_log_pos=740; +START SLAVE; +START SLAVE UNTIL MASTER_LOG_FILE='master-bin.000001', MASTER_LOG_POS=740; diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index f61090102ff..3f61176e37b 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -10,5 +10,4 @@ # ############################################################################## kill : Bug#37780 2008-12-03 HHunger need some changes to be robust enough for pushbuild. -query_cache_28249 : Bug#41098 Query Cache returns wrong result with concurrent insert innodb_bug39438 : BUG#42383 2009-01-28 lsoares "This fails in embedded and on windows. Note that this test is not run on windows and on embedded in PB for main trees currently" diff --git a/mysql-test/t/query_cache_debug.test b/mysql-test/t/query_cache_debug.test index 18dfe487ac3..8cf5e9d4b16 100644 --- a/mysql-test/t/query_cache_debug.test +++ b/mysql-test/t/query_cache_debug.test @@ -44,3 +44,71 @@ set global query_cache_size= 0; use test; drop table t1; +# +# Bug#41098: Query Cache returns wrong result with concurrent insert +# + +SET @old_concurrent_insert= @@GLOBAL.concurrent_insert; +SET @old_query_cache_size= @@GLOBAL.query_cache_size; + +--disable_warnings +DROP TABLE IF EXISTS t1, t2; +--enable_warnings +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; + +connect(con1,localhost,root,,test,,); +connect(con2,localhost,root,,test,,); + +connection con1; +--echo # Switch to connection con1 +SET SESSION debug='+d,wait_after_query_cache_invalidate'; +--echo # Send concurrent insert, will wait in the query cache table invalidate +--send INSERT INTO t1 VALUES (4) + +connection default; +--echo # Switch to connection default +--echo # Wait for concurrent insert to reach the debug point +let $wait_condition= + SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST + WHERE STATE = "wait_after_query_cache_invalidate" AND + INFO = "INSERT INTO t1 VALUES (4)"; +--source include/wait_condition.inc + +connection con2; +--echo # Switch to connection con2 +--echo # Send SELECT that shouldn't be cached +SELECT * FROM t1; + +connection default; +--echo # Switch to connection default +--echo # 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; + +connection con1; +--echo # Switch to connection con1 +--echo # Gather insert result +--reap +SHOW STATUS LIKE "Qcache_queries_in_cache"; +--echo # Test that it's cacheable +SELECT * FROM t1; +SHOW STATUS LIKE "Qcache_queries_in_cache"; + +--echo # Disconnect +disconnect con1; +disconnect con2; + +connection default; +--echo # 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; diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 7c97ee4cf32..4a5b75c4f78 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1518,6 +1518,9 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used, invalidate_table(thd, tables_used); } + DBUG_EXECUTE_IF("wait_after_query_cache_invalidate", + debug_wait_for_kill("wait_after_query_cache_invalidate");); + DBUG_VOID_RETURN; } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index fcf86edeaa9..e712727a78b 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -904,20 +904,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, } DBUG_ASSERT(transactional_table || !changed || thd->transaction.stmt.modified_non_trans_table); - - if (thd->lock) - { - /* - Invalidate the table in the query cache if something changed - after unlocking when changes become fisible. - TODO: this is workaround. right way will be move invalidating in - the unlock procedure. - */ - if (lock_type == TL_WRITE_CONCURRENT_INSERT && changed) - { - query_cache_invalidate3(thd, table_list, 1); - } - } } thd_proc_info(thd, "end"); /* diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index 4073044bf63..ad06ea4b7f0 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -2152,6 +2152,15 @@ my_bool ha_myisam::register_query_cache_table(THD *thd, char *table_name, } } + /* + This query execution might have started after the query cache was flushed + by a concurrent INSERT. In this case, don't cache this statement as the + data file length difference might not be visible yet if the tables haven't + been unlocked by the concurrent insert thread. + */ + if (file->state->uncacheable) + DBUG_RETURN(FALSE); + /* It is ok to try to cache current statement. */ DBUG_RETURN(TRUE); } diff --git a/storage/myisam/mi_locking.c b/storage/myisam/mi_locking.c index ec359d13a14..6a4c21160f4 100644 --- a/storage/myisam/mi_locking.c +++ b/storage/myisam/mi_locking.c @@ -293,6 +293,8 @@ void mi_get_status(void* param, int concurrent_insert) info->save_state=info->s->state.state; info->state= &info->save_state; info->append_insert_at_end= concurrent_insert; + if (concurrent_insert) + info->s->state.state.uncacheable= TRUE; DBUG_VOID_RETURN; } diff --git a/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h index 9af3f652c5f..b64c7f6bd7f 100644 --- a/storage/myisam/myisamdef.h +++ b/storage/myisam/myisamdef.h @@ -38,6 +38,7 @@ typedef struct st_mi_status_info my_off_t key_file_length; my_off_t data_file_length; ha_checksum checksum; + my_bool uncacheable; /* Active concurrent insert */ } MI_STATUS_INFO; typedef struct st_mi_state_info