← Back to team overview

maria-developers team mailing list archive

Deadlock with STOP SLAVE and LOCK_active_mi

 

So while looking at MDEV-9573, I found this code in mysql_execute_command()
for STOP SLAVE:

    mysql_mutex_lock(&LOCK_active_mi);
    if ((mi= (master_info_index->
              get_master_info(&lex_mi->connection_name,
                              Sql_condition::WARN_LEVEL_ERROR))))
      if (!stop_slave(thd, mi, 1/* net report*/))
        my_ok(thd);
    mysql_mutex_unlock(&LOCK_active_mi);

So basically, this code is holding LOCK_active_mi for the entire duration of
the STOP SLAVE operation.

This seems completely broken. Anything in a slave thread that tries to take
LOCK_active_mi will then deadlock with the STOP SLAVE operation. It is
simple enough to trigger, testcase (with sleep-injecting patch) at the end
of the email. This uses INFORMATION_SCHEMA.SESSSION_STATUS; I could imagine
accessing a number of system variables could trigger it as well.

>From a quick check, it looks like this has been like this forever, eg. 5.1
has similar code.

Any suggestions for how this is supposed to work? Or is it just broken by
design, but saved because normally slave threads do not need to access SHOW
STATUS or system variables?

 - Kristian.

-----------------------------------------------------------------------
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index ced9225..2358d6b 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -7765,6 +7765,7 @@ static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff,
 
   var->type= SHOW_CHAR;
   var->value= buff;
+if (thd->slave_thread) my_sleep(5000000);
   mysql_mutex_lock(&LOCK_active_mi);
   if (master_info_index) 
   {
-----------------------------------------------------------------------
--source include/have_innodb.inc
--source include/master-slave.inc

--connection master
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
--save_master_pos

--connection slave
--sync_with_master
--source include/stop_slave.inc

--connection master
INSERT INTO t1
SELECT VARIABLE_VALUE
  FROM INFORMATION_SCHEMA.SESSION_STATUS
 WHERE VARIABLE_NAME='COM_COMMIT';
--save_master_pos

--connection slave
--source include/start_slave.inc
# Wait a bit to hit a sleep() in code after taking LOCK_active_mi.
SELECT SLEEP(2);
STOP SLAVE;
SELECT * FROM t1 ORDER BY a;

START SLAVE;
--sync_with_master
SELECT * FROM t1 ORDER BY a;

# Clean up.
--connection master
DROP TABLE t1;
--source include/rpl_end.inc
-----------------------------------------------------------------------


Follow ups