summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThirunarayanan Balathandayuthapani <thiru@mariadb.com>2022-10-11 15:14:45 +0530
committerThirunarayanan Balathandayuthapani <thiru@mariadb.com>2022-10-17 21:51:01 +0530
commit162cccee73bc04cf089d35cf457eb109596aec4e (patch)
tree329332c03ac909b6c2a3681b7213caab2d88ace7
parentdb330c87d6d253a1e2e93a747327040d9205c5fe (diff)
downloadmariadb-git-bb-10.6-MDEV-19229.tar.gz
MDEV-19229 Allow innodb_undo_tablespaces to be changed after database creationbb-10.6-MDEV-19229
trx_sys_t::undo_log_empty: Set to true if there are no undo log to rollback and purge. The algorithm for re-creating the undo tablespace when trx_sys_t::undo_log_empty is enabled: 1) Free the rollback segment header page of system tablespace for the slots 1..127 2) trx_sys_t::reset_page(): Reset the TRX_SYS page and assign all rollback segment slots from 1..127 to FIL_NULL 3) Update the binlog and WSREP information in system tablespace rollback segment header Step (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 above (6), (7) 8) Make checkpoint again, so that server or mariabackup can read the undo log tablespace page0 before applying the redo logs 9) Assign the rollback segment header page for each rollback segment slots 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 that can be used for reinitialization of 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 trx_assign_rseg_low(): Even user mentioned the innodb_undo_tablespace value as 0, allow the transaction to use the other undo tablespace rollback segment slots srv_start(): Override the user specified value of innodb_undo_tablespaces variable with already existing actual undo tablespaces Added test case to test the scenario during startup and mariabackup incremental process too.
-rw-r--r--extra/mariabackup/write_filt.cc12
-rw-r--r--extra/mariabackup/xtrabackup.cc90
-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.result62
-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.test74
-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.h7
-rw-r--r--storage/innobase/include/trx0rseg.h7
-rw-r--r--storage/innobase/include/trx0sys.h16
-rw-r--r--storage/innobase/srv/srv0start.cc306
-rw-r--r--storage/innobase/trx/trx0purge.cc4
-rw-r--r--storage/innobase/trx/trx0rseg.cc64
-rw-r--r--storage/innobase/trx/trx0sys.cc5
-rw-r--r--storage/innobase/trx/trx0trx.cc12
24 files changed, 701 insertions, 171 deletions
diff --git a/extra/mariabackup/write_filt.cc b/extra/mariabackup/write_filt.cc
index 8339286e1df..4871e65e3c6 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 4d2da0082aa..18342bfa9e2 100644
--- a/extra/mariabackup/xtrabackup.cc
+++ b/extra/mariabackup/xtrabackup.cc
@@ -3801,89 +3801,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;
- ulint space;
- int n_retries = 5;
- ulint fsp_flags;
-
- 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.*/
@@ -3923,13 +3840,6 @@ 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);
if (err != DB_SUCCESS) {
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 642ba41d97f..f52f5dd0295 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 /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 /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..b199f3f7165
--- /dev/null
+++ b/mysql-test/suite/innodb/r/undo_upgrade.result
@@ -0,0 +1,62 @@
+#
+# 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=.* 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: Successfull 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
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..717215f3474
--- /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 2484e787973..2f5507b616a 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=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=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..69bd9985eea
--- /dev/null
+++ b/mysql-test/suite/innodb/t/undo_upgrade.test
@@ -0,0 +1,74 @@
+--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=.* 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: Successfull 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*;
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..586e3d32c38
--- /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..dbf7f7b8d67
--- /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..e14564b476a
--- /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..1372959d184 100644
--- a/storage/innobase/include/srv0start.h
+++ b/storage/innobase/include/srv0start.h
@@ -33,10 +33,11 @@ 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] mtr mini-transaction in case of reinitialize
+ of undo tablespaces
@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= nullptr);
/** 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..0b24a2c0335 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] 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..ec3a58cbecd 100644
--- a/storage/innobase/include/trx0sys.h
+++ b/storage/innobase/include/trx0sys.h
@@ -855,6 +855,8 @@ class trx_sys_t
bool m_initialised;
+ /** True if there is no undo log to purge or rollback */
+ bool undo_log_empty= true;
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_empty(bool val)
+ {
+ if (undo_log_empty)
+ undo_log_empty= val;
+ }
+
+ /** Get the undo log empty value */
+ bool is_undo_empty() const { return undo_log_empty; }
+
+ /* 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 8e291352d55..90d04ee5cd1 100644
--- a/storage/innobase/srv/srv0start.cc
+++ b/storage/innobase/srv/srv0start.cc
@@ -97,6 +97,7 @@ Created 2/16/1996 Heikki Tuuri
#include "btr0pcur.h"
#include "zlib.h"
#include "ut0crc32.h"
+#include "log.h"
/** We are prepared for a situation that we have this many threads waiting for
a transactional lock inside InnoDB. srv_start() sets the value. */
@@ -431,11 +432,239 @@ 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;
+ bool dblwr_enabled= true;
+ 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 (err) return err;
+
+ 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);
+ }
+
+ trx_rseg_t *rseg= nullptr;
+ for (ulint rseg_id= 1; rseg_id < TRX_SYS_N_RSEGS; rseg_id++)
+ {
+ rseg= &trx_sys.rseg_array[rseg_id];
+ rseg->destroy();
+ rseg->init(nullptr, FIL_NULL);
+ }
+
+ return err;
+}
+
+/** Delete the old undo tablespaces present in the undo log directory */
+static dberr_t srv_undo_delete_old_tablespaces()
+{
+ if (recv_recovery_is_on())
+ mysql_mutex_lock(&log_sys.mutex);
+ /* Delete the old undo tablespaces*/
+ for (ulint i= 0; i < srv_undo_tablespaces_open; ++i)
+ fil_close_tablespace(srv_undo_space_id_start + i);
+
+ if (recv_recovery_is_on())
+ mysql_mutex_unlock(&log_sys.mutex);
+
+ 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 (ulint i= 0; i < srv_undo_tablespaces_open; ++i)
+ {
+ char name[OS_FILE_MAX_PATH];
+ snprintf(name, sizeof name, "%s/undo%03zu", 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 *dict_hdr;
+
+ mtr.start();
+ 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)
+ continue;
+ buf_block_t *block= buf_page_get_gen(
+ rseg->page_id(), 0, RW_X_LATCH, nullptr, BUF_GET, &mtr, &err);
+ if (err) { mtr.commit(); return err; }
+ while (!fseg_free_step(TRX_RSEG + TRX_RSEG_FSEG_HEADER +
+ block->page.frame, &mtr));
+ }
+ }
+
+ /* Reset TRX_SYS page */
+ err= trx_sys.reset_page(&mtr);
+
+ 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 */
+ trx_rseg_t& rseg = trx_sys.rseg_array[0];
+ buf_block_t *rseg_hdr=
+ buf_page_get_gen(rseg.page_id(), 0, RW_X_LATCH, nullptr, BUF_GET,
+ &mtr, &err);
+ if (!rseg_hdr)
+ goto func_exit;
+
+ if (trx_sys.recovered_binlog_lsn > 0)
+ {
+ 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(
+ 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(
+ rseg_hdr, &trx_sys.recovered_wsrep_xid, &mtr);
+#endif /* WITH_WSREP */
+ }
+
+ mtr.commit();
+
+ DBUG_EXECUTE_IF("after_rseg_reset_abort",
+ log_write_up_to(mtr.commit_lsn(), true);
+ err= DB_ERROR;);
+
+ if (err) return err;
+
+ 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) return err;
+
+ mtr.start();
+
+ 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;
+
+ if (srv_undo_tablespaces == 0)
+ {
+ srv_undo_space_id_start= 0;
+ srv_undo_tablespaces_open= 0;
+ goto func_exit;
+ }
+
+ /* Assign the new space id for the first undo tablespace */
+ srv_undo_space_id_start = mach_read_from_4(
+ DICT_HDR + DICT_HDR_MAX_SPACE_ID + dict_hdr->page.frame);
+
+ if (srv_undo_space_id_start + srv_undo_tablespaces
+ > SRV_SPACE_ID_UPPER_BOUND)
+ {
+ err= DB_ERROR;
+ sql_print_error("InnoDB: Running out of tablespace id");
+ goto func_exit;
+ }
+
+ 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) return err;
+
+ /* 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. */
@@ -449,15 +678,13 @@ 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";
-
- if (srv_undo_tablespaces == 0)
- ib::warn() << "innodb_undo_tablespaces=0 disables"
- " dedicated undo log tablespaces";
- }
return DB_SUCCESS;
}
@@ -643,23 +870,23 @@ srv_check_undo_redo_logs_exists()
return(DB_SUCCESS);
}
-static dberr_t srv_all_undo_tablespaces_open(bool create_new_db, ulint n_undo)
+static dberr_t srv_all_undo_tablespaces_open(bool create_new_undo, ulint 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). */
- ulint prev_id= create_new_db ? srv_undo_space_id_start - 1 : 0;
+ ulint prev_id= create_new_undo ? srv_undo_space_id_start - 1 : 0;
for (ulint i= 0; i < n_undo; ++i)
{
char name[OS_FILE_MAX_PATH];
snprintf(name, sizeof name, "%s/undo%03zu", srv_undo_dir, i + 1);
- ulint space_id= srv_undo_tablespace_open(create_new_db, name, i);
+ ulint space_id= srv_undo_tablespace_open(create_new_undo, name, i);
if (!space_id)
{
- if (!create_new_db)
+ if (!create_new_undo)
break;
ib::error() << "Unable to open create tablespace '" << name << "'.";
return DB_ERROR;
@@ -686,24 +913,25 @@ static dberr_t srv_all_undo_tablespaces_open(bool create_new_db, ulint n_undo)
{
char name[OS_FILE_MAX_PATH];
snprintf(name, sizeof name, "%s/undo%03zu", 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] mtr mini-transaction in case of reinitialize
+ of undo tablespaces
@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_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;
@@ -711,9 +939,8 @@ 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)
@@ -734,11 +961,11 @@ srv_undo_tablespaces_init(bool create_new_db)
already exist. */
srv_undo_tablespaces_active= srv_undo_tablespaces;
- ulint n_undo= (create_new_db || srv_operation == SRV_OPERATION_BACKUP ||
+ ulint 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
@@ -746,18 +973,21 @@ 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)
{
- mtr_t mtr;
+ mtr_t *create_mtr= mtr;
+ if (!create_mtr) create_mtr= new mtr_t();
for (ulint i= 0; i < srv_undo_tablespaces; ++i)
{
- mtr.start();
+ if (!mtr) create_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,
+ create_mtr);
+ if (!mtr) create_mtr->commit();
+ if (err) return err;
}
+
+ if (!mtr) delete create_mtr;
}
return DB_SUCCESS;
@@ -1309,6 +1539,8 @@ dberr_t srv_start(bool create_new_db)
}
return(srv_init_abort(err));
}
+
+ srv_undo_space_id_start= 1;
} else {
srv_log_file_size = 0;
@@ -1475,10 +1707,7 @@ file_checked:
/* 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();
}
@@ -1512,11 +1741,18 @@ file_checked:
}
}
+ err= srv_undo_tablespaces_reinitialize();
+ if (err) return srv_init_abort(err);
+
+ if (srv_undo_tablespaces != srv_undo_tablespaces_open)
+ srv_undo_tablespaces= (ulong) srv_undo_tablespaces_open;
+
fil_system.space_id_reuse_warned = false;
if (!srv_read_only_mode) {
const ulint flags = FSP_FLAGS_PAGE_SSIZE();
- for (ulint id = 0; id <= srv_undo_tablespaces; id++) {
+ for (ulint 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);
}
diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc
index b5d00d9624f..f7b77260a4c 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..fbe2bdb36d6 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,6 @@ 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);
this->space= space;
page_no= page;
last_page_no= FIL_NULL;
@@ -414,6 +414,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 +425,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_empty(is_undo_empty);
return DB_SUCCESS;
}
@@ -532,6 +536,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_empty(rseg->history_size == 0);
return err;
}
@@ -592,10 +597,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(&mtr);
+ err = DB_ERROR;
+ sql_print_error(
+ "Failed to open the undo "
+ "tablespace undo%03zu",
+ (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 +704,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);
-
- const size_t len = strlen(trx->mysql_log_file_name) + 1;
+ DBUG_PRINT("trx", ("trx_mysql_binlog_offset %llu", log_offset));
+ const size_t len= strlen(log_file_name) + 1;
+ ut_ad(len > 1);
- ut_ad(len > 1);
+ if (UNIV_UNLIKELY(len > TRX_RSEG_BINLOG_NAME_LEN))
+ return;
- 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);
+ mtr->write<8,mtr_t::MAYBE_NOP>(
+ *rseg_header,
+ TRX_RSEG + TRX_RSEG_BINLOG_OFFSET + rseg_header->page.frame,
+ log_offset);
- void* name = TRX_RSEG + TRX_RSEG_BINLOG_NAME + rseg_header->page.frame;
+ byte *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 2479e5a4cc1..e98dcef0250 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 111f8fe5f3a..51f333532a5 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 =