mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
Binlog-in-engine: mariabackup and concurrent FLUSH, PRUGE, or RESET MASTER
The mariabackup copies innodb-implemented binlog files at the end of the backup. This does not need to block any writes to the binlogs, as the --prepare will use the InnoDB redo to recover them into a consistent state. However, the copy might complain about a file missing if PURGE BINARY LOGS or RESET MASTER is run in parallel; or about a file changing size if FLUSH BINARY LOGS is run in parallel. So prevent that by taking a low-impact BACKUP STAGE START lock during the binlog copy, and blocking on that in PURGE, RESET MASTER and FLUSH BINARY LOGS. Signed-off-by: Kristian Nielsen <knielsen@knielsen-hq.org>
This commit is contained in:
@@ -357,6 +357,12 @@ void BackupImpl::process_binlog_job(std::string src, std::string dst,
|
||||
|
||||
binlog_found= get_binlog_header(c_src, m_page_buf.get(), start_lsn, is_empty);
|
||||
if (binlog_found > 0 && !is_empty && start_lsn <= backup_lsn) {
|
||||
// Test binlog_in_engine.mariabackup_binlogs will try to inject
|
||||
// RESET MASTER and PURGE BINARY LOGS here.
|
||||
DBUG_EXECUTE_IF("binlog_copy_sleep_2",
|
||||
if (src.find("binlog-000002.ibb") !=
|
||||
std::string::npos)
|
||||
my_sleep(2000000););
|
||||
if (!m_ds->copy_file(c_src, dst.c_str(), thread_num))
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@@ -5430,6 +5430,12 @@ class BackupStages {
|
||||
);
|
||||
|
||||
// Copy InnoDB binlog files.
|
||||
// Going to BACKUP STAGE START protects against RESET
|
||||
// MASTER deleting files during the copy, or FLUSH
|
||||
// BINARY LOGS truncating them.
|
||||
if (!opt_no_lock)
|
||||
xb_mysql_query(mysql_connection, "BACKUP STAGE START",
|
||||
false, false);
|
||||
if (!m_common_backup.copy_engine_binlogs(opt_binlog_directory,
|
||||
recv_sys.lsn)) {
|
||||
msg("Error on copy InnoDB binlog files");
|
||||
@@ -5439,6 +5445,9 @@ class BackupStages {
|
||||
msg("InnoDB binlog file backup process is finished with error");
|
||||
return false;
|
||||
}
|
||||
if (!opt_no_lock)
|
||||
xb_mysql_query(mysql_connection, "BACKUP STAGE END",
|
||||
false, false);
|
||||
|
||||
backup_finish(backup_datasinks.m_data);
|
||||
return true;
|
||||
|
||||
@@ -134,14 +134,15 @@ SET GLOBAL innodb_flush_log_at_trx_commit= @old_innodb_flush_log_at_trx_commit;
|
||||
# restart
|
||||
*** MDEV-7402: 'reset master' hangs, waits for signalled COND_xid_list ***
|
||||
connect con3,localhost,root,,;
|
||||
SET debug_sync="reset_logs_after_set_reset_master_pending SIGNAL reset_master_ready WAIT_FOR reset_master_cont";
|
||||
SET debug_sync="reset_logs_after_set_reset_master_pending SIGNAL reset_master_ready WAIT_FOR reset_master_cont TIMEOUT 1";
|
||||
RESET MASTER;
|
||||
connection default;
|
||||
SET debug_sync="now WAIT_FOR reset_master_ready";
|
||||
RESET MASTER;
|
||||
ERROR HY000: Cannot execute RESET MASTER as the binlog is in use by a connected slave or other RESET MASTER or binlog reader. Check SHOW PROCESSLIST for "Binlog Dump" commands and use KILL to stop such readers
|
||||
SET debug_sync="now SIGNAL reset_master_cont";
|
||||
connection con3;
|
||||
Warnings:
|
||||
Warning 1639 debug sync point wait timed out
|
||||
connection default;
|
||||
SET debug_sync = 'reset';
|
||||
*** MDEV-24660: MYSQL_BIN_LOG::cleanup(): Assertion `b->xid_count == 0' failed in MYSQL_BIN_LOG::cleanup
|
||||
|
||||
@@ -156,12 +156,14 @@ connect(con3,localhost,root,,);
|
||||
# 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";
|
||||
# After MDEV-34705, two RESET MASTERs are protected by MDL_BACKUP_START from
|
||||
# running concurrently; we test this by having a debug_sync point time
|
||||
# out while a second RESET MASTER is waiting for the first one.
|
||||
SET debug_sync="reset_logs_after_set_reset_master_pending SIGNAL reset_master_ready WAIT_FOR reset_master_cont TIMEOUT 1";
|
||||
send RESET MASTER;
|
||||
|
||||
--connection default
|
||||
SET debug_sync="now WAIT_FOR reset_master_ready";
|
||||
--error ER_BINLOG_IN_USE
|
||||
RESET MASTER;
|
||||
SET debug_sync="now SIGNAL reset_master_cont";
|
||||
|
||||
|
||||
73
mysql-test/suite/binlog_in_engine/mariabackup_binlogs.result
Normal file
73
mysql-test/suite/binlog_in_engine/mariabackup_binlogs.result
Normal file
@@ -0,0 +1,73 @@
|
||||
include/reset_master.inc
|
||||
*** Test mariabackup concurrent with RESET MASTER and FLUSH BINARY LOGS.
|
||||
CREATE TABLE t1(a INT PRIMARY KEY) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES (1);
|
||||
INSERT INTO t1 SELECT a+1 FROM t1;
|
||||
INSERT INTO t1 SELECT a+2 FROM t1;
|
||||
INSERT INTO t1 SELECT a+4 FROM t1;
|
||||
INSERT INTO t1 SELECT a+8 FROM t1;
|
||||
FLUSH BINARY LOGS;
|
||||
INSERT INTO t1 SELECT a+16 FROM t1;
|
||||
INSERT INTO t1 SELECT a+32 FROM t1;
|
||||
FLUSH BINARY LOGS;
|
||||
INSERT INTO t1 SELECT a+64 FROM t1;
|
||||
INSERT INTO t1 SELECT a+128 FROM t1;
|
||||
SET @old_needed= @@GLOBAL.slave_connections_needed_for_purge;
|
||||
SET GLOBAL slave_connections_needed_for_purge=0;
|
||||
connect con$i,localhost,root,,;
|
||||
BEGIN NOT ATOMIC
|
||||
SELECT SLEEP(0.9);
|
||||
FLUSH BINARY LOGS;
|
||||
END //
|
||||
connection default;
|
||||
connection con1;
|
||||
SLEEP(0.9)
|
||||
0
|
||||
disconnect con1;
|
||||
connection default;
|
||||
FLUSH BINARY LOGS;
|
||||
INSERT INTO t1 SELECT a+256 FROM t1;
|
||||
connect con$i,localhost,root,,;
|
||||
BEGIN NOT ATOMIC
|
||||
SELECT SLEEP(1.1);
|
||||
PURGE BINARY LOGS TO 'binlog-000003.ibb';
|
||||
END //
|
||||
connection default;
|
||||
connection con2;
|
||||
SLEEP(1.1)
|
||||
0
|
||||
disconnect con2;
|
||||
connection default;
|
||||
RESET MASTER;
|
||||
INSERT INTO t1 SELECT a+512 FROM t1 WHERE a <= 256;
|
||||
FLUSH BINARY LOGS;
|
||||
INSERT INTO t1 SELECT a+768 FROM t1 WHERE a <= 256;
|
||||
FLUSH BINARY LOGS;
|
||||
connect con$i,localhost,root,,;
|
||||
BEGIN NOT ATOMIC
|
||||
SELECT SLEEP(0.1);
|
||||
RESET MASTER;
|
||||
END //
|
||||
connection default;
|
||||
connection con3;
|
||||
SLEEP(0.1)
|
||||
0
|
||||
disconnect con3;
|
||||
connection default;
|
||||
INSERT INTO t1 SELECT a+1024 FROM t1 WHERE a <= 256;
|
||||
FLUSH BINARY LOGS;
|
||||
INSERT INTO t1 SELECT a+1280 FROM t1 WHERE a <= 256;
|
||||
FLUSH BINARY LOGS;
|
||||
connect con$i,localhost,root,,;
|
||||
BEGIN NOT ATOMIC
|
||||
SELECT SLEEP(1.1);
|
||||
RESET MASTER;
|
||||
END //
|
||||
connection default;
|
||||
connection con4;
|
||||
SLEEP(1.1)
|
||||
0
|
||||
disconnect con4;
|
||||
connection default;
|
||||
DROP TABLE t1;
|
||||
SET GLOBAL slave_connections_needed_for_purge= @old_needed;
|
||||
118
mysql-test/suite/binlog_in_engine/mariabackup_binlogs.test
Normal file
118
mysql-test/suite/binlog_in_engine/mariabackup_binlogs.test
Normal file
@@ -0,0 +1,118 @@
|
||||
--source include/not_embedded.inc
|
||||
--source include/have_debug.inc
|
||||
--source include/have_innodb_binlog.inc
|
||||
|
||||
--source include/reset_master.inc
|
||||
|
||||
let $basedir= $MYSQLTEST_VARDIR/tmp/backup;
|
||||
let $datadir_2= $MYSQLTEST_VARDIR/tmp/restore;
|
||||
|
||||
--echo *** Test mariabackup concurrent with RESET MASTER and FLUSH BINARY LOGS.
|
||||
CREATE TABLE t1(a INT PRIMARY KEY) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES (1);
|
||||
INSERT INTO t1 SELECT a+1 FROM t1;
|
||||
INSERT INTO t1 SELECT a+2 FROM t1;
|
||||
INSERT INTO t1 SELECT a+4 FROM t1;
|
||||
INSERT INTO t1 SELECT a+8 FROM t1;
|
||||
FLUSH BINARY LOGS;
|
||||
INSERT INTO t1 SELECT a+16 FROM t1;
|
||||
INSERT INTO t1 SELECT a+32 FROM t1;
|
||||
FLUSH BINARY LOGS;
|
||||
INSERT INTO t1 SELECT a+64 FROM t1;
|
||||
INSERT INTO t1 SELECT a+128 FROM t1;
|
||||
|
||||
# Attempt to run a RESET MASTER, a FLUSH BINARY LOGS, and a PURGE concurrent
|
||||
# with the copy of the binlog files, to test that backup locks will prevent
|
||||
# the binlog files from changing during the copy.
|
||||
# We test this by putting a sleep in mariabackup after getting the list
|
||||
# of binlog files and before copying them. And then sending paralle
|
||||
# RESET/FLUSH/PURGE with a delay that is shorter than the sleep but longer
|
||||
# than the typical time to run the backup.
|
||||
# This gives a good chance to hit the potential race, and missing it is
|
||||
# not critical, it will at most cause a false negative, but never false
|
||||
# positive.
|
||||
SET @old_needed= @@GLOBAL.slave_connections_needed_for_purge;
|
||||
SET GLOBAL slave_connections_needed_for_purge=0;
|
||||
|
||||
--let $i= 1
|
||||
while ($i <= 4) {
|
||||
--connect con$i,localhost,root,,
|
||||
--delimiter //
|
||||
|
||||
if ($i == 1) {
|
||||
# A FLUSH BINARY LOGS that truncates binlog-000002.ibb at around the same
|
||||
# time that mariabackup tries to copy it.
|
||||
send BEGIN NOT ATOMIC
|
||||
SELECT SLEEP(0.9);
|
||||
FLUSH BINARY LOGS;
|
||||
END //
|
||||
}
|
||||
|
||||
if ($i == 2) {
|
||||
# A PURGE BINARY LOGS that removes binlog-000002.ibb at around the same
|
||||
# time that mariabackup tries to copy it.
|
||||
send BEGIN NOT ATOMIC
|
||||
SELECT SLEEP(1.1);
|
||||
PURGE BINARY LOGS TO 'binlog-000003.ibb';
|
||||
END //
|
||||
}
|
||||
|
||||
if ($i == 3) {
|
||||
# A RESET MASTER that removes binlog-000002.ibb early during the first
|
||||
# stage of backup.
|
||||
send BEGIN NOT ATOMIC
|
||||
SELECT SLEEP(0.1);
|
||||
RESET MASTER;
|
||||
END //
|
||||
}
|
||||
|
||||
if ($i == 4) {
|
||||
# A RESET MASTER that removes binlog-000002.ibb at around the same
|
||||
# time that mariabackup tries to copy it.
|
||||
send BEGIN NOT ATOMIC
|
||||
SELECT SLEEP(1.1);
|
||||
RESET MASTER;
|
||||
END //
|
||||
}
|
||||
|
||||
--delimiter ;
|
||||
|
||||
--connection default
|
||||
--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$basedir --dbug=+d,binlog_copy_sleep_2
|
||||
--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --prepare --target-dir=$basedir
|
||||
--exec $XTRABACKUP --copy-back --datadir=$datadir_2 --target-dir=$basedir
|
||||
--rmdir $basedir
|
||||
--rmdir $datadir_2
|
||||
|
||||
--connection con$i
|
||||
REAP;
|
||||
--disconnect con$i
|
||||
--connection default
|
||||
|
||||
if ($i == 1) {
|
||||
# Another FLUSH, moving the active binlog file to binlog-000004.ibb, so
|
||||
# that next round can PURGE to remove binlog-000002.ibb.
|
||||
FLUSH BINARY LOGS;
|
||||
INSERT INTO t1 SELECT a+256 FROM t1;
|
||||
}
|
||||
|
||||
if ($i == 2) {
|
||||
# Re-create the binlog-000002.ibb for the following RESET MASTER test.
|
||||
RESET MASTER;
|
||||
INSERT INTO t1 SELECT a+512 FROM t1 WHERE a <= 256;
|
||||
FLUSH BINARY LOGS;
|
||||
INSERT INTO t1 SELECT a+768 FROM t1 WHERE a <= 256;
|
||||
FLUSH BINARY LOGS;
|
||||
}
|
||||
if ($i == 3) {
|
||||
INSERT INTO t1 SELECT a+1024 FROM t1 WHERE a <= 256;
|
||||
FLUSH BINARY LOGS;
|
||||
INSERT INTO t1 SELECT a+1280 FROM t1 WHERE a <= 256;
|
||||
FLUSH BINARY LOGS;
|
||||
}
|
||||
|
||||
inc $i;
|
||||
}
|
||||
|
||||
DROP TABLE t1;
|
||||
SET GLOBAL slave_connections_needed_for_purge= @old_needed;
|
||||
@@ -174,18 +174,37 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
|
||||
tmp_write_to_binlog= 0;
|
||||
if (mysql_bin_log.is_open())
|
||||
{
|
||||
DYNAMIC_ARRAY *drop_gtid_domain=
|
||||
(thd && (thd->lex->delete_gtid_domain.elements > 0)) ?
|
||||
&thd->lex->delete_gtid_domain : NULL;
|
||||
if (mysql_bin_log.flush_binlog(drop_gtid_domain))
|
||||
*write_to_binlog= -1;
|
||||
|
||||
/* Note that WSREP(thd) might not be true here e.g. during
|
||||
SST. */
|
||||
if (WSREP_ON)
|
||||
MDL_request mdl_request;
|
||||
MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_START,
|
||||
MDL_EXPLICIT);
|
||||
if (thd &&
|
||||
thd->mdl_context.acquire_lock(&mdl_request,
|
||||
thd->variables.lock_wait_timeout))
|
||||
result= 1;
|
||||
else
|
||||
{
|
||||
/* Wait for last binlog checkpoint event to be logged. */
|
||||
mysql_bin_log.wait_for_last_checkpoint_event();
|
||||
if (thd)
|
||||
thd->backup_commit_lock= &mdl_request;
|
||||
|
||||
DYNAMIC_ARRAY *drop_gtid_domain=
|
||||
(thd && (thd->lex->delete_gtid_domain.elements > 0)) ?
|
||||
&thd->lex->delete_gtid_domain : NULL;
|
||||
if (mysql_bin_log.flush_binlog(drop_gtid_domain))
|
||||
*write_to_binlog= -1;
|
||||
|
||||
/* Note that WSREP(thd) might not be true here e.g. during
|
||||
SST. */
|
||||
if (WSREP_ON)
|
||||
{
|
||||
/* Wait for last binlog checkpoint event to be logged. */
|
||||
mysql_bin_log.wait_for_last_checkpoint_event();
|
||||
}
|
||||
if (thd)
|
||||
{
|
||||
if (mdl_request.ticket)
|
||||
thd->mdl_context.release_lock(mdl_request.ticket);
|
||||
thd->backup_commit_lock= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -800,6 +800,14 @@ bool purge_master_logs(THD* thd, const char* to_log)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
MDL_request mdl_request;
|
||||
MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_START,
|
||||
MDL_EXPLICIT);
|
||||
if (thd->mdl_context.acquire_lock(&mdl_request,
|
||||
thd->variables.lock_wait_timeout))
|
||||
return TRUE;
|
||||
thd->backup_commit_lock= &mdl_request;
|
||||
|
||||
int res;
|
||||
if (!opt_binlog_engine_hton)
|
||||
{
|
||||
@@ -840,6 +848,11 @@ bool purge_master_logs(THD* thd, const char* to_log)
|
||||
give_purge_note(purge_info.nonpurge_reason,
|
||||
purge_info.nonpurge_filename, true);
|
||||
}
|
||||
|
||||
if (mdl_request.ticket)
|
||||
thd->mdl_context.release_lock(mdl_request.ticket);
|
||||
thd->backup_commit_lock= 0;
|
||||
|
||||
return purge_error_message(thd, res);
|
||||
}
|
||||
|
||||
@@ -4870,11 +4883,25 @@ int reset_master(THD* thd, rpl_gtid *init_state, uint32 init_state_len,
|
||||
}
|
||||
#endif /* WITH_WSREP */
|
||||
bool ret= 0;
|
||||
|
||||
MDL_request mdl_request;
|
||||
MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_START,
|
||||
MDL_EXPLICIT);
|
||||
if (thd->mdl_context.acquire_lock(&mdl_request,
|
||||
thd->variables.lock_wait_timeout))
|
||||
return 1;
|
||||
thd->backup_commit_lock= &mdl_request;
|
||||
|
||||
/* Temporarily disable master semisync before resetting master. */
|
||||
repl_semisync_master.before_reset_master();
|
||||
ret= mysql_bin_log.reset_logs(thd, 1, init_state, init_state_len,
|
||||
next_log_number);
|
||||
repl_semisync_master.after_reset_master();
|
||||
|
||||
if (mdl_request.ticket)
|
||||
thd->mdl_context.release_lock(mdl_request.ticket);
|
||||
thd->backup_commit_lock= 0;
|
||||
|
||||
DBUG_EXECUTE_IF("crash_after_reset_master", DBUG_SUICIDE(););
|
||||
|
||||
return ret;
|
||||
|
||||
Reference in New Issue
Block a user