From 510ca9b6973eacf879b94baeb8bbdd84d224c8d6 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 19 Jan 2015 14:32:28 +0100 Subject: [PATCH] MDEV-7402 'reset master' hangs, waits for signalled COND_xid_list Using a boolean flag for 'there is a RESET MASTER in progress' doesn't work very well for multiple concurrent RESET MASTER statements. Changed to a counter. --- .../suite/binlog/r/binlog_checkpoint.result | 12 +++++++ .../suite/binlog/t/binlog_checkpoint.test | 31 +++++++++++++++++++ sql/log.cc | 7 +++-- sql/log.h | 2 +- 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/mysql-test/suite/binlog/r/binlog_checkpoint.result b/mysql-test/suite/binlog/r/binlog_checkpoint.result index 2ce9ed760f5..f76fc6da189 100644 --- a/mysql-test/suite/binlog/r/binlog_checkpoint.result +++ b/mysql-test/suite/binlog/r/binlog_checkpoint.result @@ -112,6 +112,18 @@ master-bin.000003 # master-bin.000004 # master-bin.000005 # master-bin.000006 # +SET debug_sync = 'reset'; +*** MDEV-7402: 'reset master' hangs, waits for signalled COND_xid_list *** +SET debug_sync="reset_logs_after_set_reset_master_pending SIGNAL reset_master_ready WAIT_FOR reset_master_cont"; +RESET MASTER; +SET @old_dbug= @@global.DEBUG_DBUG; +SET GLOBAL debug_dbug="+d,inject_binlog_background_thread_before_mark_xid_done"; +SET debug_sync="now WAIT_FOR reset_master_ready"; +RESET MASTER; +SET debug_sync="now WAIT_FOR injected_binlog_background_thread"; +SET GLOBAL debug_dbug=@old_dbug; +SET debug_sync="now SIGNAL reset_master_cont"; +SET debug_sync = 'reset'; DROP TABLE t1, t2; SET GLOBAL max_binlog_size= @old_max_binlog_size; SET GLOBAL innodb_flush_log_at_trx_commit= @old_innodb_flush_log_at_trx_commit; diff --git a/mysql-test/suite/binlog/t/binlog_checkpoint.test b/mysql-test/suite/binlog/t/binlog_checkpoint.test index 356f860af32..cdb71887ad6 100644 --- a/mysql-test/suite/binlog/t/binlog_checkpoint.test +++ b/mysql-test/suite/binlog/t/binlog_checkpoint.test @@ -138,8 +138,39 @@ SET DEBUG_SYNC= "now WAIT_FOR injected_binlog_background_thread"; SET GLOBAL debug_dbug= @old_dbug; INSERT INTO t1 VALUES (31, REPEAT("x", 4100)); --source include/show_binary_logs.inc +SET debug_sync = 'reset'; +--echo *** MDEV-7402: 'reset master' hangs, waits for signalled COND_xid_list *** + +--source include/wait_for_binlog_checkpoint.inc + +connect(con3,localhost,root,,); +# Make the binlog background thread wait before clearing the pending checkpoint. +# The bug was that one RESET MASTER would clear the reset_master_pending +# flag set by another RESET MASTER; this could cause the wakeup from the +# binlog background thread not to be sent, and thus the second RESET MASTER +# to wait infinitely. +SET debug_sync="reset_logs_after_set_reset_master_pending SIGNAL reset_master_ready WAIT_FOR reset_master_cont"; +send RESET MASTER; + +--connection default +SET @old_dbug= @@global.DEBUG_DBUG; +SET GLOBAL debug_dbug="+d,inject_binlog_background_thread_before_mark_xid_done"; +SET debug_sync="now WAIT_FOR reset_master_ready"; +RESET MASTER; +SET debug_sync="now WAIT_FOR injected_binlog_background_thread"; +SET GLOBAL debug_dbug=@old_dbug; +SET debug_sync="now SIGNAL reset_master_cont"; + +--connection con3 +REAP; + +--connection default +SET debug_sync = 'reset'; + + +# Clean up. DROP TABLE t1, t2; SET GLOBAL max_binlog_size= @old_max_binlog_size; SET GLOBAL innodb_flush_log_at_trx_commit= @old_innodb_flush_log_at_trx_commit; diff --git a/sql/log.cc b/sql/log.cc index 71b1b8728af..38fe1066896 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -3031,7 +3031,7 @@ const char *MYSQL_LOG::generate_name(const char *log_name, MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period) - :reset_master_pending(false), mark_xid_done_waiting(0), + :reset_master_pending(0), mark_xid_done_waiting(0), bytes_written(0), file_id(1), open_count(1), group_commit_queue(0), group_commit_queue_busy(FALSE), num_commits(0), num_group_commits(0), @@ -3867,12 +3867,13 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log, do this before we take the LOCK_log to not deadlock. */ mysql_mutex_lock(&LOCK_xid_list); - reset_master_pending= true; + reset_master_pending++; while (mark_xid_done_waiting > 0) mysql_cond_wait(&COND_xid_list, &LOCK_xid_list); mysql_mutex_unlock(&LOCK_xid_list); } + DEBUG_SYNC(thd, "reset_logs_after_set_reset_master_pending"); if (thd) ha_reset_logs(thd); /* @@ -4054,7 +4055,7 @@ err: DBUG_ASSERT(b->xid_count == 0); my_free(binlog_xid_count_list.get()); } - reset_master_pending= false; + reset_master_pending--; mysql_mutex_unlock(&LOCK_xid_list); } diff --git a/sql/log.h b/sql/log.h index 1bf87733ce2..a93e2e1c0b9 100644 --- a/sql/log.h +++ b/sql/log.h @@ -470,7 +470,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG anyway). Instead we should signal COND_xid_list whenever a new binlog checkpoint arrives - when all have arrived, RESET MASTER will complete. */ - bool reset_master_pending; + uint reset_master_pending; ulong mark_xid_done_waiting; /* LOCK_log and LOCK_index are inited by init_pthread_objects() */