From d6f7fd601637a070ccf922073c0ef9a41a129717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 16 Aug 2018 16:10:18 +0300 Subject: [PATCH] MDEV-13564: Refuse MLOG_TRUNCATE in mariabackup The MySQL 5.7 TRUNCATE TABLE is inherently incompatible with hot backup, because it is creating and deleting a separate log file, and it is not writing redo log for all changes of the InnoDB data dictionary tables. Refuse to create a corrupted backup if the unsafe form of TRUNCATE was executed. Note: Undo log tablespace truncation cannot be detected easily. Also it is incompatible with backup, for similar reasons. xtrabackup_backup_func(): "Subscribe to" the log events before the first invocation of xtrabackup_copy_logfile(). recv_parse_or_apply_log_rec_body(): If the function pointer log_truncate is set, invoke it to report MLOG_TRUNCATE. --- extra/mariabackup/xtrabackup.cc | 20 +++++++++++++------ .../mariabackup/truncate_during_backup.result | 4 ++++ .../mariabackup/truncate_during_backup.test | 19 ++++++++++++++++++ storage/innobase/include/log0recv.h | 9 +++++++-- storage/innobase/log/log0recv.cc | 13 +++++++++++- 5 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 mysql-test/suite/mariabackup/truncate_during_backup.result create mode 100644 mysql-test/suite/mariabackup/truncate_during_backup.test diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 5673c94fb0e..d9397b001b7 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -358,9 +358,8 @@ struct ddl_tracker_t { /* For DDL operation found in redo log, */ space_id_to_name_t id_to_name; }; -const space_id_t REMOVED_SPACE_ID = ULINT_MAX; -static ddl_tracker_t ddl_tracker; +static ddl_tracker_t ddl_tracker; /* Whether xtrabackup_binlog_info should be created on recovery */ static bool recover_binlog_info; @@ -618,9 +617,8 @@ void backup_file_op(ulint space_id, const byte* flags, /** Callback whenever MLOG_INDEX_LOAD happens. -@param[in] space_id space id to check -@return false */ -void backup_optimized_ddl_op(ulint space_id) +@param[in] space_id space id to check */ +static void backup_optimized_ddl_op(ulint space_id) { // TODO : handle incremental if (xtrabackup_incremental) @@ -631,6 +629,15 @@ void backup_optimized_ddl_op(ulint space_id) pthread_mutex_unlock(&backup_mutex); } +/** Callback whenever MLOG_TRUNCATE happens. */ +static void backup_truncate_fail() +{ + msg("mariabackup: Incompatible TRUNCATE operation detected.%s\n", + opt_lock_ddl_per_table + ? "" + : " Use --lock-ddl-per-table to lock all tables before backup."); +} + /* ======== Date copying thread context ======== */ typedef struct { @@ -4240,12 +4247,13 @@ fail_before_log_copying_thread_start: /* copy log file by current position */ log_copy_scanned_lsn = checkpoint_lsn_start; recv_sys->recovered_lsn = log_copy_scanned_lsn; + log_optimized_ddl_op = backup_optimized_ddl_op; + log_truncate = backup_truncate_fail; if (xtrabackup_copy_logfile()) goto fail_before_log_copying_thread_start; log_copying_stop = os_event_create(0); - log_optimized_ddl_op = backup_optimized_ddl_op; os_thread_create(log_copying_thread, NULL, &log_copying_thread_id); /* FLUSH CHANGED_PAGE_BITMAPS call */ diff --git a/mysql-test/suite/mariabackup/truncate_during_backup.result b/mysql-test/suite/mariabackup/truncate_during_backup.result new file mode 100644 index 00000000000..f9d5c785749 --- /dev/null +++ b/mysql-test/suite/mariabackup/truncate_during_backup.result @@ -0,0 +1,4 @@ +CREATE TABLE t1 ENGINE=InnoDB SELECT 1; +DROP TABLE t1; +SET GLOBAL innodb_log_checkpoint_now=1; +SET GLOBAL innodb_log_checkpoint_now=DEFAULT; diff --git a/mysql-test/suite/mariabackup/truncate_during_backup.test b/mysql-test/suite/mariabackup/truncate_during_backup.test new file mode 100644 index 00000000000..c01a74588e7 --- /dev/null +++ b/mysql-test/suite/mariabackup/truncate_during_backup.test @@ -0,0 +1,19 @@ +--source include/have_debug.inc +let $targetdir=$MYSQLTEST_VARDIR/tmp/backup; +mkdir $targetdir; + +CREATE TABLE t1 ENGINE=InnoDB SELECT 1; + +--let after_load_tablespaces=TRUNCATE test.t1 + +--disable_result_log +--error 1 +exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir --dbug=+d,mariabackup_events; +--enable_result_log + +--let after_load_tablespaces= + +DROP TABLE t1; +SET GLOBAL innodb_log_checkpoint_now=1; +SET GLOBAL innodb_log_checkpoint_now=DEFAULT; +rmdir $targetdir; diff --git a/storage/innobase/include/log0recv.h b/storage/innobase/include/log0recv.h index 6716b1cbd0d..28a4327ec4d 100644 --- a/storage/innobase/include/log0recv.h +++ b/storage/innobase/include/log0recv.h @@ -153,10 +153,15 @@ bool recv_parse_log_recs(lsn_t checkpoint_lsn, store_t store, bool apply); /** Moves the parsing buffer data left to the buffer start. */ void recv_sys_justify_left_parsing_buf(); -/** Report optimized DDL operation (without redo log), corresponding to MLOG_INDEX_LOAD. +/** Report optimized DDL operation (without redo log), +corresponding to MLOG_INDEX_LOAD. @param[in] space_id tablespace identifier */ -extern void(*log_optimized_ddl_op)(ulint space_id); +extern void (*log_optimized_ddl_op)(ulint space_id); + +/** Report backup-unfriendly TRUNCATE operation (with separate log file), +corresponding to MLOG_TRUNCATE. */ +extern void (*log_truncate)(); /** Report an operation to create, delete, or rename a file during backup. @param[in] space_id tablespace identifier diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index ee4d627261a..c623b884628 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -169,11 +169,16 @@ typedef std::map< static recv_spaces_t recv_spaces; -/** Report optimized DDL operation (without redo log), corresponding to MLOG_INDEX_LOAD. +/** Report optimized DDL operation (without redo log), +corresponding to MLOG_INDEX_LOAD. @param[in] space_id tablespace identifier */ void (*log_optimized_ddl_op)(ulint space_id); +/** Report backup-unfriendly TRUNCATE operation (with separate log file), +corresponding to MLOG_TRUNCATE. */ +void (*log_truncate)(); + /** Report an operation to create, delete, or rename a file during backup. @param[in] space_id tablespace identifier @param[in] flags tablespace flags (NULL if not create) @@ -1196,6 +1201,12 @@ recv_parse_or_apply_log_rec_body( } return(ptr + 8); case MLOG_TRUNCATE: + if (log_truncate) { + ut_ad(srv_operation != SRV_OPERATION_NORMAL); + log_truncate(); + recv_sys->found_corrupt_fs = true; + return NULL; + } return(truncate_t::parse_redo_entry(ptr, end_ptr, space_id)); default: