mirror of
https://github.com/MariaDB/server.git
synced 2025-01-30 10:31:54 +01:00
MDEV-5636: Deadlock in RESET MASTER
The problem is a deadlock between MYSQL_BIN_LOG::reset_logs() and MYSQL_BIN_LOG::mark_xid_done(). The former takes LOCK_log and waits for the latter to complete. But the latter also tries to take LOCK_log; this can lead to a deadlock. There was already code that tries to deal with this, with the flag reset_master_pending. However, there was still a small opportunity for deadlock, when an previous mark_xid_done() is still running when reset_logs() is called and is at the precise point where it first releases LOCK_xid_list and then re-aquires both LOCK_log and LOCK_xid_list. Solve by setting reset_master_pending in reset_logs() before taking LOCK_log. And also count how many invocations of LOCK_xid_list are in the progress of releasing and re-aquiring locks, and in reset_logs() wait for that number to drop to zero after setting reset_master_pending and before taking LOCK_log.
This commit is contained in:
parent
76e929a92e
commit
07eaf6ea76
2 changed files with 31 additions and 19 deletions
49
sql/log.cc
49
sql/log.cc
|
@ -2933,7 +2933,7 @@ const char *MYSQL_LOG::generate_name(const char *log_name,
|
||||||
|
|
||||||
|
|
||||||
MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
|
MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
|
||||||
:reset_master_pending(false),
|
:reset_master_pending(false), mark_xid_done_waiting(0),
|
||||||
bytes_written(0), file_id(1), open_count(1),
|
bytes_written(0), file_id(1), open_count(1),
|
||||||
group_commit_queue(0), group_commit_queue_busy(FALSE),
|
group_commit_queue(0), group_commit_queue_busy(FALSE),
|
||||||
num_commits(0), num_group_commits(0),
|
num_commits(0), num_group_commits(0),
|
||||||
|
@ -3749,6 +3749,31 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log,
|
||||||
const char* save_name;
|
const char* save_name;
|
||||||
DBUG_ENTER("reset_logs");
|
DBUG_ENTER("reset_logs");
|
||||||
|
|
||||||
|
if (!is_relay_log)
|
||||||
|
{
|
||||||
|
if (init_state && !is_empty_state())
|
||||||
|
{
|
||||||
|
my_error(ER_BINLOG_MUST_BE_EMPTY, MYF(0));
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Mark that a RESET MASTER is in progress.
|
||||||
|
This ensures that a binlog checkpoint will not try to write binlog
|
||||||
|
checkpoint events, which would be useless (as we are deleting the binlog
|
||||||
|
anyway) and could deadlock, as we are holding LOCK_log.
|
||||||
|
|
||||||
|
Wait for any mark_xid_done() calls that might be already running to
|
||||||
|
complete (mark_xid_done_waiting counter to drop to zero); we need to
|
||||||
|
do this before we take the LOCK_log to not deadlock.
|
||||||
|
*/
|
||||||
|
mysql_mutex_lock(&LOCK_xid_list);
|
||||||
|
reset_master_pending= true;
|
||||||
|
while (mark_xid_done_waiting > 0)
|
||||||
|
mysql_cond_wait(&COND_xid_list, &LOCK_xid_list);
|
||||||
|
mysql_mutex_unlock(&LOCK_xid_list);
|
||||||
|
}
|
||||||
|
|
||||||
if (thd)
|
if (thd)
|
||||||
ha_reset_logs(thd);
|
ha_reset_logs(thd);
|
||||||
/*
|
/*
|
||||||
|
@ -3760,24 +3785,6 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log,
|
||||||
|
|
||||||
if (!is_relay_log)
|
if (!is_relay_log)
|
||||||
{
|
{
|
||||||
if (init_state && !is_empty_state())
|
|
||||||
{
|
|
||||||
my_error(ER_BINLOG_MUST_BE_EMPTY, MYF(0));
|
|
||||||
mysql_mutex_unlock(&LOCK_index);
|
|
||||||
mysql_mutex_unlock(&LOCK_log);
|
|
||||||
DBUG_RETURN(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Mark that a RESET MASTER is in progress.
|
|
||||||
This ensures that a binlog checkpoint will not try to write binlog
|
|
||||||
checkpoint events, which would be useless (as we are deleting the binlog
|
|
||||||
anyway) and could deadlock, as we are holding LOCK_log.
|
|
||||||
*/
|
|
||||||
mysql_mutex_lock(&LOCK_xid_list);
|
|
||||||
reset_master_pending= true;
|
|
||||||
mysql_mutex_unlock(&LOCK_xid_list);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We are going to nuke all binary log files.
|
We are going to nuke all binary log files.
|
||||||
Without binlog, we cannot XA recover prepared-but-not-committed
|
Without binlog, we cannot XA recover prepared-but-not-committed
|
||||||
|
@ -8834,9 +8841,13 @@ TC_LOG_BINLOG::mark_xid_done(ulong binlog_id, bool write_checkpoint)
|
||||||
locks in the opposite order.
|
locks in the opposite order.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
++mark_xid_done_waiting;
|
||||||
mysql_mutex_unlock(&LOCK_xid_list);
|
mysql_mutex_unlock(&LOCK_xid_list);
|
||||||
mysql_mutex_lock(&LOCK_log);
|
mysql_mutex_lock(&LOCK_log);
|
||||||
mysql_mutex_lock(&LOCK_xid_list);
|
mysql_mutex_lock(&LOCK_xid_list);
|
||||||
|
--mark_xid_done_waiting;
|
||||||
|
if (unlikely(reset_master_pending))
|
||||||
|
mysql_cond_signal(&COND_xid_list);
|
||||||
/* We need to reload current_binlog_id due to release/re-take of lock. */
|
/* We need to reload current_binlog_id due to release/re-take of lock. */
|
||||||
current= current_binlog_id;
|
current= current_binlog_id;
|
||||||
|
|
||||||
|
|
|
@ -471,6 +471,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
|
||||||
checkpoint arrives - when all have arrived, RESET MASTER will complete.
|
checkpoint arrives - when all have arrived, RESET MASTER will complete.
|
||||||
*/
|
*/
|
||||||
bool reset_master_pending;
|
bool reset_master_pending;
|
||||||
|
ulong mark_xid_done_waiting;
|
||||||
|
|
||||||
/* LOCK_log and LOCK_index are inited by init_pthread_objects() */
|
/* LOCK_log and LOCK_index are inited by init_pthread_objects() */
|
||||||
mysql_mutex_t LOCK_index;
|
mysql_mutex_t LOCK_index;
|
||||||
|
|
Loading…
Add table
Reference in a new issue