diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 969ffb6907db0..dae4389d928f9 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -79,6 +79,9 @@ Street, Fifth Floor, Boston, MA 02110-1335 USA #include #include #include "trx0sys.h" +#include "trx0trx.h" +#include "trx0roll.h" +#include #include #include #include "ha_innodb.h" @@ -151,6 +154,8 @@ my_bool xtrabackup_help; my_bool xtrabackup_export; my_bool ignored_option; +my_bool xtrabackup_rollback_xa; + longlong xtrabackup_use_memory; uint opt_protocol; @@ -1354,6 +1359,7 @@ enum options_xtrabackup OPT_XTRA_BACKUP, OPT_XTRA_PREPARE, OPT_XTRA_EXPORT, + OPT_XTRA_ROLLBACK_XA, OPT_XTRA_PRINT_PARAM, OPT_XTRA_USE_MEMORY, OPT_XTRA_THROTTLE, @@ -1477,6 +1483,13 @@ struct my_option xb_client_options[]= { "create files to import to another database when prepare.", (G_PTR *) &xtrabackup_export, (G_PTR *) &xtrabackup_export, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"rollback-xa", OPT_XTRA_ROLLBACK_XA, + "Rollback prepared XA transactions on --prepare. Enabled by default; " + "use --skip-rollback-xa to disable. " + "After preparing target directory with this option " + "it can no longer be a base for incremental backup.", + (G_PTR *) &xtrabackup_rollback_xa, (G_PTR *) &xtrabackup_rollback_xa, 0, + GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"print-param", OPT_XTRA_PRINT_PARAM, "print parameter of mysqld needed for copyback.", (G_PTR *) &xtrabackup_print_param, (G_PTR *) &xtrabackup_print_param, 0, @@ -6900,8 +6913,26 @@ static bool xtrabackup_prepare_func(char** argv) if (!ok) goto cleanup; } + /* Prevent incompatible combination of --rollback_xa and --export + options. These options cannot be used together because: + - --export sets SRV_OPERATION_RESTORE_EXPORT which makes the redo + log mapping read-only for consistency during table export + - --rollback_xa requires write access to the log system to modify + transaction state during XA rollback + The combination creates mmap state inconsistency in InnoDB's MTR + system, leading to crash. + */ + if (xtrabackup_rollback_xa && xtrabackup_export) { + msg("mariabackup: ERROR: --rollback_xa and --export options cannot " + "be used together. This combination causes internal mmap state " + "inconsistency leading to crashes."); + goto error; + } + srv_operation = xtrabackup_export - ? SRV_OPERATION_RESTORE_EXPORT : SRV_OPERATION_RESTORE; + ? SRV_OPERATION_RESTORE_EXPORT + : (xtrabackup_rollback_xa ? SRV_OPERATION_RESTORE_ROLLBACK_XA + : SRV_OPERATION_RESTORE); if (innodb_init_param()) { goto error; @@ -6920,12 +6951,46 @@ static bool xtrabackup_prepare_func(char** argv) srv_max_dirty_pages_pct_lwm = srv_max_buf_pool_modified_pct; } + if (xtrabackup_rollback_xa) + srv_fast_shutdown = 0; + recv_sys.recovery_on = false; if (innodb_init()) { goto error; } - ut_ad(!fil_system.freeze_space_list); + if (xtrabackup_rollback_xa) { + /* Roll back recovered prepared XA transactions. + The backup does not contain the binary log needed + to resolve them. (MDEV-36025) */ + XID *xid_list = + (XID *) my_malloc(PSI_NOT_INSTRUMENTED, + MAX_XID_LIST_SIZE * sizeof(XID), + MYF(0)); + if (!xid_list) { + msg("Can't allocate memory for XID list"); + ok = false; + goto cleanup; + } + ut_ad(recv_no_log_write); + ut_d(recv_no_log_write = false); + int got; + while ((got = trx_recover_for_mysql(xid_list, + MAX_XID_LIST_SIZE)) > 0) { + for (int i = 0; i < got; i++) { + trx_t *trx = trx_get_trx_by_xid(&xid_list[i]); + if (trx) { + trx_rollback_for_mysql(trx); + trx->free(); + msg("Rolled back prepared XA transaction"); + } + } + } + my_free(xid_list); + ut_d(recv_no_log_write = true); + } + + ut_ad(!fil_system.freeze_space_list); corrupted_pages.read_from_file(MB_CORRUPTED_PAGES_FILE); if (xtrabackup_incremental) @@ -6982,9 +7047,21 @@ static bool xtrabackup_prepare_func(char** argv) else if (ok) xb_write_galera_info(xtrabackup_incremental); #endif - innodb_shutdown(); + /* Without buf_flush_sync(), the rolled-back changes would exist only + in the buffer pool and be lost on shutdown, leaving the data files in + an inconsistent state. + In the innodb_preshutdown(), the condition was updated to include + SRV_OPERATION_RESTORE_ROLLBACK_XA so it waits for transactions when + srv_fast_shutdown == 0. The innodb_preshutdown() is called by + innodb_shutdown(), which will wait for any active transactions to + finish and shut down purge and undo background sources for + SRV_OPERATION_RESTORE_ROLLBACK_XA. */ + if (xtrabackup_rollback_xa) + buf_flush_sync_batch(0, false); + + innodb_shutdown(); - innodb_free_param(); + innodb_free_param(); /* output to metadata file */ if (ok) { diff --git a/mysql-test/suite/mariabackup/encrypted_export.test b/mysql-test/suite/mariabackup/encrypted_export.test index d1802118a0960..7102579de23bc 100644 --- a/mysql-test/suite/mariabackup/encrypted_export.test +++ b/mysql-test/suite/mariabackup/encrypted_export.test @@ -15,7 +15,7 @@ exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --parallel=10 echo # xtrabackup prepare export; --disable_result_log -exec $XTRABACKUP --prepare --export --target-dir=$targetdir; +exec $XTRABACKUP --prepare --export --target-dir=$targetdir --skip-rollback-xa; --enable_result_log --source include/start_mysqld.inc diff --git a/mysql-test/suite/mariabackup/innodb_force_recovery.test b/mysql-test/suite/mariabackup/innodb_force_recovery.test index 3a7b3c6106c6d..0f37ab90c09db 100644 --- a/mysql-test/suite/mariabackup/innodb_force_recovery.test +++ b/mysql-test/suite/mariabackup/innodb_force_recovery.test @@ -66,7 +66,7 @@ close $fd; EOF --echo # "innodb_force_recovery" should be read from "backup-my.cnf" (mariabackup) --disable_result_log -exec $XTRABACKUP --defaults-file=$targetdir/backup-my.cnf --prepare --export --target-dir=$targetdir >$backuplog; +exec $XTRABACKUP --defaults-file=$targetdir/backup-my.cnf --prepare --export --target-dir=$targetdir --skip-rollback-xa >$backuplog; --enable_result_log --let SEARCH_PATTERN=innodb_force_recovery = 1 --let SEARCH_FILE=$backuplog @@ -84,7 +84,7 @@ close $fd; EOF --echo # "innodb_force_recovery=1" should be read from "backup-my.cnf" (innobackupex) --disable_result_log -exec $XTRABACKUP --innobackupex --defaults-file=$targetdir/backup-my.cnf --apply-log --export $targetdir >$backuplog; +exec $XTRABACKUP --innobackupex --defaults-file=$targetdir/backup-my.cnf --apply-log --export --skip-rollback-xa $targetdir >$backuplog; --enable_result_log --let SEARCH_PATTERN=innodb_force_recovery = 1 --let SEARCH_FILE=$backuplog @@ -121,7 +121,7 @@ close $fd; EOF --echo # "innodb_force_recovery" from the command line should override "backup-my.cnf" (innobackupex) --disable_result_log -exec $XTRABACKUP --innobackupex --defaults-file=$targetdir/backup-my.cnf --apply-log --innodb-force-recovery=0 --export $targetdir >$backuplog; +exec $XTRABACKUP --innobackupex --defaults-file=$targetdir/backup-my.cnf --apply-log --innodb-force-recovery=0 --export --skip-rollback-xa $targetdir >$backuplog; --enable_result_log --let SEARCH_PATTERN=innodb_force_recovery = 1 --let SEARCH_FILE=$backuplog diff --git a/mysql-test/suite/mariabackup/innodb_xa_rollback.result b/mysql-test/suite/mariabackup/innodb_xa_rollback.result new file mode 100644 index 0000000000000..9ba48d54b4a45 --- /dev/null +++ b/mysql-test/suite/mariabackup/innodb_xa_rollback.result @@ -0,0 +1,41 @@ +CALL mtr.add_suppression("Found 1 prepared XA transactions"); +RESET MASTER; +CREATE TABLE t1 (a INT) ENGINE=INNODB; +XA START 'test1'; +INSERT t1 VALUES (10); +XA END 'test1'; +XA PREPARE 'test1'; +XA RECOVER; +formatID gtrid_length bqual_length data +1 5 0 test1 +# xtrabackup backup +XA ROLLBACK 'test1'; +# xtrabackup prepare and rollback prepared XA +# shutdown server +# remove datadir +# xtrabackup move back +# restart +XA RECOVER; +formatID gtrid_length bqual_length data +1 5 0 test1 +# xtrabackup prepare and DO NOT rollback prepared XA +# shutdown server +# remove datadir +# xtrabackup move back +# restart +XA RECOVER; +formatID gtrid_length bqual_length data +1 5 0 test1 +XA ROLLBACK 'test1'; +# xtrabackup prepare for export and rollback prepared XA (should fail) +# The above command should fail with error about incompatible options +# xtrabackup prepare for export and DO NOT rollback prepared XA +# shutdown server +# remove datadir +# xtrabackup move back +# restart +XA RECOVER; +formatID gtrid_length bqual_length data +1 5 0 test1 +XA ROLLBACK 'test1'; +DROP TABLE t1; diff --git a/mysql-test/suite/mariabackup/innodb_xa_rollback.test b/mysql-test/suite/mariabackup/innodb_xa_rollback.test new file mode 100644 index 0000000000000..d86531cc070da --- /dev/null +++ b/mysql-test/suite/mariabackup/innodb_xa_rollback.test @@ -0,0 +1,75 @@ +# +# Optionally rollback prepared XA when backup is prepared +# +--source include/have_innodb.inc +--source include/have_binlog_format_mixed.inc + +CALL mtr.add_suppression("Found 1 prepared XA transactions"); + +RESET MASTER; + +let targetdir1=$MYSQLTEST_VARDIR/tmp/backup1; +let targetdir2=$MYSQLTEST_VARDIR/tmp/backup2; +let targetdir3=$MYSQLTEST_VARDIR/tmp/backup3; +let targetdir4=$MYSQLTEST_VARDIR/tmp/backup4; + +CREATE TABLE t1 (a INT) ENGINE=INNODB; +XA START 'test1'; +INSERT t1 VALUES (10); +XA END 'test1'; +XA PREPARE 'test1'; +XA RECOVER; + +--echo # xtrabackup backup +--disable_result_log +exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir1; +--enable_result_log + +perl; +use lib "lib"; +use My::Handles { suppress_init_messages => 1 }; +use My::File::Path; +copytree($ENV{'targetdir1'}, $ENV{'targetdir2'}); +copytree($ENV{'targetdir1'}, $ENV{'targetdir3'}); +copytree($ENV{'targetdir1'}, $ENV{'targetdir4'}); +EOF + +XA ROLLBACK 'test1'; + +--echo # xtrabackup prepare and rollback prepared XA +--disable_result_log +exec $XTRABACKUP --prepare --rollback_xa --target-dir=$targetdir1; +--let $targetdir = $targetdir1 +--source include/restart_and_restore.inc +--enable_result_log +XA RECOVER; + +--echo # xtrabackup prepare and DO NOT rollback prepared XA +--disable_result_log +exec $XTRABACKUP --prepare --target-dir=$targetdir2; +--let $targetdir = $targetdir2 +--source include/restart_and_restore.inc +--enable_result_log +XA RECOVER; +XA ROLLBACK 'test1'; + +--echo # xtrabackup prepare for export and rollback prepared XA (should fail) +--disable_result_log +--error 1 +exec $XTRABACKUP --prepare --rollback_xa --export --target-dir=$targetdir3; +--echo # The above command should fail with error about incompatible options + +--echo # xtrabackup prepare for export and DO NOT rollback prepared XA +--disable_result_log +exec $XTRABACKUP --prepare --export --target-dir=$targetdir4 --skip-rollback-xa; +--let $targetdir = $targetdir4 +--source include/restart_and_restore.inc +--enable_result_log +XA RECOVER; +XA ROLLBACK 'test1'; + +DROP TABLE t1; +rmdir $targetdir1; +rmdir $targetdir2; +rmdir $targetdir3; +rmdir $targetdir4; diff --git a/mysql-test/suite/mariabackup/page_compression_level.test b/mysql-test/suite/mariabackup/page_compression_level.test index e80c956e6210a..2b831877f39f8 100644 --- a/mysql-test/suite/mariabackup/page_compression_level.test +++ b/mysql-test/suite/mariabackup/page_compression_level.test @@ -10,7 +10,7 @@ let $targetdir=$MYSQLTEST_VARDIR/tmp/backup; --disable_result_log exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir; echo # xtrabackup prepare; -exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --defaults-group-suffix=.1 --prepare --export --target-dir=$targetdir; +exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --defaults-group-suffix=.1 --prepare --export --target-dir=$targetdir --skip-rollback-xa; --enable_result_log --let SEARCH_PATTERN=innodb_compression_level=3 diff --git a/mysql-test/suite/mariabackup/partial.test b/mysql-test/suite/mariabackup/partial.test index e03bd3030c6be..4289395fecb3f 100644 --- a/mysql-test/suite/mariabackup/partial.test +++ b/mysql-test/suite/mariabackup/partial.test @@ -41,7 +41,7 @@ EOF echo # xtrabackup prepare; --disable_result_log -exec $XTRABACKUP --defaults-file=$server_cnf --defaults-group-suffix=.1 --prepare --export --target-dir=$targetdir; +exec $XTRABACKUP --defaults-file=$server_cnf --defaults-group-suffix=.1 --prepare --export --target-dir=$targetdir --skip-rollback-xa; --enable_result_log list_files $targetdir/test *.cfg; @@ -59,7 +59,7 @@ SELECT * FROM t1; --echo # MDEV-33023 Crash in mariadb-backup --prepare --export after --prepare --disable_result_log exec $XTRABACKUP --defaults-file=$server_cnf --defaults-group-suffix=.1 --prepare --target-dir=$targetdir; -exec $XTRABACKUP --defaults-file=$server_cnf --defaults-group-suffix=.1 --prepare --export --target-dir=$targetdir; +exec $XTRABACKUP --defaults-file=$server_cnf --defaults-group-suffix=.1 --prepare --export --target-dir=$targetdir --skip-rollback-xa; --enable_result_log list_files $targetdir/test *.cfg; diff --git a/mysql-test/suite/mariabackup/partial_exclude.test b/mysql-test/suite/mariabackup/partial_exclude.test index fdf113ff5f766..832fde2ce3807 100644 --- a/mysql-test/suite/mariabackup/partial_exclude.test +++ b/mysql-test/suite/mariabackup/partial_exclude.test @@ -57,7 +57,7 @@ rmdir $MYSQLD_DATADIR/db5; --let $backup_log=$MYSQLTEST_VARDIR/tmp/backup.log --disable_result_log -exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --export --prepare --target-dir="$targetdir" > $backup_log; +exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --export --prepare --target-dir="$targetdir" --skip-rollback-xa > $backup_log; --enable_result_log --let SEARCH_FILE=$backup_log diff --git a/mysql-test/suite/mariabackup/partition_partial.test b/mysql-test/suite/mariabackup/partition_partial.test index 30e31a9d43e3c..c459d39da685a 100644 --- a/mysql-test/suite/mariabackup/partition_partial.test +++ b/mysql-test/suite/mariabackup/partition_partial.test @@ -23,7 +23,7 @@ INSERT INTO t1 VALUES (1), (101), (201), (301); echo # xtrabackup prepare; --disable_result_log -exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --defaults-group-suffix=.1 --prepare --export --target-dir=$targetdir; +exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --defaults-group-suffix=.1 --prepare --export --target-dir=$targetdir --skip-rollback-xa; --enable_result_log diff --git a/mysql-test/suite/mariabackup/xa_prepared_on_restore.result b/mysql-test/suite/mariabackup/xa_prepared_on_restore.result new file mode 100644 index 0000000000000..1c433d7cf4ec6 --- /dev/null +++ b/mysql-test/suite/mariabackup/xa_prepared_on_restore.result @@ -0,0 +1,30 @@ +call mtr.add_suppression("Can't init tc log"); +call mtr.add_suppression("Found .* prepared transactions!"); +call mtr.add_suppression("Aborting"); +CREATE TABLE t1 (a INT) ENGINE=INNODB; +SET GLOBAL innodb_flush_log_at_trx_commit=1; +INSERT INTO t1 VALUES (0); +FLUSH TABLES; +connect con1,localhost,root,,; +SET debug_sync='ha_commit_trans_after_prepare WAIT_FOR go'; +INSERT INTO t1 VALUES (1); +connect con2,localhost,root,,; +SET debug_sync='ha_commit_trans_after_prepare WAIT_FOR go'; +INSERT INTO t1 VALUES (2); +connect con3,localhost,root,,; +SET debug_sync='ha_commit_trans_after_prepare WAIT_FOR go'; +INSERT INTO t1 VALUES (3); +connect con4,localhost,root,,; +SET debug_sync='ha_commit_trans_after_prepare WAIT_FOR go'; +INSERT INTO t1 VALUES (4); +connect con5,localhost,root,,; +SET debug_sync='ha_commit_trans_after_prepare WAIT_FOR go'; +INSERT INTO t1 VALUES (5); +connection default; +# Kill the server +FOUND 1 /Found .* prepared transactions!/ in mysqld.1.err +# restart +SELECT * FROM t1; +a +0 +DROP TABLE t1; diff --git a/mysql-test/suite/mariabackup/xa_prepared_on_restore.test b/mysql-test/suite/mariabackup/xa_prepared_on_restore.test new file mode 100644 index 0000000000000..3f5935fdc372e --- /dev/null +++ b/mysql-test/suite/mariabackup/xa_prepared_on_restore.test @@ -0,0 +1,89 @@ +# +# MDEV-36025: After mariabackup --prepare, server startup fails with +# "Found N prepared transactions!" because the backup does not include +# the binary log needed to resolve internal 2PC prepared transactions. +# + +--source include/have_innodb.inc +--source include/have_log_bin.inc +--source include/have_debug_sync.inc +--source include/not_embedded.inc + +call mtr.add_suppression("Can't init tc log"); +call mtr.add_suppression("Found .* prepared transactions!"); +call mtr.add_suppression("Aborting"); + +CREATE TABLE t1 (a INT) ENGINE=INNODB; +SET GLOBAL innodb_flush_log_at_trx_commit=1; +INSERT INTO t1 VALUES (0); +FLUSH TABLES; + +let $_datadir= `SELECT @@datadir`; +let $targetdir=$MYSQLTEST_VARDIR/tmp/backup; +let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err; + +# Pause 5 transactions after internal 2PC prepare (InnoDB prepare done, +# but not yet committed in binlog). + +--connect (con1,localhost,root,,) +SET debug_sync='ha_commit_trans_after_prepare WAIT_FOR go'; +--send INSERT INTO t1 VALUES (1) + +--connect (con2,localhost,root,,) +SET debug_sync='ha_commit_trans_after_prepare WAIT_FOR go'; +--send INSERT INTO t1 VALUES (2) + +--connect (con3,localhost,root,,) +SET debug_sync='ha_commit_trans_after_prepare WAIT_FOR go'; +--send INSERT INTO t1 VALUES (3) + +--connect (con4,localhost,root,,) +SET debug_sync='ha_commit_trans_after_prepare WAIT_FOR go'; +--send INSERT INTO t1 VALUES (4) + +--connect (con5,localhost,root,,) +SET debug_sync='ha_commit_trans_after_prepare WAIT_FOR go'; +--send INSERT INTO t1 VALUES (5) + +--connection default + +--let $wait_condition= SELECT count(*) = 5 FROM information_schema.processlist WHERE state = 'debug sync point: ha_commit_trans_after_prepare' +--source include/wait_condition.inc + +# xtrabackup backup with --no-lock to avoid deadlock with paused transaction; +--disable_result_log +exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --no-lock --target-dir=$targetdir; +--enable_result_log + +# Kill server while transaction is still in prepared state +--source include/kill_mysqld.inc + +# xtrabackup prepare; +--disable_result_log +exec $XTRABACKUP --prepare --target-dir=$targetdir; +--enable_result_log + +# Restore from backup +rmdir $_datadir; +--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --copy-back --datadir=$_datadir --target-dir=$targetdir --parallel=2 --throttle=1 + +# Try to start the server - should fail because InnoDB has prepared +# transactions but there is no binlog to resolve them. +--error 1 +--exec $MYSQLD_LAST_CMD + +--let SEARCH_PATTERN= Found .* prepared transactions! +--source include/search_pattern_in_file.inc + +# Use --tc-heuristic-recover=ROLLBACK to roll back the prepared transactions +--error 1 +--exec $MYSQLD_LAST_CMD --tc-heuristic-recover=ROLLBACK + +# Normal start should now succeed +--source include/start_mysqld.inc + +# After restore, prepared transactions should have been rolled back; +SELECT * FROM t1; + +DROP TABLE t1; +rmdir $targetdir; diff --git a/mysql-test/suite/mariabackup/xb_page_compress.test b/mysql-test/suite/mariabackup/xb_page_compress.test index e2819e264c1a2..5b761b7e86e91 100644 --- a/mysql-test/suite/mariabackup/xb_page_compress.test +++ b/mysql-test/suite/mariabackup/xb_page_compress.test @@ -29,7 +29,7 @@ let $targetdir=$MYSQLTEST_VARDIR/tmp/backup; --disable_result_log exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --parallel=10 "--tables=test.*1" --target-dir=$targetdir; echo # xtrabackup prepare; -exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --defaults-group-suffix=.1 --prepare --export --target-dir=$targetdir; +exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --defaults-group-suffix=.1 --prepare --export --target-dir=$targetdir --skip-rollback-xa; --enable_result_log ALTER TABLE t1 DISCARD TABLESPACE; diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index e1d153b0cc9aa..17e189e0e06d3 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -2769,8 +2769,7 @@ ATTRIBUTE_COLD void buf_flush_page_cleaner_init() noexcept { ut_ad(!buf_page_cleaner_is_active); ut_ad(srv_operation <= SRV_OPERATION_EXPORT_RESTORED || - srv_operation == SRV_OPERATION_RESTORE || - srv_operation == SRV_OPERATION_RESTORE_EXPORT); + is_mariabackup_restore_or_export()); buf_flush_async_lsn= 0; buf_flush_sync_lsn= 0; buf_page_cleaner_is_active= true; diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index 1b9dd6228aacc..7d45258469422 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -437,7 +437,7 @@ static bool fil_node_open_file(fil_node_t *node, const byte *page, bool no_lsn) ut_ad(!node->is_open()); ut_ad(!is_predefined_tablespace(node->space->id) || srv_operation == SRV_OPERATION_BACKUP || - srv_operation == SRV_OPERATION_RESTORE || + is_mariabackup_restore() || srv_operation == SRV_OPERATION_RESTORE_DELTA); ut_ad(!node->space->is_temporary()); ut_ad(node->space->referenced()); @@ -1172,8 +1172,8 @@ void fil_space_t::close() mysql_mutex_lock(&fil_system.mutex); ut_ad(this == fil_system.temp_space || srv_operation == SRV_OPERATION_BACKUP - || srv_operation == SRV_OPERATION_RESTORE - || srv_operation == SRV_OPERATION_RESTORE_DELTA); + || srv_operation == SRV_OPERATION_RESTORE_DELTA + || is_mariabackup_restore()); for (fil_node_t* node = UT_LIST_GET_FIRST(chain); node != NULL; @@ -2322,6 +2322,7 @@ fil_ibd_discover( break; case SRV_OPERATION_RESTORE_EXPORT: case SRV_OPERATION_RESTORE: + case SRV_OPERATION_RESTORE_ROLLBACK_XA: break; case SRV_OPERATION_NORMAL: case SRV_OPERATION_EXPORT_RESTORED: @@ -2426,7 +2427,7 @@ fil_ibd_load(uint32_t space_id, const char *filename, fil_space_t *&space) noexc return FIL_LOAD_ID_CHANGED; } - if (srv_operation == SRV_OPERATION_RESTORE) { + if (is_mariabackup_restore()) { /* Replace absolute DATA DIRECTORY file paths with short names relative to the backup directory. */ const char* name = strrchr(filename, '/'); diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index fafb6218002bd..bae9f1440b7b2 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -322,6 +322,8 @@ enum srv_operation_mode { SRV_OPERATION_BACKUP, /** Mariabackup restoring a backup for subsequent --copy-back */ SRV_OPERATION_RESTORE, + /** Mariabackup restoring a backup with rolling back prepared XA's */ + SRV_OPERATION_RESTORE_ROLLBACK_XA, /** Mariabackup restoring the incremental part of a backup */ SRV_OPERATION_RESTORE_DELTA, /** Mariabackup restoring a backup for subsequent --export */ @@ -334,6 +336,21 @@ enum srv_operation_mode { /** Current mode of operation */ extern enum srv_operation_mode srv_operation; +inline bool is_mariabackup_restore() +{ + /* To rollback XA's trx_sys must be initialized, the rest is the same + as regular backup restore, that is why we join these two operations in + most cases. */ + return srv_operation == SRV_OPERATION_RESTORE + || srv_operation == SRV_OPERATION_RESTORE_ROLLBACK_XA; +} + +inline bool is_mariabackup_restore_or_export() +{ + return is_mariabackup_restore() + || srv_operation == SRV_OPERATION_RESTORE_EXPORT; +} + /** whether this is the server's first start after mariabackup --prepare */ extern bool srv_start_after_restore; diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 3d87d274b2844..9e454eac2a3ce 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -717,7 +717,7 @@ static struct mysql_mutex_assert_owner(&recv_sys.mutex); const char *filename= f_name.c_str(); - if (srv_operation == SRV_OPERATION_RESTORE) + if (is_mariabackup_restore()) { /* Replace absolute DATA DIRECTORY file paths with short names relative to the backup directory. */ @@ -894,7 +894,7 @@ static struct crypt_data); ut_ad(space); const char *filename= name.c_str(); - if (srv_operation == SRV_OPERATION_RESTORE) + if (is_mariabackup_restore()) { if (const char *tbl_name= strrchr(filename, '/')) { @@ -904,7 +904,7 @@ static struct } } pfs_os_file_t handle= OS_FILE_CLOSED; - if (srv_operation == SRV_OPERATION_RESTORE) + if (is_mariabackup_restore()) { /* During mariadb-backup --backup, a table could be renamed, created and dropped, and we may be missing the file at this @@ -1308,8 +1308,7 @@ static void fil_name_process(const char *name, ulint len, uint32_t space_id, mfile_type_t ftype, lsn_t lsn, bool if_exists) { ut_ad(srv_operation <= SRV_OPERATION_EXPORT_RESTORED - || srv_operation == SRV_OPERATION_RESTORE - || srv_operation == SRV_OPERATION_RESTORE_EXPORT); + || is_mariabackup_restore_or_export()); /* We will also insert space=NULL into the map, so that further checks can ensure that a FILE_MODIFY record was @@ -1405,7 +1404,7 @@ static void fil_name_process(const char *name, ulint len, uint32_t space_id, FILE_* record. */ ut_ad(space == NULL); - if (srv_operation == SRV_OPERATION_RESTORE && d + if (is_mariabackup_restore() && d && ftype == FILE_RENAME) { rename: d->file_name = fname.name; @@ -1418,7 +1417,7 @@ static void fil_name_process(const char *name, ulint len, uint32_t space_id, } if (srv_force_recovery - || srv_operation == SRV_OPERATION_RESTORE) { + || is_mariabackup_restore()) { /* Without innodb_force_recovery, missing tablespaces will only be reported in @@ -1438,7 +1437,7 @@ static void fil_name_process(const char *name, ulint len, uint32_t space_id, case FIL_LOAD_DEFER: if (d && ftype == FILE_RENAME - && srv_operation == SRV_OPERATION_RESTORE) { + && is_mariabackup_restore()) { goto rename; } /* Skip the deferred spaces @@ -4113,8 +4112,7 @@ ATTRIBUTE_COLD static void buf_pool_invalidate() noexcept void recv_sys_t::apply(bool last_batch) { ut_ad(srv_operation <= SRV_OPERATION_EXPORT_RESTORED || - srv_operation == SRV_OPERATION_RESTORE || - srv_operation == SRV_OPERATION_RESTORE_EXPORT); + is_mariabackup_restore_or_export()); mysql_mutex_assert_owner(&mutex); @@ -4147,9 +4145,7 @@ void recv_sys_t::apply(bool last_batch) if (!pages.empty()) { recv_no_ibuf_operations = !last_batch || - srv_operation == SRV_OPERATION_RESTORE || - srv_operation == SRV_OPERATION_RESTORE_EXPORT; - ut_ad(!last_batch || lsn == scanned_lsn); + is_mariabackup_restore_or_export(); progress_time= time(nullptr); report_progress(); @@ -4237,8 +4233,7 @@ void recv_sys_t::apply(bool last_batch) buf_pool_invalidate(); log_sys.latch.wr_lock(); } - else if (srv_operation == SRV_OPERATION_RESTORE || - srv_operation == SRV_OPERATION_RESTORE_EXPORT) + else if (is_mariabackup_restore_or_export()) buf_flush_sync_batch(lsn, false); else /* Instead of flushing, last_batch sorts the buf_pool.flush_list @@ -4488,6 +4483,7 @@ recv_init_missing_space(dberr_t err, const recv_spaces_t::const_iterator& i) default: break; case SRV_OPERATION_RESTORE: + case SRV_OPERATION_RESTORE_ROLLBACK_XA: case SRV_OPERATION_RESTORE_EXPORT: if (i->second.name.find("/#sql") == std::string::npos) { sql_print_warning("InnoDB: Tablespace " UINT32PF @@ -4820,8 +4816,7 @@ dberr_t recv_recovery_from_checkpoint_start() dberr_t err = DB_SUCCESS; ut_ad(srv_operation <= SRV_OPERATION_EXPORT_RESTORED - || srv_operation == SRV_OPERATION_RESTORE - || srv_operation == SRV_OPERATION_RESTORE_EXPORT); + || is_mariabackup_restore_or_export()); ut_d(mysql_mutex_lock(&buf_pool.flush_list_mutex)); ut_ad(UT_LIST_GET_LEN(buf_pool.LRU) == 0); ut_ad(UT_LIST_GET_LEN(buf_pool.unzip_LRU) == 0); @@ -4989,8 +4984,8 @@ dberr_t recv_recovery_from_checkpoint_start() } recv_sys.apply_log_recs = true; recv_no_ibuf_operations = false; - ut_d(recv_no_log_write = srv_operation == SRV_OPERATION_RESTORE - || srv_operation == SRV_OPERATION_RESTORE_EXPORT); + ut_d(recv_no_log_write = is_mariabackup_restore_or_export()); + if (srv_operation == SRV_OPERATION_NORMAL) { err = recv_rename_files(); } diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 397656fc0e59e..4c97676a2c011 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -1128,8 +1128,7 @@ dberr_t srv_start(bool create_new_db) mtr_t mtr; ut_ad(srv_operation <= SRV_OPERATION_RESTORE_EXPORT - || srv_operation == SRV_OPERATION_RESTORE - || srv_operation == SRV_OPERATION_RESTORE_EXPORT); + || is_mariabackup_restore_or_export()); if (srv_force_recovery) { ib::info() << "!!! innodb_force_recovery is set to " @@ -1289,8 +1288,7 @@ dberr_t srv_start(bool create_new_db) recv_sys.debug_free(); } else { ut_ad(srv_operation <= SRV_OPERATION_EXPORT_RESTORED - || srv_operation == SRV_OPERATION_RESTORE - || srv_operation == SRV_OPERATION_RESTORE_EXPORT); + || is_mariabackup_restore_or_export()); ut_ad(!recv_sys.recovery_on); if (srv_force_recovery >= SRV_FORCE_NO_LOG_REDO) { @@ -1451,6 +1449,7 @@ dberr_t srv_start(bool create_new_db) switch (srv_operation) { case SRV_OPERATION_NORMAL: case SRV_OPERATION_EXPORT_RESTORED: + case SRV_OPERATION_RESTORE_ROLLBACK_XA: case SRV_OPERATION_RESTORE_EXPORT: /* Initialize the change buffer. */ err = dict_boot(); @@ -1462,8 +1461,7 @@ dberr_t srv_start(bool create_new_db) /* This must precede recv_sys.apply(true). */ srv_undo_tablespaces_active = trx_rseg_get_n_undo_tablespaces(); - - if (srv_operation != SRV_OPERATION_RESTORE) { + if (!is_mariabackup_restore()) { dict_sys.load_sys_tables(); } err = trx_lists_init_at_db_start(); @@ -1508,8 +1506,7 @@ dberr_t srv_start(bool create_new_db) fil_system.space_id_reuse_warned = false; if (srv_operation > SRV_OPERATION_EXPORT_RESTORED) { - ut_ad(srv_operation == SRV_OPERATION_RESTORE_EXPORT - || srv_operation == SRV_OPERATION_RESTORE); + ut_ad(is_mariabackup_restore_or_export()); return(err); } @@ -1915,7 +1912,9 @@ void innodb_preshutdown() if (srv_read_only_mode) return; - if (!srv_fast_shutdown && srv_operation <= SRV_OPERATION_EXPORT_RESTORED) + if (!srv_fast_shutdown && (srv_operation <= SRV_OPERATION_EXPORT_RESTORED + || srv_operation + == SRV_OPERATION_RESTORE_ROLLBACK_XA)) { /* Because a slow shutdown must empty the change buffer, we had better prevent any further changes from being buffered. */ diff --git a/storage/innobase/trx/trx0rseg.cc b/storage/innobase/trx/trx0rseg.cc index 34a371c50c957..8381e11bb1ddc 100644 --- a/storage/innobase/trx/trx0rseg.cc +++ b/storage/innobase/trx/trx0rseg.cc @@ -519,7 +519,7 @@ static dberr_t trx_rseg_mem_restore(trx_rseg_t *rseg, mtr_t *mtr) #endif } - if (srv_operation == SRV_OPERATION_RESTORE) + if (is_mariabackup_restore()) /* mariabackup --prepare only deals with the redo log and the data files, not with transactions or the data dictionary. */ @@ -615,11 +615,11 @@ dberr_t trx_rseg_array_init() trx_lists_init_at_db_start() does not invoke purge_sys.create() and purge queue mutex stays uninitialized, and trx_rseg_mem_restore() quits before initializing undo log lists. */ - if (srv_operation != SRV_OPERATION_RESTORE) + if (!is_mariabackup_restore()) /* Acquiring purge queue mutex here should be fine from the deadlock prevention point of view, because executing that function is a prerequisite for starting the purge subsystem or - any transactions. */ + trx_rseg_mem_restore() quits before initializing undo log lists. */ purge_sys.queue_lock(); for (ulint rseg_id = 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) { mtr.start(); @@ -691,7 +691,7 @@ dberr_t trx_rseg_array_init() mtr.commit(); } - if (srv_operation != SRV_OPERATION_RESTORE) + if (!is_mariabackup_restore()) purge_sys.queue_unlock(); if (err != DB_SUCCESS) { for (auto& rseg : trx_sys.rseg_array) { diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 1ff3b9ac36d4a..79c558b41e9a4 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -510,8 +510,7 @@ TRANSACTIONAL_TARGET void trx_free_at_shutdown(trx_t *trx) || trx_state_eq(trx, TRX_STATE_PREPARED_RECOVERED) || (trx_state_eq(trx, TRX_STATE_ACTIVE) && (!srv_was_started - || srv_operation == SRV_OPERATION_RESTORE - || srv_operation == SRV_OPERATION_RESTORE_EXPORT + || is_mariabackup_restore_or_export() || srv_read_only_mode || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO || (!srv_is_being_started @@ -701,7 +700,7 @@ dberr_t trx_lists_init_at_db_start() ut_a(srv_is_being_started); ut_ad(!srv_was_started); - if (srv_operation == SRV_OPERATION_RESTORE) { + if (is_mariabackup_restore()) { /* mariabackup --prepare only deals with the redo log and the data files, not with transactions or the data dictionary. */