diff options
author | Thirunarayanan Balathandayuthapani <thiru@mariadb.com> | 2022-10-24 20:46:43 +0530 |
---|---|---|
committer | Thirunarayanan Balathandayuthapani <thiru@mariadb.com> | 2022-10-24 20:46:43 +0530 |
commit | 0e458a8ba2074764d558b0816bc352f703e441bd (patch) | |
tree | 5ba9a1c67aed944165c0f10ed2cfc53c99f8dffa | |
parent | af59b677ea2d07981200f556d24863a69577fe4f (diff) | |
download | mariadb-git-bb-10.11-MDEV-19229.tar.gz |
MDEV-19229 Allow innodb_undo_tablespaces to be changed after database creationbb-10.11-MDEV-19229
trx_sys_t::undo_log_nonempty: Set to true if there are undo logs
to rollback and purge.
The algorithm for re-creating the undo tablespace when
trx_sys_t::undo_log_nonempty is disabled:
1) trx_sys_t::reset_page(): Reset the TRX_SYS page and assign all
rollback segment slots from 1..127 to FIL_NULL
2) Free the rollback segment header page of system tablespace
for the slots 1..127
3) Update the binlog and WSREP information in system tablespace
rollback segment header
Step (1), (2) and Step (3) should happen atomically within a
single mini-transaction.
4) srv_undo_delete_old_tablespaces(): Delete the old undo tablespaces
present in the undo log directory
5) Make checkpoint to get rid of old undo log tablespaces redo logs
6) Assign new start space id for the undo log tablespaces
7) Re-create the specified undo log tablespaces. InnoDB uses same
mtr for this one and step (6)
8) Make checkpoint again, so that server or mariabackup
can read the undo log tablespace page0 before applying
the redo logs
srv_undo_tablespaces_reinit(): Recreate the undo log tablespaces.
It does reset trx_sys page, delete the old undo tablespaces,
update the binlog offset, write set replication checkpoint
in system rollback segment page
trx_rseg_update_binlog_offset(): Added 2 new parameters to pass
binlog file name and binlog offset
trx_rseg_array_init(): Return error if the rollback segment
slot points to non-existent tablespace
srv_undo_tablespaces_init(): Added new parameter mtr
to initialize all undo tablespaces
trx_assign_rseg_low(): Allow the transaction to use the rollback
segment slots(1..127) even if InnoDB failed to change to the
requested innodb_undo_tablespaces=0
srv_start(): Override the user specified value of
innodb_undo_tablespaces variable with already existing actual
undo tablespaces
wf_incremental_process(): Detects whether TRX_SYS page has been
modified since last backup. If it is then incremental backup
fails and throws the information about taking full backup again
xb_assign_undo_space_start(): Removed the function. Because
undo001 has first undo space id value in page0
Added test case to test the scenario during startup and mariabackup
incremental process too.
Reviewed-by : Marko Mäkelä
Tested-by : Matthias Leich
24 files changed, 757 insertions, 179 deletions
diff --git a/extra/mariabackup/write_filt.cc b/extra/mariabackup/write_filt.cc index 01e4d83e344..086d4078e31 100644 --- a/extra/mariabackup/write_filt.cc +++ b/extra/mariabackup/write_filt.cc @@ -129,6 +129,18 @@ wf_incremental_process(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile) incremental_lsn >= mach_read_from_8(page + FIL_PAGE_LSN)) continue; + /* Check whether TRX_SYS page has been changed */ + if (mach_read_from_4(page + FIL_PAGE_SPACE_ID) + == TRX_SYS_SPACE + && mach_read_from_4(page + FIL_PAGE_OFFSET) + == TRX_SYS_PAGE_NO) { + msg(cursor->thread_n, + "--incremental backup is impossible if " + "the server had been restarted with " + "different innodb_undo_tablespaces."); + return false; + } + /* updated page */ if (cp->npages == page_size / 4) { /* flush buffer */ diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 2783064cf38..ac15d98321e 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -3826,89 +3826,6 @@ next_datadir_item: return(err); } -/** Assign srv_undo_space_id_start variable if there are undo tablespace present. -Read the TRX_SYS page from ibdata1 file and get the minimum space id from -the first slot rollback segments of TRX_SYS_PAGE_NO. -@retval DB_ERROR if file open or page read failed. -@retval DB_SUCCESS if srv_undo_space_id assigned successfully. */ -static dberr_t xb_assign_undo_space_start() -{ - - pfs_os_file_t file; - bool ret; - dberr_t error = DB_SUCCESS; - uint32_t space; - uint32_t fsp_flags; - int n_retries = 5; - - if (srv_undo_tablespaces == 0) { - return error; - } - - file = os_file_create(0, srv_sys_space.first_datafile()->filepath(), - OS_FILE_OPEN, OS_FILE_NORMAL, OS_DATA_FILE, true, &ret); - - if (!ret) { - msg("Error opening %s", srv_sys_space.first_datafile()->filepath()); - return DB_ERROR; - } - - byte* page = static_cast<byte*> - (aligned_malloc(srv_page_size, srv_page_size)); - - if (os_file_read(IORequestRead, file, page, 0, srv_page_size) - != DB_SUCCESS) { - msg("Reading first page failed.\n"); - error = DB_ERROR; - goto func_exit; - } - - fsp_flags = mach_read_from_4( - page + FSP_HEADER_OFFSET + FSP_SPACE_FLAGS); -retry: - if (os_file_read(IORequestRead, file, page, - TRX_SYS_PAGE_NO << srv_page_size_shift, - srv_page_size) != DB_SUCCESS) { - msg("Reading TRX_SYS page failed."); - error = DB_ERROR; - goto func_exit; - } - - /* TRX_SYS page can't be compressed or encrypted. */ - if (buf_page_is_corrupted(false, page, fsp_flags)) { - if (n_retries--) { - std::this_thread::sleep_for( - std::chrono::milliseconds(1)); - goto retry; - } else { - msg("mariabackup: TRX_SYS page corrupted.\n"); - error = DB_ERROR; - goto func_exit; - } - } - - /* 0th slot always points to system tablespace. - 1st slot should point to first undotablespace which is minimum. */ - - ut_ad(mach_read_from_4(TRX_SYS + TRX_SYS_RSEGS - + TRX_SYS_RSEG_SLOT_SIZE - + TRX_SYS_RSEG_PAGE_NO + page) - != FIL_NULL); - - space = mach_read_from_4(TRX_SYS + TRX_SYS_RSEGS - + TRX_SYS_RSEG_SLOT_SIZE - + TRX_SYS_RSEG_SPACE + page); - - srv_undo_space_id_start = space; - -func_exit: - aligned_free(page); - ret = os_file_close(file); - ut_a(ret); - - return error; -} - /**************************************************************************** Populates the tablespace memory cache by scanning for and opening data files. @returns DB_SUCCESS or error code.*/ @@ -3946,14 +3863,7 @@ xb_load_tablespaces() } /* Add separate undo tablespaces to fil_system */ - - err = xb_assign_undo_space_start(); - - if (err != DB_SUCCESS) { - return err; - } - - err = srv_undo_tablespaces_init(false); + err = srv_undo_tablespaces_init(false, nullptr); if (err != DB_SUCCESS) { return(err); diff --git a/mysql-test/suite/innodb/include/have_undo_tablespaces.inc b/mysql-test/suite/innodb/include/have_undo_tablespaces.inc deleted file mode 100644 index 87830a4a5f0..00000000000 --- a/mysql-test/suite/innodb/include/have_undo_tablespaces.inc +++ /dev/null @@ -1,4 +0,0 @@ -if (`select count(*) = 0 from information_schema.global_variables where variable_name like 'innodb_undo_tablespaces' and variable_value >= 2`) -{ - --skip Test requires InnoDB with at-least 2 undo tablespaces. -} diff --git a/mysql-test/suite/innodb/r/log_file.result b/mysql-test/suite/innodb/r/log_file.result index 734e9b07687..f5e98d30b30 100644 --- a/mysql-test/suite/innodb/r/log_file.result +++ b/mysql-test/suite/innodb/r/log_file.result @@ -184,7 +184,7 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES WHERE engine = 'innodb' AND support IN ('YES', 'DEFAULT', 'ENABLED'); ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS -FOUND 1 /InnoDB: Expected to open innodb_undo_tablespaces=3 but was able to find only 1/ in mysqld.1.err +FOUND 1 /InnoDB: Failed to open the undo tablespace/ in mysqld.1.err bak_ib_logfile0 bak_ibdata1 bak_ibdata2 @@ -214,7 +214,7 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES WHERE engine = 'innodb' AND support IN ('YES', 'DEFAULT', 'ENABLED'); ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS -FOUND 1 /InnoDB: Expected to open innodb_undo_tablespaces=3 but was able to find only 0/ in mysqld.1.err +FOUND 2 /InnoDB: Failed to open the undo tablespace/ in mysqld.1.err bak_ib_logfile0 bak_ibdata1 bak_ibdata2 diff --git a/mysql-test/suite/innodb/r/undo_truncate.result b/mysql-test/suite/innodb/r/undo_truncate.result index 5be1e5c3426..b7729020aca 100644 --- a/mysql-test/suite/innodb/r/undo_truncate.result +++ b/mysql-test/suite/innodb/r/undo_truncate.result @@ -1,3 +1,5 @@ +SET GLOBAL innodb_fast_shutdown=0; +# restart: --innodb_undo_tablespaces=2 SET GLOBAL innodb_undo_log_truncate = 0; SET GLOBAL innodb_purge_rseg_truncate_frequency = 1; create table t1(keyc int primary key, c char(100)) engine = innodb; diff --git a/mysql-test/suite/innodb/r/undo_truncate_recover.result b/mysql-test/suite/innodb/r/undo_truncate_recover.result index 909771e6a17..4d75712060a 100644 --- a/mysql-test/suite/innodb/r/undo_truncate_recover.result +++ b/mysql-test/suite/innodb/r/undo_truncate_recover.result @@ -1,3 +1,5 @@ +SET GLOBAL innodb_fast_shutdown=0; +# restart: --innodb_undo_tablespaces=2 SET GLOBAL innodb_undo_log_truncate = 1; SET GLOBAL innodb_purge_rseg_truncate_frequency = 1; create table t1(keyc int primary key, c char(100)) engine = innodb; diff --git a/mysql-test/suite/innodb/r/undo_upgrade.result b/mysql-test/suite/innodb/r/undo_upgrade.result new file mode 100644 index 00000000000..12d6f30e1d8 --- /dev/null +++ b/mysql-test/suite/innodb/r/undo_upgrade.result @@ -0,0 +1,80 @@ +# +# MDEV-19229 Allow innodb_undo_tablespaces to be changed +# after database creation +# +call mtr.add_suppression("Found .* prepared XA transactions"); +call mtr.add_suppression("InnoDB: Plugin initialization aborted"); +call mtr.add_suppression("Plugin 'InnoDB' init function returned error"); +call mtr.add_suppression("Plugin 'InnoDB' registration as a STORAGE ENGINE failed"); +call mtr.add_suppression("InnoDB: Cannot change innodb_undo_tablespaces=\\d+ because previous shutdown was not with innodb_fast_shutdown=0"); +CREATE TABLE t1(f1 INT NOT NULL)ENGINE=InnoDB; +connect con_purge,localhost,root,,,; +START TRANSACTION WITH CONSISTENT SNAPSHOT; +connection default; +INSERT INTO t1 VALUES(1); +UPDATE t1 SET f1=100; +# case 1: Undo log left to purge +# restart: --innodb_undo_tablespaces=2 +# Display 4 undo tablespaces +select @@global.innodb_undo_tablespaces; +@@global.innodb_undo_tablespaces +4 +# Should list 4 undo log tablespaces +undo001 +undo002 +undo003 +undo004 +# case 2: XA transaction alone left +InnoDB 0 transactions not purged +XA START 'zombie'; +INSERT INTO t1 VALUES(2); +XA END 'zombie'; +XA PREPARE 'zombie'; +# restart: --innodb_undo_tablespaces=2 +# Display 4 undo tablespaces +select @@global.innodb_undo_tablespaces; +@@global.innodb_undo_tablespaces +4 +# Should list 4 undo log tablespaces +undo001 +undo002 +undo003 +undo004 +XA COMMIT 'zombie'; +# case 3: Successful innodb_undo_tablespace upgrade +SET GLOBAL innodb_fast_shutdown=0; +# restart: --innodb_undo_tablespaces=2 +# Display 2 undo tablespaces +SELECT @@global.innodb_undo_tablespaces; +@@global.innodb_undo_tablespaces +2 +# Should list 2 undo log tablespaces +undo001 +undo002 +DROP TABLE t1; +InnoDB 0 transactions not purged +# case 4: Reduce the innodb_undo_tablespace to 0 +# restart: --innodb_undo_tablespaces=0 +# Display 0 undo tablespace +SELECT @@global.innodb_undo_tablespaces; +@@global.innodb_undo_tablespaces +0 +# Shouldn't list any undo log tablespaces +# case 5: Change undo tablespace when force_recovery < 5 +# restart: --innodb_undo_tablespaces=2 --innodb_force_recovery=4 +# Display 2 undo tablespace +SELECT @@global.innodb_undo_tablespaces; +@@global.innodb_undo_tablespaces +2 +# Should list 2 undo log tablespaces +undo001 +undo002 +# case 6: Fail to change undo tablespace when force_recovery > 4 +# restart: --innodb_undo_tablespaces=4 --innodb_force_recovery=5 +# Display 2 undo tablespace +SELECT @@global.innodb_undo_tablespaces; +@@global.innodb_undo_tablespaces +2 +# Should list 2 undo log tablespaces, not 4 +undo001 +undo002 diff --git a/mysql-test/suite/innodb/r/undo_upgrade_debug.result b/mysql-test/suite/innodb/r/undo_upgrade_debug.result new file mode 100644 index 00000000000..9c464459b98 --- /dev/null +++ b/mysql-test/suite/innodb/r/undo_upgrade_debug.result @@ -0,0 +1,43 @@ +# +# MDEV-19229 Allow innodb_undo_tablespaces to be changed +# after database creation +# +call mtr.add_suppression("InnoDB: Plugin initialization aborted"); +call mtr.add_suppression("Plugin 'InnoDB' init function returned error"); +call mtr.add_suppression("Plugin 'InnoDB' registration as a STORAGE ENGINE failed"); +set global innodb_fast_shutdown=0; +# case 1: Abort after resetting TRX_SYS page rollback segments +# restart: --innodb_undo_tablespaces=4 --debug_dbug=+d,after_rseg_reset_abort +# restart: --innodb_undo_tablespaces=4 +# Should list 4 undo log tablespaces +undo001 +undo002 +undo003 +undo004 +# case 2: Abort after deleting the old undo tablespaces +# restart: --innodb_undo_tablespaces=2 --debug_dbug=+d,after_deleting_old_undo_abort +# restart: --innodb_undo_tablespaces=2 +# Should list 2 undo log tablespaces +undo001 +undo002 +# case 3: Abort after successfully deleting the old undo tablespace +# restart: --innodb_undo_tablespaces=3 --debug_dbug=+d,after_deleting_old_undo_success +# restart: --innodb_undo_tablespaces=3 +# Should list 3 undo log tablespaces +undo001 +undo002 +undo003 +# case 4: Abort after re-creating new undo tablespaces +# restart: --innodb_undo_tablespaces=4 --debug_dbug=+d,after_reinit_undo_abort +# restart: --innodb_undo_tablespaces=4 +# Should list 4 undo log tablespaces +undo001 +undo002 +undo003 +undo004 +# case 5: Abort after re-creating new undo tablespaces successfully +# restart: --innodb_undo_tablespaces=2 --debug_dbug=+d,after_reinit_undo_success +# restart: --innodb_undo_tablespaces=2 +# Should list 2 undo log tablespaces +undo001 +undo002 diff --git a/mysql-test/suite/innodb/t/log_file.test b/mysql-test/suite/innodb/t/log_file.test index 438fbe1a6e1..805a4b4cd56 100644 --- a/mysql-test/suite/innodb/t/log_file.test +++ b/mysql-test/suite/innodb/t/log_file.test @@ -164,7 +164,7 @@ let SEARCH_PATTERN=undo tablespace .*undo003.* exists\. Creating system tablespa --source include/start_mysqld.inc eval $check_no_innodb; --source include/shutdown_mysqld.inc -let SEARCH_PATTERN=InnoDB: Expected to open innodb_undo_tablespaces=3 but was able to find only 1; +let SEARCH_PATTERN=InnoDB: Failed to open the undo tablespace; --source include/search_pattern_in_file.inc # clean up & Restore --source ../include/log_file_cleanup.inc @@ -176,7 +176,7 @@ let SEARCH_PATTERN=InnoDB: Expected to open innodb_undo_tablespaces=3 but was ab --source include/start_mysqld.inc eval $check_no_innodb; --source include/shutdown_mysqld.inc -let SEARCH_PATTERN=InnoDB: Expected to open innodb_undo_tablespaces=3 but was able to find only 0; +let SEARCH_PATTERN=InnoDB: Failed to open the undo tablespace; --source include/search_pattern_in_file.inc # clean up & Restore diff --git a/mysql-test/suite/innodb/t/undo_truncate.test b/mysql-test/suite/innodb/t/undo_truncate.test index 19829ce21e7..a5e0a52f798 100644 --- a/mysql-test/suite/innodb/t/undo_truncate.test +++ b/mysql-test/suite/innodb/t/undo_truncate.test @@ -1,6 +1,5 @@ --source include/have_innodb.inc --source include/innodb_page_size.inc ---source include/have_undo_tablespaces.inc --source include/not_embedded.inc --source include/have_sequence.inc --source include/no_valgrind_without_big.inc @@ -12,6 +11,11 @@ call mtr.add_suppression("InnoDB: Difficult to find free blocks in the buffer po call mtr.add_suppression("InnoDB: Trying to delete tablespace.*pending operations"); --enable_query_log +# Re-create the undo log tablespaces after slow shutdown +SET GLOBAL innodb_fast_shutdown=0; +let $restart_parameters="--innodb_undo_tablespaces=2"; +--source include/restart_mysqld.inc + SET GLOBAL innodb_undo_log_truncate = 0; SET GLOBAL innodb_purge_rseg_truncate_frequency = 1; @@ -53,6 +57,7 @@ let $trx_before= `select substr('$trx_before',9)+2`; SET GLOBAL innodb_purge_rseg_truncate_frequency=1; SET GLOBAL innodb_max_purge_lag_wait=0; set global innodb_fast_shutdown=0; +let $restart_parameters=; --source include/restart_mysqld.inc --replace_regex /.*Trx id counter ([0-9]+).*/\1/ let $trx_after= `SHOW ENGINE INNODB STATUS`; diff --git a/mysql-test/suite/innodb/t/undo_truncate_recover.test b/mysql-test/suite/innodb/t/undo_truncate_recover.test index e499ff3dfbe..1b27b0fe6e4 100644 --- a/mysql-test/suite/innodb/t/undo_truncate_recover.test +++ b/mysql-test/suite/innodb/t/undo_truncate_recover.test @@ -8,10 +8,14 @@ --source include/innodb_page_size_small.inc --source include/have_innodb.inc --source include/have_debug.inc ---source include/have_undo_tablespaces.inc # Tests with embedded server do not support restarting --source include/not_embedded.inc +# Re-create the undo log tablespaces after slow shutdown +SET GLOBAL innodb_fast_shutdown=0; +let $restart_parameters="--innodb_undo_tablespaces=2"; +--source include/restart_mysqld.inc + SET GLOBAL innodb_undo_log_truncate = 1; SET GLOBAL innodb_purge_rseg_truncate_frequency = 1; diff --git a/mysql-test/suite/innodb/t/undo_upgrade.opt b/mysql-test/suite/innodb/t/undo_upgrade.opt new file mode 100644 index 00000000000..87bc4c4d7f7 --- /dev/null +++ b/mysql-test/suite/innodb/t/undo_upgrade.opt @@ -0,0 +1,2 @@ +--log-bin=1 +--innodb_undo_tablespaces=4 diff --git a/mysql-test/suite/innodb/t/undo_upgrade.test b/mysql-test/suite/innodb/t/undo_upgrade.test new file mode 100644 index 00000000000..952d8b72b69 --- /dev/null +++ b/mysql-test/suite/innodb/t/undo_upgrade.test @@ -0,0 +1,94 @@ +--source include/have_innodb.inc +--source include/innodb_page_size.inc +--source include/not_embedded.inc + +--echo # +--echo # MDEV-19229 Allow innodb_undo_tablespaces to be changed +--echo # after database creation +--echo # +call mtr.add_suppression("Found .* prepared XA transactions"); +call mtr.add_suppression("InnoDB: Plugin initialization aborted"); +call mtr.add_suppression("Plugin 'InnoDB' init function returned error"); +call mtr.add_suppression("Plugin 'InnoDB' registration as a STORAGE ENGINE failed"); +call mtr.add_suppression("InnoDB: Cannot change innodb_undo_tablespaces=\\d+ because previous shutdown was not with innodb_fast_shutdown=0"); + +let $MYSQLD_DATADIR= `select @@datadir`; + +CREATE TABLE t1(f1 INT NOT NULL)ENGINE=InnoDB; +connect(con_purge,localhost,root,,,); +START TRANSACTION WITH CONSISTENT SNAPSHOT; + +connection default; +INSERT INTO t1 VALUES(1); +UPDATE t1 SET f1=100; + +let $restart_parameters=--innodb_undo_tablespaces=2; +--echo # case 1: Undo log left to purge +--source include/restart_mysqld.inc +--echo # Display 4 undo tablespaces +select @@global.innodb_undo_tablespaces; + +--echo # Should list 4 undo log tablespaces +list_files $MYSQLD_DATADIR undo*; + +--echo # case 2: XA transaction alone left +--source include/wait_all_purged.inc +XA START 'zombie'; +INSERT INTO t1 VALUES(2); +XA END 'zombie'; +XA PREPARE 'zombie'; + +--source include/restart_mysqld.inc + +--echo # Display 4 undo tablespaces +select @@global.innodb_undo_tablespaces; + +--echo # Should list 4 undo log tablespaces +list_files $MYSQLD_DATADIR undo*; + +XA COMMIT 'zombie'; + +--echo # case 3: Successful innodb_undo_tablespace upgrade +SET GLOBAL innodb_fast_shutdown=0; + +let $restart_parameters=--innodb_undo_tablespaces=2; +--source include/restart_mysqld.inc + +--echo # Display 2 undo tablespaces +SELECT @@global.innodb_undo_tablespaces; + +--echo # Should list 2 undo log tablespaces +list_files $MYSQLD_DATADIR undo*; + +DROP TABLE t1; +--source include/wait_all_purged.inc + +--echo # case 4: Reduce the innodb_undo_tablespace to 0 +let $restart_parameters=--innodb_undo_tablespaces=0; +--source include/restart_mysqld.inc + +--echo # Display 0 undo tablespace +SELECT @@global.innodb_undo_tablespaces; + +--echo # Shouldn't list any undo log tablespaces +list_files $MYSQLD_DATADIR undo*; + +--echo # case 5: Change undo tablespace when force_recovery < 5 +let $restart_parameters=--innodb_undo_tablespaces=2 --innodb_force_recovery=4; +--source include/restart_mysqld.inc + +--echo # Display 2 undo tablespace +SELECT @@global.innodb_undo_tablespaces; + +--echo # Should list 2 undo log tablespaces +list_files $MYSQLD_DATADIR undo*; + +--echo # case 6: Fail to change undo tablespace when force_recovery > 4 +let $restart_parameters=--innodb_undo_tablespaces=4 --innodb_force_recovery=5; +--source include/restart_mysqld.inc + +--echo # Display 2 undo tablespace +SELECT @@global.innodb_undo_tablespaces; + +--echo # Should list 2 undo log tablespaces, not 4 +list_files $MYSQLD_DATADIR undo*; diff --git a/mysql-test/suite/innodb/t/undo_upgrade_debug.test b/mysql-test/suite/innodb/t/undo_upgrade_debug.test new file mode 100644 index 00000000000..e274d6c020d --- /dev/null +++ b/mysql-test/suite/innodb/t/undo_upgrade_debug.test @@ -0,0 +1,70 @@ +--source include/have_innodb.inc +--source include/innodb_page_size.inc +--source include/have_debug.inc +--source include/not_embedded.inc + +--echo # +--echo # MDEV-19229 Allow innodb_undo_tablespaces to be changed +--echo # after database creation +--echo # +let $MYSQLD_DATADIR= `select @@datadir`; +call mtr.add_suppression("InnoDB: Plugin initialization aborted"); +call mtr.add_suppression("Plugin 'InnoDB' init function returned error"); +call mtr.add_suppression("Plugin 'InnoDB' registration as a STORAGE ENGINE failed"); + +set global innodb_fast_shutdown=0; + +--echo # case 1: Abort after resetting TRX_SYS page rollback segments +let $restart_parameters=--innodb_undo_tablespaces=4 --debug_dbug="+d,after_rseg_reset_abort"; + +--source include/restart_mysqld.inc + +let $restart_parameters=--innodb_undo_tablespaces=4; +--source include/restart_mysqld.inc + +--echo # Should list 4 undo log tablespaces +list_files $MYSQLD_DATADIR undo*; + +--echo # case 2: Abort after deleting the old undo tablespaces +let $restart_parameters=--innodb_undo_tablespaces=2 --debug_dbug="+d,after_deleting_old_undo_abort"; + +--source include/restart_mysqld.inc + +let $restart_parameters=--innodb_undo_tablespaces=2; +--source include/restart_mysqld.inc + +--echo # Should list 2 undo log tablespaces +list_files $MYSQLD_DATADIR undo*; + +--echo # case 3: Abort after successfully deleting the old undo tablespace +let $restart_parameters=--innodb_undo_tablespaces=3 --debug_dbug="+d,after_deleting_old_undo_success"; + +--source include/restart_mysqld.inc + +let $restart_parameters=--innodb_undo_tablespaces=3; +--source include/restart_mysqld.inc + +--echo # Should list 3 undo log tablespaces +list_files $MYSQLD_DATADIR undo*; + +--echo # case 4: Abort after re-creating new undo tablespaces +let $restart_parameters=--innodb_undo_tablespaces=4 --debug_dbug="+d,after_reinit_undo_abort"; + +--source include/restart_mysqld.inc + +let $restart_parameters=--innodb_undo_tablespaces=4; +--source include/restart_mysqld.inc + +--echo # Should list 4 undo log tablespaces +list_files $MYSQLD_DATADIR undo*; + +--echo # case 5: Abort after re-creating new undo tablespaces successfully +let $restart_parameters=--innodb_undo_tablespaces=2 --debug_dbug="+d,after_reinit_undo_success"; + +--source include/restart_mysqld.inc + +let $restart_parameters=--innodb_undo_tablespaces=2; +--source include/restart_mysqld.inc + +--echo # Should list 2 undo log tablespaces +list_files $MYSQLD_DATADIR undo*; diff --git a/mysql-test/suite/mariabackup/undo_upgrade.result b/mysql-test/suite/mariabackup/undo_upgrade.result new file mode 100644 index 00000000000..7b77e9ff6e6 --- /dev/null +++ b/mysql-test/suite/mariabackup/undo_upgrade.result @@ -0,0 +1,19 @@ +set global innodb_fast_shutdown=0; +# restart: --innodb_undo_tablespaces=2 +CREATE TABLE t1(a varchar(60)) ENGINE INNODB; +start transaction; +INSERT INTO t1 VALUES(1); +# xtrabackup backup +# Restart the server with 4 undo tablespaces +set global innodb_fast_shutdown=0; +# restart: --innodb_undo_tablespaces=4 +# incremental backup should fail +FOUND 1 /--incremental backup is impossible if the server had been restarted with different innodb_undo_tablespaces/ in backup.log +# Take full backup again +# Prepare full backup +# Display 4 undo log files from target directory +undo001 +undo002 +undo003 +undo004 +DROP TABLE t1; diff --git a/mysql-test/suite/mariabackup/undo_upgrade.test b/mysql-test/suite/mariabackup/undo_upgrade.test new file mode 100644 index 00000000000..3d599269782 --- /dev/null +++ b/mysql-test/suite/mariabackup/undo_upgrade.test @@ -0,0 +1,50 @@ +--source include/have_innodb.inc +--source include/innodb_page_size.inc + +let basedir=$MYSQLTEST_VARDIR/tmp/backup; +let incremental_dir=$MYSQLTEST_VARDIR/tmp/backup_inc1; + +set global innodb_fast_shutdown=0; +let $restart_parameters=--innodb_undo_tablespaces=2; +--source include/restart_mysqld.inc + +CREATE TABLE t1(a varchar(60)) ENGINE INNODB; +start transaction; +INSERT INTO t1 VALUES(1); + +--echo # xtrabackup backup +--disable_result_log +exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$basedir; +--enable_result_log + +--echo # Restart the server with 4 undo tablespaces +let $restart_parameters=--innodb_undo_tablespaces=4; +set global innodb_fast_shutdown=0; +--source include/restart_mysqld.inc + +let $backuplog=$MYSQLTEST_VARDIR/tmp/backup.log; +--echo # incremental backup should fail +--error 1 +exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$incremental_dir --incremental-basedir=$basedir 2> $backuplog; + +--let SEARCH_PATTERN=--incremental backup is impossible if the server had been restarted with different innodb_undo_tablespaces +--let SEARCH_FILE=$backuplog +--source include/search_pattern_in_file.inc +remove_file $backuplog; +rmdir $incremental_dir; +rmdir $basedir; + +--echo # Take full backup again +--disable_result_log +exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$basedir; +--enable_result_log +--disable_result_log + +echo # Prepare full backup; +exec $XTRABACKUP --prepare --target-dir=$basedir; + +--echo # Display 4 undo log files from target directory +list_files $basedir undo*; + +DROP TABLE t1; +rmdir $basedir; diff --git a/storage/innobase/include/srv0start.h b/storage/innobase/include/srv0start.h index 44b19aa666b..c18cf1ceb63 100644 --- a/storage/innobase/include/srv0start.h +++ b/storage/innobase/include/srv0start.h @@ -33,10 +33,10 @@ Created 10/10/1995 Heikki Tuuri struct dict_table_t; /** Open the configured number of dedicated undo tablespaces. -@param[in] create_new_db whether the database is being initialized +@param[in] create_new_undo whether the undo tablespaces has to be created +@param[in,out] mtr mini-transaction @return DB_SUCCESS or error code */ -dberr_t -srv_undo_tablespaces_init(bool create_new_db); +dberr_t srv_undo_tablespaces_init(bool create_new_undo, mtr_t *mtr); /** Start InnoDB. @param[in] create_new_db whether to create a new database diff --git a/storage/innobase/include/trx0rseg.h b/storage/innobase/include/trx0rseg.h index 7ad20b0fff0..6ea4b8cb1cc 100644 --- a/storage/innobase/include/trx0rseg.h +++ b/storage/innobase/include/trx0rseg.h @@ -306,7 +306,10 @@ which corresponds to the transaction just being committed. In a replication slave, this updates the master binlog position up to which replication has proceeded. @param[in,out] rseg_header rollback segment header -@param[in] trx committing transaction +@param[in] log_file_name binlog file name +@param[in] log_offset binlog offset value @param[in,out] mtr mini-transaction */ -void trx_rseg_update_binlog_offset(buf_block_t *rseg_header, const trx_t *trx, +void trx_rseg_update_binlog_offset(buf_block_t *rseg_header, + const char *log_file_name, + ulonglong log_offset, mtr_t *mtr); diff --git a/storage/innobase/include/trx0sys.h b/storage/innobase/include/trx0sys.h index 4d231077b12..2ed9566c215 100644 --- a/storage/innobase/include/trx0sys.h +++ b/storage/innobase/include/trx0sys.h @@ -855,6 +855,8 @@ class trx_sys_t bool m_initialised; + /** False if there is no undo log to purge or rollback */ + bool undo_log_nonempty; public: /** List of all transactions. */ thread_safe_trx_ilist_t trx_list; @@ -1165,6 +1167,20 @@ public: return count; } + /** Set the undo log empty value */ + void set_undo_non_empty(bool val) + { + if (!undo_log_nonempty) + undo_log_nonempty= val; + } + + /** Get the undo log empty value */ + bool is_undo_empty() const { return !undo_log_nonempty; } + + /* Reset the trx_sys page and retain the dblwr information, + system rollback segment header page + @return error code */ + inline dberr_t reset_page(mtr_t *mtr); private: static my_bool find_same_or_older_callback(rw_trx_hash_element_t *element, trx_id_t *id) diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 4954a07c64e..fbe370bbfd9 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -326,11 +326,246 @@ static dberr_t srv_undo_tablespace_create(const char* name) return(err); } -/* Validate the number of undo opened undo tablespace and user given -undo tablespace +inline dberr_t trx_sys_t::reset_page(mtr_t *mtr) +{ + dberr_t err= DB_SUCCESS; + buf_block_t *sys_header= buf_page_get_gen( + page_id_t(TRX_SYS_SPACE, TRX_SYS_PAGE_NO), 0, RW_X_LATCH, nullptr, + BUF_GET, mtr, &err); + + if (!sys_header) return err; + + const bool dblwr_enabled= + mach_read_from_4(TRX_SYS_DOUBLEWRITE_MAGIC + TRX_SYS_DOUBLEWRITE + + sys_header->page.frame) + == TRX_SYS_DOUBLEWRITE_MAGIC_N; + + char doublewrite[TRX_SYS_DOUBLEWRITE_BLOCK2 + 4]; + memcpy(doublewrite, TRX_SYS_DOUBLEWRITE + sys_header->page.frame, + sizeof doublewrite); + + fsp_init_file_page(fil_system.sys_space, sys_header, mtr); + + mtr->write<2>(*sys_header, FIL_PAGE_TYPE + sys_header->page.frame, + FIL_PAGE_TYPE_TRX_SYS); + + mtr->write<4>(*sys_header, + TRX_SYS + TRX_SYS_RSEGS + TRX_SYS_RSEG_PAGE_NO + + sys_header->page.frame, FSP_FIRST_RSEG_PAGE_NO); + mtr->memset(sys_header, + TRX_SYS + TRX_SYS_RSEGS + TRX_SYS_RSEG_SLOT_SIZE, + 254 * TRX_SYS_RSEG_SLOT_SIZE, 0xff); + + static_assert(TRX_SYS_RSEG_SLOT_SIZE == 8, ""); + + if (dblwr_enabled) + { + mtr->memcpy( + *sys_header, sys_header->page.frame + TRX_SYS_DOUBLEWRITE, + doublewrite, sizeof doublewrite); + mtr->memmove( + *sys_header, + TRX_SYS_DOUBLEWRITE + FSEG_HEADER_SIZE + TRX_SYS_DOUBLEWRITE_REPEAT, + TRX_SYS_DOUBLEWRITE + FSEG_HEADER_SIZE, 12); + memcpy( + sys_header->page.frame + TRX_SYS_DOUBLEWRITE + + FSEG_HEADER_SIZE + TRX_SYS_DOUBLEWRITE_REPEAT, + sys_header->page.frame + TRX_SYS_DOUBLEWRITE + FSEG_HEADER_SIZE, 12); + } + + return DB_SUCCESS; +} + +/** Delete the old undo tablespaces present in the undo log directory */ +static dberr_t srv_undo_delete_old_tablespaces() +{ + /* Delete the old undo tablespaces*/ + for (uint32_t i= 0; i < srv_undo_tablespaces_open; ++i) + fil_close_tablespace(srv_undo_space_id_start + i); + + DBUG_EXECUTE_IF("after_deleting_old_undo_abort", return DB_ERROR;); + + /* Do checkpoint to get rid of old undo log tablespaces redo logs */ + log_make_checkpoint(); + + DBUG_EXECUTE_IF("after_deleting_old_undo_success", return DB_ERROR;); + + for (uint32_t i= 0; i < srv_undo_tablespaces_open; ++i) + { + char name[OS_FILE_MAX_PATH]; + snprintf(name, sizeof name, "%s/undo%03" PRIu32, srv_undo_dir, i + 1); + os_file_delete_if_exists(innodb_data_file_key, name, nullptr); + } + + return DB_SUCCESS; +} + +/** Recreate the undo log tablespaces */ +static dberr_t srv_undo_tablespaces_reinit() +{ + mtr_t mtr; + dberr_t err; + buf_block_t *first_rseg_hdr; + uint32_t latest_space_id; + + mtr.start(); + + buf_block_t *dict_hdr= buf_page_get_gen( + page_id_t(DICT_HDR_SPACE, DICT_HDR_PAGE_NO), 0, RW_X_LATCH, + nullptr, BUF_GET, &mtr, &err); + + if (!dict_hdr) + goto func_exit; + + /* Assign the new space id for the first undo tablespace */ + latest_space_id= mach_read_from_4( + DICT_HDR + DICT_HDR_MAX_SPACE_ID + dict_hdr->page.frame); + + if (latest_space_id + srv_undo_tablespaces > SRV_SPACE_ID_UPPER_BOUND) + { + err= DB_ERROR; + sql_print_error("InnoDB: Running out of tablespace id"); + goto func_exit; + } + + first_rseg_hdr= + buf_page_get_gen(trx_sys.rseg_array[0].page_id(), 0, RW_X_LATCH, + nullptr, BUF_GET, &mtr, &err); + if (!first_rseg_hdr) + goto func_exit; + + /* Reset TRX_SYS page */ + err= trx_sys.reset_page(&mtr); + + if (err) + goto func_exit; + + if (srv_undo_tablespaces_open == 0) + { + /* Free the system rollback segment */ + for (ulint i= 1; i < TRX_SYS_N_RSEGS; i++) + { + trx_rseg_t *rseg= &trx_sys.rseg_array[i]; + if (rseg->space != fil_system.sys_space) + continue; + buf_block_t *block= buf_page_get_gen( + rseg->page_id(), 0, RW_X_LATCH, nullptr, BUF_GET, &mtr); + if (!block) break; + while (!fseg_free_step(TRX_RSEG + TRX_RSEG_FSEG_HEADER + + block->page.frame, &mtr)); + } + } + + for (ulint rseg_id= 1; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) + { + trx_rseg_t *rseg= &trx_sys.rseg_array[rseg_id]; + rseg->destroy(); + rseg->init(nullptr, FIL_NULL); + } + + if (trx_sys.recovered_binlog_lsn +#ifdef WITH_WSREP + || !trx_sys.recovered_wsrep_xid.is_null() +#endif /* WITH_WSREP */ + ) + { + /* Update binlog offset, binlog file name & wsrep xid in + system tablespace rollback segment */ + if (trx_sys.recovered_binlog_lsn) + { + ut_d(const size_t len = strlen(trx_sys.recovered_binlog_filename) + 1); + ut_ad(len > 1); + ut_ad(len <= TRX_RSEG_BINLOG_NAME_LEN); + trx_rseg_update_binlog_offset( + first_rseg_hdr, trx_sys.recovered_binlog_filename, + trx_sys.recovered_binlog_offset, &mtr); + } + +#ifdef WITH_WSREP + if (!trx_sys.recovered_wsrep_xid.is_null()) + trx_rseg_update_wsrep_checkpoint( + first_rseg_hdr, &trx_sys.recovered_wsrep_xid, &mtr); +#endif /* WITH_WSREP */ + } + + dict_hdr->page.fix(); + + mtr.commit(); + + DBUG_EXECUTE_IF("after_rseg_reset_abort", + log_write_up_to(mtr.commit_lsn(), true); + dict_hdr->page.unfix(); + return DB_ERROR;); + + sql_print_information( + "InnoDB: Reinitializing innodb_undo_tablespaces= %u from %u", + srv_undo_tablespaces, srv_undo_tablespaces_open); + + /* Delete the old undo tablespaces */ + err= srv_undo_delete_old_tablespaces(); + if (err) + { + dict_hdr->page.unfix(); + return err; + } + + mtr.start(); + + dict_hdr->page.lock.x_lock(); + mtr.memo_push(dict_hdr, MTR_MEMO_PAGE_X_FIX); + + if (srv_undo_tablespaces == 0) + { + srv_undo_space_id_start= 0; + srv_undo_tablespaces_open= 0; + goto func_exit; + } + + srv_undo_space_id_start= latest_space_id; + if (fil_assign_new_space_id(&srv_undo_space_id_start)) + mtr.write<4>(*dict_hdr, DICT_HDR + DICT_HDR_MAX_SPACE_ID + + dict_hdr->page.frame, srv_undo_space_id_start); + + /* Re-create the new undo tablespaces */ + err= srv_undo_tablespaces_init(true, &mtr); +func_exit: + mtr.commit(); + + DBUG_EXECUTE_IF("after_reinit_undo_abort", + log_write_up_to(mtr.commit_lsn(), true); + err= DB_ERROR;); + + if (err == DB_SUCCESS) + { + /* Usually, recovery must work no matter when + log_checkpoints are triggered. This is a special case, + because this code is executed as part of InnoDB startup. + Backup requires that the server has been started up, + backup should never observe the log records that + were written in mtr and also srv_undo_tablespaces_init() + initializes the undo tablespace start id based on page0 + content before reading the redo log */ + log_make_checkpoint(); + + DBUG_EXECUTE_IF("after_reinit_undo_success", err= DB_ERROR;); + srv_undo_tablespaces_active= srv_undo_tablespaces; + } + return err; +} + +/** Reinitialize the undo tablespaces when there is no undo log +left to purge/rollback and validate the number of undo opened +undo tablespace and user given undo tablespace @return DB_SUCCESS if it is valid */ -static dberr_t srv_validate_undo_tablespaces() +static dberr_t srv_undo_tablespaces_reinitialize() { + + /* Re-create the undo tablespaces if it has no undo logs + left to purge/rollback */ + if (srv_undo_tablespaces != srv_undo_tablespaces_open && + trx_sys.is_undo_empty()) + return srv_undo_tablespaces_reinit(); + /* If the user says that there are fewer than what we find we tolerate that discrepancy but not the inverse. Because there could be unused undo tablespaces for future use. */ @@ -344,15 +579,14 @@ static dberr_t srv_validate_undo_tablespaces() return DB_ERROR; } + else if (srv_undo_tablespaces < srv_undo_tablespaces_open) + sql_print_warning("InnoDB: Cannot change innodb_undo_tablespaces=%u " + "because previous shutdown was not with " + "innodb_fast_shutdown=0", srv_undo_tablespaces); else if (srv_undo_tablespaces_open > 0) - { - ib::info() << "Opened " << srv_undo_tablespaces_open - << " undo tablespaces"; + sql_print_information("InnoDB: Opened " UINT32PF " undo tablespaces", + srv_undo_tablespaces_open); - if (srv_undo_tablespaces == 0) - ib::warn() << "innodb_undo_tablespaces=0 disables" - " dedicated undo log tablespaces"; - } return DB_SUCCESS; } @@ -539,25 +773,25 @@ srv_check_undo_redo_logs_exists() return(DB_SUCCESS); } -static dberr_t srv_all_undo_tablespaces_open(bool create_new_db, - uint32_t n_undo) +static dberr_t srv_all_undo_tablespaces_open(bool create_new_undo, + uint32_t n_undo) { /* Open all the undo tablespaces that are currently in use. If we fail to open any of these it is a fatal error. The tablespace ids should be contiguous. It is a fatal error because they are required for recovery and are referenced by the UNDO logs (a.k.a RBS). */ - uint32_t prev_id= create_new_db ? srv_undo_space_id_start - 1 : 0; + uint32_t prev_id= create_new_undo ? srv_undo_space_id_start - 1 : 0; for (uint32_t i= 0; i < n_undo; ++i) { char name[OS_FILE_MAX_PATH]; snprintf(name, sizeof name, "%s/undo%03u", srv_undo_dir, i + 1); - uint32_t space_id= srv_undo_tablespace_open(create_new_db, name, i); + uint32_t space_id= srv_undo_tablespace_open(create_new_undo, name, i); if (!space_id) { - if (!create_new_db) - break; + if (!create_new_undo) + break; ib::error() << "Unable to open create tablespace '" << name << "'."; return DB_ERROR; } @@ -583,23 +817,25 @@ static dberr_t srv_all_undo_tablespaces_open(bool create_new_db, { char name[OS_FILE_MAX_PATH]; snprintf(name, sizeof name, "%s/undo%03u", srv_undo_dir, i); - if (!srv_undo_tablespace_open(create_new_db, name, i)) + if (!srv_undo_tablespace_open(create_new_undo, name, i)) break; ++srv_undo_tablespaces_open; } - return srv_validate_undo_tablespaces(); + return DB_SUCCESS; } /** Open the configured number of dedicated undo tablespaces. -@param[in] create_new_db whether the database is being initialized +@param[in] create_new_undo whether the undo tablespaces has to be created +@param[in,out] mtr mini-transaction @return DB_SUCCESS or error code */ -dberr_t srv_undo_tablespaces_init(bool create_new_db) +dberr_t srv_undo_tablespaces_init(bool create_new_undo, mtr_t *mtr) { srv_undo_tablespaces_open= 0; + ut_ad(!create_new_undo || mtr); ut_a(srv_undo_tablespaces <= TRX_SYS_N_RSEGS); - ut_a(!create_new_db || srv_operation == SRV_OPERATION_NORMAL); + ut_a(!create_new_undo || srv_operation == SRV_OPERATION_NORMAL); if (srv_undo_tablespaces == 1) srv_undo_tablespaces= 0; @@ -607,9 +843,8 @@ dberr_t srv_undo_tablespaces_init(bool create_new_db) /* Create the undo spaces only if we are creating a new instance. We don't allow creating of new undo tablespaces in an existing instance (yet). */ - if (create_new_db) + if (create_new_undo) { - srv_undo_space_id_start= 1; DBUG_EXECUTE_IF("innodb_undo_upgrade", srv_undo_space_id_start= 3;); for (ulint i= 0; i < srv_undo_tablespaces; ++i) @@ -630,11 +865,11 @@ dberr_t srv_undo_tablespaces_init(bool create_new_db) already exist. */ srv_undo_tablespaces_active= srv_undo_tablespaces; - uint32_t n_undo= (create_new_db || srv_operation == SRV_OPERATION_BACKUP || - srv_operation == SRV_OPERATION_RESTORE_DELTA) + uint32_t n_undo= (create_new_undo || srv_operation == SRV_OPERATION_BACKUP || + srv_operation == SRV_OPERATION_RESTORE_DELTA) ? srv_undo_tablespaces : TRX_SYS_N_RSEGS; - if (dberr_t err= srv_all_undo_tablespaces_open(create_new_db, n_undo)) + if (dberr_t err= srv_all_undo_tablespaces_open(create_new_undo, n_undo)) return err; /* Initialize srv_undo_space_id_start=0 when there are no @@ -642,17 +877,13 @@ dberr_t srv_undo_tablespaces_init(bool create_new_db) if (srv_undo_tablespaces_open == 0) srv_undo_space_id_start= 0; - if (create_new_db) + if (create_new_undo) { - mtr_t mtr; for (uint32_t i= 0; i < srv_undo_tablespaces; ++i) { - mtr.start(); dberr_t err= fsp_header_init(fil_space_get(srv_undo_space_id_start + i), - SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, &mtr); - mtr.commit(); - if (err) - return err; + SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, mtr); + if (err) return err; } } @@ -1121,15 +1352,23 @@ dberr_t srv_start(bool create_new_db) } return srv_init_abort(err); } + + srv_undo_space_id_start= 1; } /* Open log file and data files in the systemtablespace: we keep them open until database shutdown */ ut_d(fil_system.sys_space->recv_size = srv_sys_space_size_debug); - err = fil_system.sys_space->open(create_new_db) - ? srv_undo_tablespaces_init(create_new_db) - : DB_ERROR; + if (fil_system.sys_space->open(create_new_db)) { + mtr_t mtr; + mtr.start(); + err= srv_undo_tablespaces_init(create_new_db, &mtr); + mtr.commit(); + } + else { + err= DB_ERROR; + } /* If the force recovery is set very high then we carry on regardless of all errors. Basically this is fingers crossed mode. */ @@ -1226,10 +1465,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(); - err = srv_validate_undo_tablespaces(); - if (err != DB_SUCCESS) { - return srv_init_abort(err); - } + if (srv_operation != SRV_OPERATION_RESTORE) { dict_sys.load_sys_tables(); } @@ -1269,8 +1505,8 @@ dberr_t srv_start(bool create_new_db) if (!srv_read_only_mode) { const uint32_t flags = FSP_FLAGS_PAGE_SSIZE(); - for (uint32_t id = 0; id <= srv_undo_tablespaces; - id++) { + for (uint32_t id = srv_undo_space_id_start; + id <= srv_undo_tablespaces; id++) { if (fil_space_t* space = fil_space_get(id)) { fsp_flags_try_adjust(space, flags); } @@ -1416,6 +1652,16 @@ dberr_t srv_start(bool create_new_db) return(srv_init_abort(DB_ERROR)); } + /* Recreate the undo tablespaces */ + if (!high_level_read_only) { + err = srv_undo_tablespaces_reinitialize(); + if (err) { + return srv_init_abort(err); + } + } + + srv_undo_tablespaces = srv_undo_tablespaces_open; + /* Here the double write buffer has already been created and so any new rollback segments will be allocated after the double write buffer. The default segment should already exist. diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index 1707f2885ba..7849ae3d6b2 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -306,7 +306,9 @@ trx_purge_add_undo_to_history(const trx_t* trx, trx_undo_t*& undo, mtr_t* mtr) /* Update the latest MySQL binlog name and offset info in rollback segment header if MySQL binlogging is on or the database server is a MySQL replication save. */ - trx_rseg_update_binlog_offset(rseg_header, trx, mtr); + trx_rseg_update_binlog_offset( + rseg_header, trx->mysql_log_file_name, + trx->mysql_log_offset, mtr); } /* Add the log as the first in the history list */ diff --git a/storage/innobase/trx/trx0rseg.cc b/storage/innobase/trx/trx0rseg.cc index 760c4e707ce..0dc82fd21bc 100644 --- a/storage/innobase/trx/trx0rseg.cc +++ b/storage/innobase/trx/trx0rseg.cc @@ -30,6 +30,7 @@ Created 3/26/1996 Heikki Tuuri #include "srv0srv.h" #include "trx0purge.h" #include "srv0mon.h" +#include "log.h" #ifdef WITH_WSREP # include <mysql/service_wsrep.h> @@ -369,7 +370,7 @@ void trx_rseg_t::destroy() void trx_rseg_t::init(fil_space_t *space, uint32_t page) { latch.SRW_LOCK_INIT(trx_rseg_latch_key); - ut_ad(!this->space); + ut_ad(!this->space || this->space != space); this->space= space; page_no= page; last_page_no= FIL_NULL; @@ -414,6 +415,7 @@ static dberr_t trx_undo_lists_init(trx_rseg_t *rseg, trx_id_t &max_trx_id, const buf_block_t *rseg_header) { ut_ad(srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN); + bool is_undo_empty= true; for (ulint i= 0; i < TRX_RSEG_N_SLOTS; i++) { @@ -424,11 +426,14 @@ static dberr_t trx_undo_lists_init(trx_rseg_t *rseg, trx_id_t &max_trx_id, max_trx_id); if (!undo) return DB_CORRUPTION; + if (is_undo_empty) + is_undo_empty= !undo->size || undo->state == TRX_UNDO_CACHED; rseg->curr_size+= undo->size; MONITOR_INC(MONITOR_NUM_UNDO_SLOT_USED); } } + trx_sys.set_undo_non_empty(!is_undo_empty); return DB_SUCCESS; } @@ -532,6 +537,7 @@ static dberr_t trx_rseg_mem_restore(trx_rseg_t *rseg, trx_id_t &max_trx_id, purge_sys.purge_queue.push(*rseg); } + trx_sys.set_undo_non_empty(rseg->history_size > 0); return err; } @@ -592,10 +598,24 @@ dberr_t trx_rseg_array_init() sys, rseg_id); if (page_no != FIL_NULL) { trx_rseg_t& rseg = trx_sys.rseg_array[rseg_id]; - rseg.init(fil_space_get( - trx_sysf_rseg_get_space( - sys, rseg_id)), - page_no); + uint32_t space_id= + trx_sysf_rseg_get_space( + sys, rseg_id); + + fil_space_t *rseg_space = + fil_space_get(space_id); + if (!rseg_space) { + mtr.commit(); + err = DB_ERROR; + sql_print_error( + "InnoDB: Failed to open the undo " + "tablespace undo%03" PRIu32, + (space_id - + srv_undo_space_id_start + 1)); + break; + } + + rseg.init(rseg_space, page_no); ut_ad(rseg.is_persistent()); if ((err = trx_rseg_mem_restore( &rseg, max_trx_id, &mtr)) @@ -685,29 +705,28 @@ which corresponds to the transaction just being committed. In a replication slave, this updates the master binlog position up to which replication has proceeded. @param[in,out] rseg_header rollback segment header -@param[in] trx committing transaction +@param[in] log_file_name binlog file name +@param[in] log_offset binlog file offset @param[in,out] mtr mini-transaction */ -void trx_rseg_update_binlog_offset(buf_block_t *rseg_header, const trx_t *trx, +void trx_rseg_update_binlog_offset(buf_block_t *rseg_header, + const char *log_file_name, + ulonglong log_offset, mtr_t *mtr) { - DBUG_LOG("trx", "trx_mysql_binlog_offset: " << trx->mysql_log_offset); + DBUG_PRINT("trx", ("trx_mysql_binlog_offset %llu", log_offset)); + const size_t len= strlen(log_file_name) + 1; + ut_ad(len > 1); - const size_t len = strlen(trx->mysql_log_file_name) + 1; + if (UNIV_UNLIKELY(len > TRX_RSEG_BINLOG_NAME_LEN)) + return; - ut_ad(len > 1); + mtr->write<8,mtr_t::MAYBE_NOP>( + *rseg_header, + TRX_RSEG + TRX_RSEG_BINLOG_OFFSET + rseg_header->page.frame, + log_offset); - if (UNIV_UNLIKELY(len > TRX_RSEG_BINLOG_NAME_LEN)) { - return; - } - - mtr->write<8,mtr_t::MAYBE_NOP>(*rseg_header, - TRX_RSEG + TRX_RSEG_BINLOG_OFFSET - + rseg_header->page.frame, - trx->mysql_log_offset); + byte *name= TRX_RSEG + TRX_RSEG_BINLOG_NAME + rseg_header->page.frame; - void* name = TRX_RSEG + TRX_RSEG_BINLOG_NAME + rseg_header->page.frame; - - if (memcmp(trx->mysql_log_file_name, name, len)) { - mtr->memcpy(*rseg_header, name, trx->mysql_log_file_name, len); - } + if (memcmp(log_file_name, name, len)) + mtr->memcpy(*rseg_header, name, log_file_name, len); } diff --git a/storage/innobase/trx/trx0sys.cc b/storage/innobase/trx/trx0sys.cc index 1113c72fcbb..4355e11cc49 100644 --- a/storage/innobase/trx/trx0sys.cc +++ b/storage/innobase/trx/trx0sys.cc @@ -325,10 +325,9 @@ bool trx_sys_create_rsegs() /* Increase the number of active undo tablespace in case new rollback segment assigned to new undo tablespace. */ - if (space > srv_undo_tablespaces_active) { + if (space > (srv_undo_space_id_start + + srv_undo_tablespaces_active - 1)) { srv_undo_tablespaces_active++; - - ut_ad(srv_undo_tablespaces_active == space); } } diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 900039d1408..901c0f6a26a 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -725,6 +725,12 @@ corrupted: return err; } + if (trx_sys.is_undo_empty()) { +func_exit: + purge_sys.clone_oldest_view(); + return DB_SUCCESS; + } + /* Look from the rollback segments if there exist undo logs for transactions. */ const time_t start_time = time(NULL); @@ -785,8 +791,7 @@ corrupted: ib::info() << "Trx id counter is " << trx_sys.get_max_trx_id(); } - purge_sys.clone_oldest_view(); - return DB_SUCCESS; + goto func_exit; } /** Assign a persistent rollback segment in a round-robin fashion, @@ -843,8 +848,7 @@ static trx_rseg_t* trx_assign_rseg_low() ut_ad(rseg->is_persistent()); if (rseg->space != fil_system.sys_space) { - if (rseg->skip_allocation() - || !srv_undo_tablespaces) { + if (rseg->skip_allocation()) { continue; } } else if (const fil_space_t *space = |