# name : show_slave_status_nolock.patch # introduced : 12 # maintainer : Oleg # #!!! notice !!! # Any small change to this file in the main branch # should be done or reviewed by the maintainer! --- /dev/null +++ b/patch_info/show_slave_status_nolock.patch @@ -0,0 +1,6 @@ +File=show_slave_status_nolock.patch +Name= SHOW SLAVE STATUS NOLOCK +Version=1.0 +Author=Percona +License=GPL +Comment= Implement SHOW SLAVE STATUS without lock (STOP SLAVE lock the same mutex what lock SHOW SLAVE STATUS) --- a/sql/lex.h +++ b/sql/lex.h @@ -378,6 +378,7 @@ { "NONE", SYM(NONE_SYM)}, { "NOT", SYM(NOT_SYM)}, { "NO_WRITE_TO_BINLOG", SYM(NO_WRITE_TO_BINLOG)}, + { "NOLOCK", SYM(NOLOCK_SYM)}, { "NULL", SYM(NULL_SYM)}, { "NUMERIC", SYM(NUMERIC_SYM)}, { "NVARCHAR", SYM(NVARCHAR_SYM)}, --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2971,6 +2971,7 @@ {"show_relaylog_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_RELAYLOG_EVENTS]), SHOW_LONG_STATUS}, {"show_slave_hosts", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_HOSTS]), SHOW_LONG_STATUS}, {"show_slave_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_STAT]), SHOW_LONG_STATUS}, + {"show_slave_status_nolock", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_NOLOCK_STAT]), SHOW_LONG_STATUS}, {"show_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS}, {"show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS}, {"show_table_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATUS]), SHOW_LONG_STATUS}, --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -190,6 +190,8 @@ SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES, SQLCOM_SIGNAL, SQLCOM_RESIGNAL, SQLCOM_SHOW_RELAYLOG_EVENTS, + /* SHOW SLAVE STATUS NOLOCK */ + SQLCOM_SHOW_SLAVE_NOLOCK_STAT, /* When a command is added here, be sure it's also added in mysqld.cc in "struct show_var_st status_vars[]= {" ... --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -336,6 +336,7 @@ sql_command_flags[SQLCOM_SHOW_CREATE]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_SLAVE_STAT]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_SLAVE_NOLOCK_STAT]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE_TRIGGER]= CF_STATUS_COMMAND; @@ -2393,12 +2394,17 @@ mysql_mutex_unlock(&LOCK_active_mi); break; } + case SQLCOM_SHOW_SLAVE_NOLOCK_STAT: case SQLCOM_SHOW_SLAVE_STAT: { /* Accept one of two privileges */ if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL)) goto error; - mysql_mutex_lock(&LOCK_active_mi); + bool do_lock=SQLCOM_SHOW_SLAVE_NOLOCK_STAT != lex->sql_command; + if(do_lock) + { + mysql_mutex_lock(&LOCK_active_mi); + } if (active_mi != NULL) { res = show_master_info(thd, active_mi); @@ -2409,7 +2415,19 @@ WARN_NO_MASTER_INFO, ER(WARN_NO_MASTER_INFO)); my_ok(thd); } - mysql_mutex_unlock(&LOCK_active_mi); + if(do_lock) + { + mysql_mutex_unlock(&LOCK_active_mi); + } + DBUG_EXECUTE_IF("after_show_slave_status", + { + const char act[]= + "now " + "signal signal.after_show_slave_status"; + DBUG_ASSERT(opt_debug_sync_timeout > 0); + DBUG_ASSERT(!debug_sync_set_action(current_thd, + STRING_WITH_LEN(act))); + };); break; } case SQLCOM_SHOW_MASTER_STAT: --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1293,6 +1293,7 @@ %token STARTS_SYM %token START_SYM /* SQL-2003-R */ %token STATUS_SYM +%token NOLOCK_SYM /* SHOW SLAVE STATUS NOLOCK */ %token STDDEV_SAMP_SYM /* SQL-2003-N */ %token STD_SYM %token STOP_SYM @@ -11111,6 +11112,11 @@ { Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; } + /* SHOW SLAVE STATUS NOLOCK */ + | SLAVE STATUS_SYM NOLOCK_SYM + { + Lex->sql_command = SQLCOM_SHOW_SLAVE_NOLOCK_STAT; //SQLCOM_SHOW_SLAVE_NOLOCK_STAT; + } | QUERY_RESPONSE_TIME_SYM wild_and_where { #ifdef HAVE_RESPONSE_TIME_DISTRIBUTION --- /dev/null +++ b/mysql-test/t/percona_show_slave_status_nolock.test @@ -0,0 +1,90 @@ +--source include/master-slave.inc +--source include/have_debug_sync.inc +--source include/have_binlog_format_statement.inc + +call mtr.add_suppression("Slave SQL: Request to stop slave SQL Thread received while applying a group that has non-transactional changes"); + +--let $rpl_connection_name=slave_lock +--let $rpl_server_number=2 +--source include/rpl_connect.inc + +--let $rpl_connection_name=slave_nolock +--let $rpl_server_number=2 +--source include/rpl_connect.inc + +--let $show_statement= SHOW PROCESSLIST +--let $field= Info + +connection master; +--echo [master] +--disable_warnings +DROP TABLE IF EXISTS t; +--enable_warnings +CREATE TABLE t(id INT); +sync_slave_with_master; + +connection slave; +--echo [slave] +SET DEBUG_SYNC='RESET'; +SET GLOBAL DEBUG="+d,after_mysql_insert,after_show_slave_status"; + +connection master; +--echo [master] +INSERT INTO t VALUES(0); + +connection slave; +--echo [slave] +--let $condition= 'INSERT INTO t VALUES(0)' +--source include/wait_show_condition.inc + +--echo check 'SHOW SLAVE STATUS' and 'SHOW SLAVE STATUS NOLOCK' - both should work fine +--source include/percona_show_slave_status_nolock.inc + +connection master; +--echo [master] +INSERT INTO t VALUES(1); + +connection slave; +--echo [slave] +--let $condition= 'INSERT INTO t VALUES(1)' +--source include/wait_show_condition.inc + +--let $rpl_connection_name=slave_stop +--let $rpl_server_number=2 +--source include/rpl_connect.inc + +connection slave_stop; +--echo [slave_stop] +send STOP SLAVE; + +connection slave; +--echo [slave] +--let $condition= 'STOP SLAVE' +--source include/wait_show_condition.inc + +--echo check 'SHOW SLAVE STATUS' and 'SHOW SLAVE STATUS NOLOCK' - just NOLOCK version should works fine +--source include/percona_show_slave_status_nolock.inc + + +connection slave_stop; +--echo [slave_stop] +reap; +--source include/wait_for_slave_to_stop.inc +START SLAVE; +--source include/wait_for_slave_to_start.inc + +connection master; +--echo [master] +SET DEBUG_SYNC='RESET'; + +connection slave; +--echo [slave] +SET GLOBAL DEBUG=''; +SET DEBUG_SYNC='RESET'; + +connection master; +--echo [master] +DROP TABLE t; +sync_slave_with_master; + +--source include/rpl_end.inc --- /dev/null +++ b/mysql-test/r/percona_show_slave_status_nolock.result @@ -0,0 +1,69 @@ +include/master-slave.inc +[connection master] +call mtr.add_suppression("Slave SQL: Request to stop slave SQL Thread received while applying a group that has non-transactional changes"); +include/rpl_connect.inc [creating slave_lock] +include/rpl_connect.inc [creating slave_nolock] +[master] +DROP TABLE IF EXISTS t; +CREATE TABLE t(id INT); +[slave] +SET DEBUG_SYNC='RESET'; +SET GLOBAL DEBUG="+d,after_mysql_insert,after_show_slave_status"; +[master] +INSERT INTO t VALUES(0); +[slave] +check 'SHOW SLAVE STATUS' and 'SHOW SLAVE STATUS NOLOCK' - both should work fine + +[slave_lock] +SHOW SLAVE STATUS; +SET DEBUG_SYNC='now WAIT_FOR signal.after_show_slave_status TIMEOUT 1'; +SIGNAL after SHOW SLAVE STATUS is 'signal.after_show_slave_status' +[slave] +SET DEBUG_SYNC='now SIGNAL signal.empty'; +[slave_nolock] +SHOW SLAVE STATUS NOLOCK; +SET DEBUG_SYNC='now WAIT_FOR signal.after_show_slave_status TIMEOUT 1'; +# should be 'signal.after_show_slave_status' +SIGNAL after SHOW SLAVE STATUS NOLOCK is 'signal.after_show_slave_status' +[slave] +SET DEBUG_SYNC='now SIGNAL signal.continue'; +[slave] +SET DEBUG_SYNC='now SIGNAL signal.empty'; + +[master] +INSERT INTO t VALUES(1); +[slave] +include/rpl_connect.inc [creating slave_stop] +[slave_stop] +STOP SLAVE; +[slave] +check 'SHOW SLAVE STATUS' and 'SHOW SLAVE STATUS NOLOCK' - just NOLOCK version should works fine + +[slave_lock] +SHOW SLAVE STATUS; +SET DEBUG_SYNC='now WAIT_FOR signal.after_show_slave_status TIMEOUT 1'; +SIGNAL after SHOW SLAVE STATUS is 'signal.empty' +[slave] +SET DEBUG_SYNC='now SIGNAL signal.empty'; +[slave_nolock] +SHOW SLAVE STATUS NOLOCK; +SET DEBUG_SYNC='now WAIT_FOR signal.after_show_slave_status TIMEOUT 1'; +# should be 'signal.after_show_slave_status' +SIGNAL after SHOW SLAVE STATUS NOLOCK is 'signal.after_show_slave_status' +[slave] +SET DEBUG_SYNC='now SIGNAL signal.continue'; +[slave] +SET DEBUG_SYNC='now SIGNAL signal.empty'; + +[slave_stop] +include/wait_for_slave_to_stop.inc +START SLAVE; +include/wait_for_slave_to_start.inc +[master] +SET DEBUG_SYNC='RESET'; +[slave] +SET GLOBAL DEBUG=''; +SET DEBUG_SYNC='RESET'; +[master] +DROP TABLE t; +include/rpl_end.inc --- /dev/null +++ b/mysql-test/include/percona_show_slave_status_nolock.inc @@ -0,0 +1,56 @@ +--echo +--disable_result_log +connection slave_lock; +--echo [slave_lock] +send SHOW SLAVE STATUS; + +connection slave; +--let $condition= 'SHOW SLAVE STATUS' +--source include/wait_show_condition.inc + +--disable_warnings +SET DEBUG_SYNC='now WAIT_FOR signal.after_show_slave_status TIMEOUT 1'; +--enable_warnings + +--let current=`SELECT SUBSTR(Variable_value FROM 22) FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE Variable_name = 'DEBUG_SYNC'` +--echo SIGNAL after SHOW SLAVE STATUS is $current + +connection slave; +--echo [slave] +SET DEBUG_SYNC='now SIGNAL signal.empty'; + +connection slave_nolock; +--echo [slave_nolock] +send SHOW SLAVE STATUS NOLOCK; + +connection slave; +--let $condition= 'SHOW SLAVE STATUS NOLOCK' +--source include/wait_show_condition.inc + +--disable_warnings +SET DEBUG_SYNC='now WAIT_FOR signal.after_show_slave_status TIMEOUT 1'; +--enable_warnings + +--echo # should be 'signal.after_show_slave_status' +--let current=`SELECT SUBSTR(Variable_value FROM 22) FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE Variable_name = 'DEBUG_SYNC'` +--echo SIGNAL after SHOW SLAVE STATUS NOLOCK is $current + +connection slave; +--echo [slave] +SET DEBUG_SYNC='now SIGNAL signal.continue'; + +connection slave_lock; +--disable_result_log +reap; +--enable_result_log + +connection slave_nolock; +--disable_result_log +reap; +--enable_result_log + +connection slave; +--echo [slave] +SET DEBUG_SYNC='now SIGNAL signal.empty'; +--enable_result_log +--echo --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1816,6 +1816,7 @@ if (mi->host[0]) { + bool do_lock=SQLCOM_SHOW_SLAVE_NOLOCK_STAT != thd->lex->sql_command; DBUG_PRINT("info",("host is set: '%s'", mi->host)); String *packet= &thd->packet; protocol->prepare_for_resend(); @@ -1824,9 +1825,15 @@ slave_running can be accessed without run_lock but not other non-volotile members like mi->io_thd, which is guarded by the mutex. */ - mysql_mutex_lock(&mi->run_lock); + if (do_lock) + { + mysql_mutex_lock(&mi->run_lock); + } protocol->store(mi->io_thd ? mi->io_thd->proc_info : "", &my_charset_bin); - mysql_mutex_unlock(&mi->run_lock); + if (do_lock) + { + mysql_mutex_unlock(&mi->run_lock); + } mysql_mutex_lock(&mi->data_lock); mysql_mutex_lock(&mi->rli.data_lock);