mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
MDEV-34705: Binlog-in-engine: mariadb-backup integration
InnoDB binlog files are now backed up along with other InnoDB data by mariadb-backup. The files are copied after backup locks have been released. Backup files created later than the backup LSN are skipped. Then during --prepare, any data missing from the hot-copied binlog files will be restored by the binlog recovery code, and any excess data written after the backup LSN will be zeroed out. A couple test cases test taking a consistent backup of a server with active traffic during the backup, by provisioning a slave from the restored binlog position and checking that the slave can replicate from the original master and get identical data. Signed-off-by: Kristian Nielsen <knielsen@knielsen-hq.org>
This commit is contained in:
@@ -61,6 +61,7 @@ Street, Fifth Floor, Boston, MA 02110-1335 USA
|
|||||||
#include "backup_debug.h"
|
#include "backup_debug.h"
|
||||||
#include "backup_mysql.h"
|
#include "backup_mysql.h"
|
||||||
#include <btr0btr.h>
|
#include <btr0btr.h>
|
||||||
|
#include <innodb_binlog.h>
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <direct.h> /* rmdir */
|
#include <direct.h> /* rmdir */
|
||||||
#endif
|
#endif
|
||||||
@@ -1668,6 +1669,7 @@ copy_back()
|
|||||||
datadir_iter_t *it = NULL;
|
datadir_iter_t *it = NULL;
|
||||||
datadir_node_t node;
|
datadir_node_t node;
|
||||||
const char *dst_dir;
|
const char *dst_dir;
|
||||||
|
ds_ctxt *ds_binlogs = NULL;
|
||||||
|
|
||||||
memset(&node, 0, sizeof(node));
|
memset(&node, 0, sizeof(node));
|
||||||
|
|
||||||
@@ -1793,6 +1795,10 @@ copy_back()
|
|||||||
|
|
||||||
ds_destroy(ds_tmp);
|
ds_destroy(ds_tmp);
|
||||||
|
|
||||||
|
/* Prepare destination directory for any InnoDB binlog files. */
|
||||||
|
dst_dir = dst_dir_buf.make(opt_binlog_directory);
|
||||||
|
ds_binlogs = ds_create(dst_dir, DS_TYPE_LOCAL);
|
||||||
|
|
||||||
/* copy the rest of tablespaces */
|
/* copy the rest of tablespaces */
|
||||||
ds_tmp = ds_create(mysql_data_home, DS_TYPE_LOCAL);
|
ds_tmp = ds_create(mysql_data_home, DS_TYPE_LOCAL);
|
||||||
|
|
||||||
@@ -1854,6 +1860,16 @@ copy_back()
|
|||||||
|
|
||||||
filename = base_name(node.filepath);
|
filename = base_name(node.filepath);
|
||||||
|
|
||||||
|
/* Copy InnoDB binlog files into --binlog-directory. */
|
||||||
|
uint64_t file_no;
|
||||||
|
if (is_binlog_name(filename, &file_no)) {
|
||||||
|
if (!(ret = copy_or_move_file(ds_binlogs, filename, filename,
|
||||||
|
dst_dir, 1))) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* skip .qp files */
|
/* skip .qp files */
|
||||||
if (filename_matches(filename, ext_list)) {
|
if (filename_matches(filename, ext_list)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1914,6 +1930,11 @@ cleanup:
|
|||||||
|
|
||||||
ds_tmp = NULL;
|
ds_tmp = NULL;
|
||||||
|
|
||||||
|
if (ds_binlogs != NULL) {
|
||||||
|
ds_destroy(ds_binlogs);
|
||||||
|
ds_binlogs = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return(ret);
|
return(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -384,6 +384,7 @@ bool get_mysql_vars(MYSQL *connection)
|
|||||||
char *aria_log_dir_path_var= NULL;
|
char *aria_log_dir_path_var= NULL;
|
||||||
char *page_zip_level_var= NULL;
|
char *page_zip_level_var= NULL;
|
||||||
char *ignore_db_dirs= NULL;
|
char *ignore_db_dirs= NULL;
|
||||||
|
char *binlog_directory_var= NULL;
|
||||||
char *endptr;
|
char *endptr;
|
||||||
ulong server_version= mysql_get_server_version(connection);
|
ulong server_version= mysql_get_server_version(connection);
|
||||||
|
|
||||||
@@ -410,6 +411,7 @@ bool get_mysql_vars(MYSQL *connection)
|
|||||||
{"innodb_compression_level", &page_zip_level_var},
|
{"innodb_compression_level", &page_zip_level_var},
|
||||||
{"ignore_db_dirs", &ignore_db_dirs},
|
{"ignore_db_dirs", &ignore_db_dirs},
|
||||||
{"aria_log_dir_path", &aria_log_dir_path_var},
|
{"aria_log_dir_path", &aria_log_dir_path_var},
|
||||||
|
{"binlog_directory", &binlog_directory_var},
|
||||||
{NULL, NULL}};
|
{NULL, NULL}};
|
||||||
|
|
||||||
read_mysql_variables(connection, "SHOW VARIABLES", mysql_vars, true);
|
read_mysql_variables(connection, "SHOW VARIABLES", mysql_vars, true);
|
||||||
@@ -551,6 +553,10 @@ bool get_mysql_vars(MYSQL *connection)
|
|||||||
if (ignore_db_dirs)
|
if (ignore_db_dirs)
|
||||||
xb_load_list_string(ignore_db_dirs, ",", register_ignore_db_dirs_filter);
|
xb_load_list_string(ignore_db_dirs, ",", register_ignore_db_dirs_filter);
|
||||||
|
|
||||||
|
if (binlog_directory_var && *binlog_directory_var)
|
||||||
|
opt_binlog_directory= my_strdup(PSI_NOT_INSTRUMENTED, binlog_directory_var,
|
||||||
|
MYF(MY_FAE));
|
||||||
|
|
||||||
out:
|
out:
|
||||||
free_mysql_variables(mysql_vars);
|
free_mysql_variables(mysql_vars);
|
||||||
|
|
||||||
|
@@ -9,6 +9,9 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "innodb_binlog.h"
|
||||||
|
|
||||||
|
|
||||||
namespace common_engine {
|
namespace common_engine {
|
||||||
|
|
||||||
class Table {
|
class Table {
|
||||||
@@ -298,17 +301,21 @@ class BackupImpl {
|
|||||||
}
|
}
|
||||||
bool copy_log_tables(bool finalize);
|
bool copy_log_tables(bool finalize);
|
||||||
bool copy_stats_tables();
|
bool copy_stats_tables();
|
||||||
|
bool copy_engine_binlogs(const char *binlog_dir, lsn_t backup_lsn);
|
||||||
bool wait_for_finish();
|
bool wait_for_finish();
|
||||||
bool close_log_tables();
|
bool close_log_tables();
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void process_table_job(Table *table, bool no_lock, bool delete_table,
|
void process_table_job(Table *table, bool no_lock, bool delete_table,
|
||||||
bool finalize, unsigned thread_num);
|
bool finalize, unsigned thread_num);
|
||||||
|
void process_binlog_job(std::string src, std::string dst,
|
||||||
|
lsn_t backup_lsn, unsigned thread_num);
|
||||||
|
|
||||||
const char *m_datadir_path;
|
const char *m_datadir_path;
|
||||||
ds_ctxt_t *m_ds;
|
ds_ctxt_t *m_ds;
|
||||||
std::vector<MYSQL *> &m_con_pool;
|
std::vector<MYSQL *> &m_con_pool;
|
||||||
TasksGroup m_process_table_jobs;
|
TasksGroup m_process_table_jobs;
|
||||||
|
std::unique_ptr<byte []> m_page_buf;
|
||||||
|
|
||||||
post_copy_table_hook_t m_table_post_copy_hook;
|
post_copy_table_hook_t m_table_post_copy_hook;
|
||||||
std::unordered_map<table_key_t, std::unique_ptr<LogTable>> m_log_tables;
|
std::unordered_map<table_key_t, std::unique_ptr<LogTable>> m_log_tables;
|
||||||
@@ -337,6 +344,29 @@ exit:
|
|||||||
m_process_table_jobs.finish_task(result);
|
m_process_table_jobs.finish_task(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BackupImpl::process_binlog_job(std::string src, std::string dst,
|
||||||
|
lsn_t backup_lsn, unsigned thread_num) {
|
||||||
|
int result = 0;
|
||||||
|
const char *c_src= src.c_str();
|
||||||
|
bool is_empty= true;
|
||||||
|
lsn_t start_lsn;
|
||||||
|
int binlog_found;
|
||||||
|
|
||||||
|
if (!m_process_table_jobs.get_result())
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (!m_ds->copy_file(c_src, dst.c_str(), thread_num))
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
m_process_table_jobs.finish_task(result);
|
||||||
|
}
|
||||||
|
|
||||||
bool BackupImpl::scan(const std::unordered_set<table_key_t> &exclude_tables,
|
bool BackupImpl::scan(const std::unordered_set<table_key_t> &exclude_tables,
|
||||||
std::unordered_set<table_key_t> *out_processed_tables, bool no_lock,
|
std::unordered_set<table_key_t> *out_processed_tables, bool no_lock,
|
||||||
bool collect_log_and_stats) {
|
bool collect_log_and_stats) {
|
||||||
@@ -461,6 +491,26 @@ bool BackupImpl::copy_stats_tables() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BackupImpl::copy_engine_binlogs(const char *binlog_dir, lsn_t backup_lsn) {
|
||||||
|
std::vector<std::string>files;
|
||||||
|
std::string dir(binlog_dir && binlog_dir[0] ? binlog_dir : m_datadir_path);
|
||||||
|
foreach_file_in_datadir(dir.c_str(),
|
||||||
|
[&](const char *name)->bool {
|
||||||
|
uint64_t file_no;
|
||||||
|
if (is_binlog_name(name, &file_no))
|
||||||
|
files.emplace_back(name);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
m_page_buf.reset(new byte [ibb_page_size]);
|
||||||
|
for (auto &file : files) {
|
||||||
|
std::string path(dir + "/" + file);
|
||||||
|
m_process_table_jobs.push_task(
|
||||||
|
std::bind(&BackupImpl::process_binlog_job, this, path,
|
||||||
|
file, backup_lsn, std::placeholders::_1));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool BackupImpl::wait_for_finish() {
|
bool BackupImpl::wait_for_finish() {
|
||||||
/* Wait for threads to exit */
|
/* Wait for threads to exit */
|
||||||
return m_process_table_jobs.wait_for_finish();
|
return m_process_table_jobs.wait_for_finish();
|
||||||
@@ -499,6 +549,10 @@ bool Backup::copy_stats_tables() {
|
|||||||
return m_backup_impl->copy_stats_tables();
|
return m_backup_impl->copy_stats_tables();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Backup::copy_engine_binlogs(const char *binlog_dir, lsn_t backup_lsn) {
|
||||||
|
return m_backup_impl->copy_engine_binlogs(binlog_dir, backup_lsn);
|
||||||
|
}
|
||||||
|
|
||||||
bool Backup::wait_for_finish() {
|
bool Backup::wait_for_finish() {
|
||||||
return m_backup_impl->wait_for_finish();
|
return m_backup_impl->wait_for_finish();
|
||||||
}
|
}
|
||||||
|
@@ -28,6 +28,7 @@ class Backup {
|
|||||||
bool no_lock, bool collect_log_and_stats);
|
bool no_lock, bool collect_log_and_stats);
|
||||||
bool copy_log_tables(bool finalize);
|
bool copy_log_tables(bool finalize);
|
||||||
bool copy_stats_tables();
|
bool copy_stats_tables();
|
||||||
|
bool copy_engine_binlogs(const char *binlog_dir, lsn_t backup_lsn);
|
||||||
bool wait_for_finish();
|
bool wait_for_finish();
|
||||||
bool close_log_tables();
|
bool close_log_tables();
|
||||||
void set_post_copy_table_hook(const post_copy_table_hook_t &hook);
|
void set_post_copy_table_hook(const post_copy_table_hook_t &hook);
|
||||||
|
@@ -1386,7 +1386,8 @@ enum options_xtrabackup
|
|||||||
OPT_XB_IGNORE_INNODB_PAGE_CORRUPTION,
|
OPT_XB_IGNORE_INNODB_PAGE_CORRUPTION,
|
||||||
OPT_INNODB_FORCE_RECOVERY,
|
OPT_INNODB_FORCE_RECOVERY,
|
||||||
OPT_INNODB_CHECKPOINT,
|
OPT_INNODB_CHECKPOINT,
|
||||||
OPT_ARIA_LOG_DIR_PATH
|
OPT_ARIA_LOG_DIR_PATH,
|
||||||
|
OPT_BINLOG_DIRECTORY
|
||||||
};
|
};
|
||||||
|
|
||||||
struct my_option xb_client_options[]= {
|
struct my_option xb_client_options[]= {
|
||||||
@@ -2053,6 +2054,12 @@ struct my_option xb_server_options[] =
|
|||||||
(G_PTR *) &xtrabackup_help, (G_PTR *) &xtrabackup_help, 0,
|
(G_PTR *) &xtrabackup_help, (G_PTR *) &xtrabackup_help, 0,
|
||||||
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||||
|
|
||||||
|
{"binlog-directory", OPT_BINLOG_DIRECTORY,
|
||||||
|
"Directory containing binlog files, if different from datadir."
|
||||||
|
"Has effect only if server is using --binlog-storage-engine=innodb",
|
||||||
|
&opt_binlog_directory, &opt_binlog_directory,
|
||||||
|
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
|
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2367,6 +2374,10 @@ xb_get_one_option(const struct my_option *opt,
|
|||||||
if (my_handle_options_init_variables)
|
if (my_handle_options_init_variables)
|
||||||
fprintf(stderr, "Obsolete option: %s. Ignored\n", opt->name);
|
fprintf(stderr, "Obsolete option: %s. Ignored\n", opt->name);
|
||||||
break;
|
break;
|
||||||
|
case OPT_BINLOG_DIRECTORY:
|
||||||
|
|
||||||
|
ADD_PRINT_PARAM_OPT(opt_binlog_directory);
|
||||||
|
break;
|
||||||
#define MYSQL_CLIENT
|
#define MYSQL_CLIENT
|
||||||
#include "sslopt-case.h"
|
#include "sslopt-case.h"
|
||||||
#undef MYSQL_CLIENT
|
#undef MYSQL_CLIENT
|
||||||
@@ -2553,6 +2564,10 @@ static bool innodb_init_param()
|
|||||||
srv_undo_dir = (char*) ".";
|
srv_undo_dir = (char*) ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!opt_binlog_directory || !xtrabackup_backup) {
|
||||||
|
opt_binlog_directory = (char *) ".";
|
||||||
|
}
|
||||||
|
|
||||||
compile_time_assert(SRV_FORCE_IGNORE_CORRUPT == 1);
|
compile_time_assert(SRV_FORCE_IGNORE_CORRUPT == 1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -5396,6 +5411,17 @@ class BackupStages {
|
|||||||
nullptr);
|
nullptr);
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Copy InnoDB binlog files.
|
||||||
|
if (!m_common_backup.copy_engine_binlogs(opt_binlog_directory,
|
||||||
|
recv_sys.lsn)) {
|
||||||
|
msg("Error on copy InnoDB binlog files");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!m_common_backup.wait_for_finish()) {
|
||||||
|
msg("InnoDB binlog file backup process is finished with error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
backup_finish(backup_datasinks.m_data);
|
backup_finish(backup_datasinks.m_data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,105 @@
|
|||||||
|
--source include/have_innodb_binlog.inc
|
||||||
|
# Test does a lot of queries that take a lot of CPU under Valgrind.
|
||||||
|
--source include/not_valgrind.inc
|
||||||
|
|
||||||
|
RESET MASTER;
|
||||||
|
|
||||||
|
let $basedir=$MYSQLTEST_VARDIR/tmp/backup;
|
||||||
|
|
||||||
|
CREATE TABLE t1(a varchar(60) PRIMARY KEY, b VARCHAR(60)) ENGINE INNODB;
|
||||||
|
INSERT INTO t1 VALUES(1, NULL);
|
||||||
|
CREATE TABLE t2 (val INT) ENGINE=InnoDB;
|
||||||
|
INSERT INTO t2 VALUES (0);
|
||||||
|
|
||||||
|
--disable_query_log
|
||||||
|
--delimiter //
|
||||||
|
CREATE PROCEDURE gen_load()
|
||||||
|
MODIFIES SQL DATA
|
||||||
|
BEGIN
|
||||||
|
DECLARE i INT;
|
||||||
|
DECLARE flag TYPE OF t2.val;
|
||||||
|
SET i = 0;
|
||||||
|
load_loop: LOOP
|
||||||
|
SELECT val INTO flag FROM t2;
|
||||||
|
IF NOT (flag=0) THEN
|
||||||
|
LEAVE load_loop;
|
||||||
|
END IF;
|
||||||
|
START TRANSACTION;
|
||||||
|
INSERT INTO t1 VALUES (CONCAT("AbAdCaFe", LPAD(i, 6, "0")), @@SESSION.last_gtid);
|
||||||
|
COMMIT;
|
||||||
|
SET i = i + 1;
|
||||||
|
END LOOP;
|
||||||
|
END
|
||||||
|
//
|
||||||
|
--delimiter ;
|
||||||
|
--enable_query_log
|
||||||
|
|
||||||
|
connect (con1,localhost,root,,);
|
||||||
|
--echo *** Start a background load...
|
||||||
|
send CALL gen_load();
|
||||||
|
|
||||||
|
--connection default
|
||||||
|
--echo *** Doing backup...
|
||||||
|
--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$basedir $backup_args
|
||||||
|
--echo *** Doing prepare...
|
||||||
|
--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --prepare --target-dir=$basedir
|
||||||
|
|
||||||
|
--echo *** Stop the background load...
|
||||||
|
UPDATE t2 SET val=1;
|
||||||
|
--connection con1
|
||||||
|
reap;
|
||||||
|
--connection default
|
||||||
|
disconnect con1;
|
||||||
|
--let $count_master= `SELECT COUNT(*) FROM t1`
|
||||||
|
|
||||||
|
--echo *** Provision a new slave from the backup
|
||||||
|
--connect (server2,127.0.0.1,root,,,$SERVER_MYPORT_2)
|
||||||
|
--let $datadir_2= `SELECT @@datadir`
|
||||||
|
|
||||||
|
--echo *** Stopping provisioned server
|
||||||
|
--source include/shutdown_mysqld.inc
|
||||||
|
|
||||||
|
--echo *** Removing old datadir for provisioned server
|
||||||
|
--rmdir $datadir_2
|
||||||
|
|
||||||
|
--echo *** Provision new server from backup
|
||||||
|
--exec $XTRABACKUP --copy-back --datadir=$datadir_2 --target-dir=$basedir $copy_back_args
|
||||||
|
|
||||||
|
--let $restart_parameters= --skip-slave-start
|
||||||
|
--source include/start_mysqld.inc
|
||||||
|
|
||||||
|
--let $gtid_pos= `SELECT @@GLOBAL.gtid_binlog_pos`
|
||||||
|
|
||||||
|
--replace_result $gtid_pos GTID_POS
|
||||||
|
eval SET GLOBAL gtid_slave_pos= '$gtid_pos';
|
||||||
|
--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
|
||||||
|
eval CHANGE MASTER TO
|
||||||
|
master_ssl_verify_server_cert=0,
|
||||||
|
master_port=$SERVER_MYPORT_1, master_host='127.0.0.1', master_user='root',
|
||||||
|
master_use_gtid= Slave_pos;
|
||||||
|
START SLAVE;
|
||||||
|
|
||||||
|
--connection default
|
||||||
|
--source include/save_master_gtid.inc
|
||||||
|
|
||||||
|
--connection server2
|
||||||
|
--source include/sync_with_master_gtid.inc
|
||||||
|
--let $count_slave= `SELECT COUNT(*) FROM t1`
|
||||||
|
if ($count_master != $count_slave) {
|
||||||
|
--echo *** ERROR: Table on master has $count_master rows, but table on provisioned slave has $count_slave rows
|
||||||
|
--die Row difference on provisioned slave.
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
|
||||||
|
--connection server2
|
||||||
|
STOP SLAVE;
|
||||||
|
RESET SLAVE ALL;
|
||||||
|
DROP PROCEDURE gen_load;
|
||||||
|
DROP TABLE t1, t2;
|
||||||
|
|
||||||
|
--connection default
|
||||||
|
DROP PROCEDURE gen_load;
|
||||||
|
DROP TABLE t1, t2;
|
||||||
|
|
||||||
|
rmdir $basedir;
|
@@ -0,0 +1,5 @@
|
|||||||
|
!include my.cnf
|
||||||
|
|
||||||
|
[mysqld]
|
||||||
|
binlog_directory=binlogs
|
||||||
|
loose_innodb_log_file_size=96M
|
@@ -0,0 +1,40 @@
|
|||||||
|
RESET MASTER;
|
||||||
|
CREATE TABLE t1(a varchar(60) PRIMARY KEY, b VARCHAR(60)) ENGINE INNODB;
|
||||||
|
INSERT INTO t1 VALUES(1, NULL);
|
||||||
|
CREATE TABLE t2 (val INT) ENGINE=InnoDB;
|
||||||
|
INSERT INTO t2 VALUES (0);
|
||||||
|
connect con1,localhost,root,,;
|
||||||
|
*** Start a background load...
|
||||||
|
CALL gen_load();
|
||||||
|
connection default;
|
||||||
|
*** Doing backup...
|
||||||
|
*** Doing prepare...
|
||||||
|
*** Stop the background load...
|
||||||
|
UPDATE t2 SET val=1;
|
||||||
|
connection con1;
|
||||||
|
connection default;
|
||||||
|
disconnect con1;
|
||||||
|
*** Provision a new slave from the backup
|
||||||
|
connect server2,127.0.0.1,root,,,$SERVER_MYPORT_2;
|
||||||
|
*** Stopping provisioned server
|
||||||
|
*** Removing old datadir for provisioned server
|
||||||
|
*** Provision new server from backup
|
||||||
|
# restart: --skip-slave-start
|
||||||
|
SET GLOBAL gtid_slave_pos= 'GTID_POS';
|
||||||
|
CHANGE MASTER TO
|
||||||
|
master_ssl_verify_server_cert=0,
|
||||||
|
master_port=SERVER_MYPORT_1, master_host='127.0.0.1', master_user='root',
|
||||||
|
master_use_gtid= Slave_pos;
|
||||||
|
START SLAVE;
|
||||||
|
connection default;
|
||||||
|
include/save_master_gtid.inc
|
||||||
|
connection server2;
|
||||||
|
include/sync_with_master_gtid.inc
|
||||||
|
connection server2;
|
||||||
|
STOP SLAVE;
|
||||||
|
RESET SLAVE ALL;
|
||||||
|
DROP PROCEDURE gen_load;
|
||||||
|
DROP TABLE t1, t2;
|
||||||
|
connection default;
|
||||||
|
DROP PROCEDURE gen_load;
|
||||||
|
DROP TABLE t1, t2;
|
@@ -0,0 +1,4 @@
|
|||||||
|
--source include/not_embedded.inc
|
||||||
|
--let $backup_args=
|
||||||
|
--let $copy_back_args= --binlog-directory=binlogs
|
||||||
|
--source include/mariabackup_slave_provision.inc
|
@@ -0,0 +1,4 @@
|
|||||||
|
!include my.cnf
|
||||||
|
|
||||||
|
[mysqld]
|
||||||
|
loose_innodb_log_file_size=96M
|
@@ -0,0 +1,40 @@
|
|||||||
|
RESET MASTER;
|
||||||
|
CREATE TABLE t1(a varchar(60) PRIMARY KEY, b VARCHAR(60)) ENGINE INNODB;
|
||||||
|
INSERT INTO t1 VALUES(1, NULL);
|
||||||
|
CREATE TABLE t2 (val INT) ENGINE=InnoDB;
|
||||||
|
INSERT INTO t2 VALUES (0);
|
||||||
|
connect con1,localhost,root,,;
|
||||||
|
*** Start a background load...
|
||||||
|
CALL gen_load();
|
||||||
|
connection default;
|
||||||
|
*** Doing backup...
|
||||||
|
*** Doing prepare...
|
||||||
|
*** Stop the background load...
|
||||||
|
UPDATE t2 SET val=1;
|
||||||
|
connection con1;
|
||||||
|
connection default;
|
||||||
|
disconnect con1;
|
||||||
|
*** Provision a new slave from the backup
|
||||||
|
connect server2,127.0.0.1,root,,,$SERVER_MYPORT_2;
|
||||||
|
*** Stopping provisioned server
|
||||||
|
*** Removing old datadir for provisioned server
|
||||||
|
*** Provision new server from backup
|
||||||
|
# restart: --skip-slave-start
|
||||||
|
SET GLOBAL gtid_slave_pos= 'GTID_POS';
|
||||||
|
CHANGE MASTER TO
|
||||||
|
master_ssl_verify_server_cert=0,
|
||||||
|
master_port=SERVER_MYPORT_1, master_host='127.0.0.1', master_user='root',
|
||||||
|
master_use_gtid= Slave_pos;
|
||||||
|
START SLAVE;
|
||||||
|
connection default;
|
||||||
|
include/save_master_gtid.inc
|
||||||
|
connection server2;
|
||||||
|
include/sync_with_master_gtid.inc
|
||||||
|
connection server2;
|
||||||
|
STOP SLAVE;
|
||||||
|
RESET SLAVE ALL;
|
||||||
|
DROP PROCEDURE gen_load;
|
||||||
|
DROP TABLE t1, t2;
|
||||||
|
connection default;
|
||||||
|
DROP PROCEDURE gen_load;
|
||||||
|
DROP TABLE t1, t2;
|
@@ -0,0 +1,4 @@
|
|||||||
|
--source include/not_embedded.inc
|
||||||
|
--let $backup_args= --no-lock
|
||||||
|
--let $copy_back_args=
|
||||||
|
--source include/mariabackup_slave_provision.inc
|
@@ -24,6 +24,7 @@ INSERT INTO t1 VALUES (1, 10);
|
|||||||
--mkdir $master_datadir/binlog_dir
|
--mkdir $master_datadir/binlog_dir
|
||||||
--copy_file $master_datadir/master-bin.000001 $master_datadir/binlog_dir/master-bin.000001
|
--copy_file $master_datadir/master-bin.000001 $master_datadir/binlog_dir/master-bin.000001
|
||||||
--copy_file $master_datadir/master-bin.000001.idx $master_datadir/binlog_dir/master-bin.000001.idx
|
--copy_file $master_datadir/master-bin.000001.idx $master_datadir/binlog_dir/master-bin.000001.idx
|
||||||
|
--move_file $master_datadir/master-bin.state $master_datadir/binlog_dir/master-bin.state
|
||||||
--let $rpl_server_parameters= --binlog-directory=binlog_dir
|
--let $rpl_server_parameters= --binlog-directory=binlog_dir
|
||||||
--source include/rpl_start_server.inc
|
--source include/rpl_start_server.inc
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ INSERT INTO t1 VALUES (2, 11);
|
|||||||
|
|
||||||
# Move master back to using the standard binlog directory.
|
# Move master back to using the standard binlog directory.
|
||||||
--source include/rpl_stop_server.inc
|
--source include/rpl_stop_server.inc
|
||||||
|
--move_file $master_datadir/binlog_dir/master-bin.state $master_datadir/master-bin.state
|
||||||
--let $rpl_server_parameters=
|
--let $rpl_server_parameters=
|
||||||
--source include/rpl_start_server.inc
|
--source include/rpl_start_server.inc
|
||||||
|
|
||||||
|
@@ -1023,6 +1023,7 @@ fsp_log_binlog_write(mtr_t *mtr, fsp_binlog_page_entry *page,
|
|||||||
{
|
{
|
||||||
uint64_t file_no= page->file_no;
|
uint64_t file_no= page->file_no;
|
||||||
uint32_t page_no= page->page_no;
|
uint32_t page_no= page->page_no;
|
||||||
|
ut_ad(page->latched);
|
||||||
if (page_offset + len >= ibb_page_size - BINLOG_PAGE_DATA_END)
|
if (page_offset + len >= ibb_page_size - BINLOG_PAGE_DATA_END)
|
||||||
page->complete= true;
|
page->complete= true;
|
||||||
if (page->flushed_clean)
|
if (page->flushed_clean)
|
||||||
@@ -1736,7 +1737,13 @@ read_more_data:
|
|||||||
if (0)
|
if (0)
|
||||||
static_assert(BINLOG_PAGE_DATA == 0,
|
static_assert(BINLOG_PAGE_DATA == 0,
|
||||||
"Replace static_assert with code from above comment");
|
"Replace static_assert with code from above comment");
|
||||||
else if (s.in_page_offset >= ibb_page_size - (BINLOG_PAGE_DATA_END + 3) ||
|
|
||||||
|
/* Check for end-of-file. */
|
||||||
|
if (cur_end_offset == ~(uint64_t)0 ||
|
||||||
|
(s.page_no << ibb_page_size_shift) + s.in_page_offset >= cur_end_offset)
|
||||||
|
return sofar;
|
||||||
|
|
||||||
|
if (s.in_page_offset >= ibb_page_size - (BINLOG_PAGE_DATA_END + 3) ||
|
||||||
page_ptr[s.in_page_offset] == FSP_BINLOG_TYPE_FILLER)
|
page_ptr[s.in_page_offset] == FSP_BINLOG_TYPE_FILLER)
|
||||||
{
|
{
|
||||||
ut_ad(s.in_page_offset >= ibb_page_size - BINLOG_PAGE_DATA_END ||
|
ut_ad(s.in_page_offset >= ibb_page_size - BINLOG_PAGE_DATA_END ||
|
||||||
@@ -1744,11 +1751,6 @@ read_more_data:
|
|||||||
goto go_next_page;
|
goto go_next_page;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for end-of-file. */
|
|
||||||
if (cur_end_offset == ~(uint64_t)0 ||
|
|
||||||
(s.page_no << ibb_page_size_shift) + s.in_page_offset >= cur_end_offset)
|
|
||||||
return sofar;
|
|
||||||
|
|
||||||
type= page_ptr[s.in_page_offset];
|
type= page_ptr[s.in_page_offset];
|
||||||
if (type == 0)
|
if (type == 0)
|
||||||
{
|
{
|
||||||
@@ -1853,7 +1855,8 @@ skip_chunk:
|
|||||||
s.skip_current= false;
|
s.skip_current= false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.in_page_offset >= ibb_page_size - (BINLOG_PAGE_DATA_END + 3))
|
if (s.in_page_offset >= ibb_page_size - (BINLOG_PAGE_DATA_END + 3) &&
|
||||||
|
(s.page_no << ibb_page_size_shift) + s.in_page_offset < cur_end_offset)
|
||||||
{
|
{
|
||||||
go_next_page:
|
go_next_page:
|
||||||
/* End of page reached, move to the next page. */
|
/* End of page reached, move to the next page. */
|
||||||
|
@@ -29,6 +29,7 @@ InnoDB implementation of binlog.
|
|||||||
#include "log0log.h"
|
#include "log0log.h"
|
||||||
#include "small_vector.h"
|
#include "small_vector.h"
|
||||||
|
|
||||||
|
#include "mysys_err.h"
|
||||||
#include "rpl_gtid_base.h"
|
#include "rpl_gtid_base.h"
|
||||||
#include "handler.h"
|
#include "handler.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
@@ -237,9 +238,6 @@ class ha_innodb_binlog_reader : public handler_binlog_reader {
|
|||||||
/* Used to read the header of the commit record. */
|
/* Used to read the header of the commit record. */
|
||||||
byte rd_buf[5*COMPR_INT_MAX64];
|
byte rd_buf[5*COMPR_INT_MAX64];
|
||||||
private:
|
private:
|
||||||
int read_from_file(uint64_t end_offset, uchar *buf, uint32_t len);
|
|
||||||
int read_from_page(uchar *page_ptr, uint64_t end_offset,
|
|
||||||
uchar *buf, uint32_t len);
|
|
||||||
int read_data(uchar *buf, uint32_t len);
|
int read_data(uchar *buf, uint32_t len);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -539,17 +537,15 @@ static int read_gtid_state_from_page(rpl_binlog_state_base *state,
|
|||||||
1 File found (but may be empty according to out_empty).
|
1 File found (but may be empty according to out_empty).
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
binlog_recovery::get_header(uint64_t file_no, lsn_t &out_lsn, bool &out_empty)
|
get_binlog_header(const char *binlog_path, byte *page_buf,
|
||||||
noexcept
|
lsn_t &out_lsn, bool &out_empty) noexcept
|
||||||
{
|
{
|
||||||
char full_path[OS_FILE_MAX_PATH];
|
|
||||||
binlog_header_data header;
|
binlog_header_data header;
|
||||||
|
|
||||||
out_empty= true;
|
out_empty= true;
|
||||||
out_lsn= 0;
|
out_lsn= 0;
|
||||||
|
|
||||||
binlog_name_make(full_path, file_no, binlog_dir);
|
File fh= my_open(binlog_path, O_RDONLY | O_BINARY, MYF(0));
|
||||||
File fh= my_open(full_path, O_RDONLY | O_BINARY, MYF(0));
|
|
||||||
if (fh < (File)0)
|
if (fh < (File)0)
|
||||||
return (my_errno == ENOENT ? 0 : -1);
|
return (my_errno == ENOENT ? 0 : -1);
|
||||||
size_t read= my_pread(fh, page_buf, ibb_page_size, 0, MYF(0));
|
size_t read= my_pread(fh, page_buf, ibb_page_size, 0, MYF(0));
|
||||||
@@ -579,6 +575,16 @@ binlog_recovery::get_header(uint64_t file_no, lsn_t &out_lsn, bool &out_empty)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
binlog_recovery::get_header(uint64_t file_no, lsn_t &out_lsn, bool &out_empty)
|
||||||
|
noexcept
|
||||||
|
{
|
||||||
|
char full_path[OS_FILE_MAX_PATH];
|
||||||
|
binlog_name_make(full_path, file_no, binlog_dir);
|
||||||
|
return get_binlog_header(full_path, page_buf, out_lsn, out_empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool binlog_recovery::init_recovery(bool space_id, uint32_t page_no,
|
bool binlog_recovery::init_recovery(bool space_id, uint32_t page_no,
|
||||||
uint16_t offset,
|
uint16_t offset,
|
||||||
lsn_t start_lsn, lsn_t end_lsn,
|
lsn_t start_lsn, lsn_t end_lsn,
|
||||||
@@ -836,9 +842,24 @@ binlog_recovery::open_cur_file() noexcept
|
|||||||
if (cur_file_fh >= (File)0)
|
if (cur_file_fh >= (File)0)
|
||||||
my_close(cur_file_fh, MYF(0));
|
my_close(cur_file_fh, MYF(0));
|
||||||
binlog_name_make(full_path, cur_file_no, binlog_dir);
|
binlog_name_make(full_path, cur_file_no, binlog_dir);
|
||||||
cur_file_fh= my_open(full_path, O_RDWR | O_BINARY, MYF(MY_WME));
|
cur_file_fh= my_open(full_path, O_RDWR | O_BINARY, MYF(0));
|
||||||
if (cur_file_fh < (File)0)
|
if (cur_file_fh < (File)0)
|
||||||
return true;
|
{
|
||||||
|
/*
|
||||||
|
If we are on page 0 and the binlog file does not exist, then we should
|
||||||
|
create it (and recover its content).
|
||||||
|
Otherwise, it is an error, we cannot recover it as we are missing the
|
||||||
|
start of it.
|
||||||
|
*/
|
||||||
|
if (my_errno != ENOENT ||
|
||||||
|
cur_page_no != 0 ||
|
||||||
|
(cur_file_fh= my_open(full_path, O_RDWR | O_CREAT | O_TRUNC |
|
||||||
|
O_BINARY, MYF(0))) < (File)0)
|
||||||
|
{
|
||||||
|
my_error(EE_FILENOTFOUND, MYF(MY_WME), full_path, my_errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
cur_phys_size= (uint64_t)my_seek(cur_file_fh, 0, MY_SEEK_END, MYF(0));
|
cur_phys_size= (uint64_t)my_seek(cur_file_fh, 0, MY_SEEK_END, MYF(0));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -934,13 +955,12 @@ binlog_recovery::close_file() noexcept
|
|||||||
bool
|
bool
|
||||||
binlog_recovery::next_file() noexcept
|
binlog_recovery::next_file() noexcept
|
||||||
{
|
{
|
||||||
if (flush_page())
|
if (cur_page_offset && flush_page())
|
||||||
return true;
|
return true;
|
||||||
if (close_file())
|
if (close_file())
|
||||||
return true;
|
return true;
|
||||||
++cur_file_no;
|
++cur_file_no;
|
||||||
cur_page_no= 0;
|
cur_page_no= 0;
|
||||||
cur_page_offset= 0;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -948,7 +968,7 @@ binlog_recovery::next_file() noexcept
|
|||||||
bool
|
bool
|
||||||
binlog_recovery::next_page() noexcept
|
binlog_recovery::next_page() noexcept
|
||||||
{
|
{
|
||||||
if (flush_page())
|
if (cur_page_offset && flush_page())
|
||||||
return true;
|
return true;
|
||||||
++cur_page_no;
|
++cur_page_no;
|
||||||
return false;
|
return false;
|
||||||
@@ -1022,7 +1042,8 @@ binlog_recovery::apply_redo(bool space_id, uint32_t page_no, uint16_t offset,
|
|||||||
/* Test for moving to the next page. */
|
/* Test for moving to the next page. */
|
||||||
else if (page_no != cur_page_no)
|
else if (page_no != cur_page_no)
|
||||||
{
|
{
|
||||||
if (cur_page_offset < ibb_page_size - BINLOG_PAGE_DATA_END &&
|
if (cur_page_offset > BINLOG_PAGE_DATA &&
|
||||||
|
cur_page_offset < ibb_page_size - BINLOG_PAGE_DATA_END &&
|
||||||
!srv_force_recovery)
|
!srv_force_recovery)
|
||||||
{
|
{
|
||||||
sql_print_error("InnoDB: Missing recovery record in file_no=%"
|
sql_print_error("InnoDB: Missing recovery record in file_no=%"
|
||||||
@@ -1114,7 +1135,7 @@ binlog_recovery::update_page_from_record(uint16_t offset,
|
|||||||
Check if this is an InnoDB binlog file name.
|
Check if this is an InnoDB binlog file name.
|
||||||
Return the index/file_no if so.
|
Return the index/file_no if so.
|
||||||
*/
|
*/
|
||||||
static bool
|
bool
|
||||||
is_binlog_name(const char *name, uint64_t *out_idx)
|
is_binlog_name(const char *name, uint64_t *out_idx)
|
||||||
{
|
{
|
||||||
const size_t base_len= sizeof(BINLOG_NAME_BASE) - 1; // Length without '\0' terminator
|
const size_t base_len= sizeof(BINLOG_NAME_BASE) - 1; // Length without '\0' terminator
|
||||||
@@ -1263,7 +1284,15 @@ innodb_binlog_init(size_t binlog_size, const char *directory)
|
|||||||
}
|
}
|
||||||
|
|
||||||
start_binlog_prealloc_thread();
|
start_binlog_prealloc_thread();
|
||||||
binlog_sync_initial();
|
if (res < 0)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
We are creating binlogs anew from scratch.
|
||||||
|
Write and fsync the initial file-header, so that recovery will know where
|
||||||
|
to start in case of a crash.
|
||||||
|
*/
|
||||||
|
binlog_sync_initial();
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1463,13 +1492,26 @@ find_pos_in_binlog(uint64_t file_no, size_t file_size, byte *page_buf,
|
|||||||
ut_a(p <= page_end);
|
ut_a(p <= page_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_page_no= p_0 - 1;
|
/*
|
||||||
*out_pos_in_page= (uint32_t)(p - page_buf);
|
Normalize the position, so that we store (page_no+1, BINLOG_PAGE_DATA)
|
||||||
|
and not (page_no, page_size - BINLOG_PAGE_DATA_END).
|
||||||
if (*out_pos_in_page >= page_size - BINLOG_PAGE_DATA_END)
|
*/
|
||||||
ret= fsp_binlog_open(file_name, fh, file_no, file_size, p_0, nullptr);
|
byte *partial_page;
|
||||||
|
if (p == page_end)
|
||||||
|
{
|
||||||
|
*out_page_no= p_0;
|
||||||
|
*out_pos_in_page= BINLOG_PAGE_DATA;
|
||||||
|
partial_page= nullptr;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ret= fsp_binlog_open(file_name, fh, file_no, file_size, p_0 - 1, page_buf);
|
{
|
||||||
|
*out_page_no= p_0 - 1;
|
||||||
|
*out_pos_in_page= (uint32_t)(p - page_buf);
|
||||||
|
partial_page= page_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret= fsp_binlog_open(file_name, fh, file_no, file_size,
|
||||||
|
*out_page_no, partial_page);
|
||||||
uint64_t pos= (*out_page_no << page_size_shift) | *out_pos_in_page;
|
uint64_t pos= (*out_page_no << page_size_shift) | *out_pos_in_page;
|
||||||
binlog_cur_written_offset[idx].store(pos, std::memory_order_relaxed);
|
binlog_cur_written_offset[idx].store(pos, std::memory_order_relaxed);
|
||||||
binlog_cur_end_offset[idx].store(pos, std::memory_order_relaxed);
|
binlog_cur_end_offset[idx].store(pos, std::memory_order_relaxed);
|
||||||
|
@@ -168,6 +168,9 @@ binlog_name_make_short(char *name_buf, uint64_t file_no)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern bool is_binlog_name(const char *name, uint64_t *out_idx);
|
||||||
|
extern int get_binlog_header(const char *binlog_path, byte *page_buf,
|
||||||
|
lsn_t &out_lsn, bool &out_empty) noexcept;
|
||||||
extern void innodb_binlog_startup_init();
|
extern void innodb_binlog_startup_init();
|
||||||
extern bool innodb_binlog_init(size_t binlog_size, const char *directory);
|
extern bool innodb_binlog_init(size_t binlog_size, const char *directory);
|
||||||
extern void innodb_binlog_close(bool shutdown);
|
extern void innodb_binlog_close(bool shutdown);
|
||||||
|
Reference in New Issue
Block a user