summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--extra/mariabackup/write_filt.cc12
-rw-r--r--extra/mariabackup/xtrabackup.cc92
-rw-r--r--mysql-test/suite/innodb/include/have_undo_tablespaces.inc4
-rw-r--r--mysql-test/suite/innodb/r/log_file.result4
-rw-r--r--mysql-test/suite/innodb/r/undo_truncate.result2
-rw-r--r--mysql-test/suite/innodb/r/undo_truncate_recover.result2
-rw-r--r--mysql-test/suite/innodb/r/undo_upgrade.result80
-rw-r--r--mysql-test/suite/innodb/r/undo_upgrade_debug.result43
-rw-r--r--mysql-test/suite/innodb/t/log_file.test4
-rw-r--r--mysql-test/suite/innodb/t/undo_truncate.test7
-rw-r--r--mysql-test/suite/innodb/t/undo_truncate_recover.test6
-rw-r--r--mysql-test/suite/innodb/t/undo_upgrade.opt2
-rw-r--r--mysql-test/suite/innodb/t/undo_upgrade.test94
-rw-r--r--mysql-test/suite/innodb/t/undo_upgrade_debug.test70
-rw-r--r--mysql-test/suite/mariabackup/undo_upgrade.result19
-rw-r--r--mysql-test/suite/mariabackup/undo_upgrade.test50
-rw-r--r--storage/innobase/include/srv0start.h6
-rw-r--r--storage/innobase/include/trx0rseg.h7
-rw-r--r--storage/innobase/include/trx0sys.h16
-rw-r--r--storage/innobase/srv/srv0start.cc330
-rw-r--r--storage/innobase/trx/trx0purge.cc4
-rw-r--r--storage/innobase/trx/trx0rseg.cc65
-rw-r--r--storage/innobase/trx/trx0sys.cc5
-rw-r--r--storage/innobase/trx/trx0trx.cc12
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 =