mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
MDEV-19229 Allow innodb_undo_tablespaces to be changed after database creation
trx_sys_t::undo_log_nonempty: Set to true if there are undo logs to rollback and purge. The algorithm for re-creating the undo tablespace when trx_sys_t::undo_log_nonempty is disabled: 1) trx_sys_t::reset_page(): Reset the TRX_SYS page and assign all rollback segment slots from 1..127 to FIL_NULL 2) Free the rollback segment header page of system tablespace for the slots 1..127 3) Update the binlog and WSREP information in system tablespace rollback segment header Step (1), (2) and Step (3) should happen atomically within a single mini-transaction. 4) srv_undo_delete_old_tablespaces(): Delete the old undo tablespaces present in the undo log directory 5) Make checkpoint to get rid of old undo log tablespaces redo logs 6) Assign new start space id for the undo log tablespaces 7) Re-create the specified undo log tablespaces. InnoDB uses same mtr for this one and step (6) 8) Make checkpoint again, so that server or mariabackup can read the undo log tablespace page0 before applying the redo logs srv_undo_tablespaces_reinit(): Recreate the undo log tablespaces. It does reset trx_sys page, delete the old undo tablespaces, update the binlog offset, write set replication checkpoint in system rollback segment page trx_rseg_update_binlog_offset(): Added 2 new parameters to pass binlog file name and binlog offset trx_rseg_array_init(): Return error if the rollback segment slot points to non-existent tablespace srv_undo_tablespaces_init(): Added new parameter mtr to initialize all undo tablespaces trx_assign_rseg_low(): Allow the transaction to use the rollback segment slots(1..127) even if InnoDB failed to change to the requested innodb_undo_tablespaces=0 srv_start(): Override the user specified value of innodb_undo_tablespaces variable with already existing actual undo tablespaces wf_incremental_process(): Detects whether TRX_SYS page has been modified since last backup. If it is then incremental backup fails and throws the information about taking full backup again xb_assign_undo_space_start(): Removed the function. Because undo001 has first undo space id value in page0 Added test case to test the scenario during startup and mariabackup incremental process too. Reviewed-by : Marko Mäkelä Tested-by : Matthias Leich
This commit is contained in:
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
80
mysql-test/suite/innodb/r/undo_upgrade.result
Normal file
80
mysql-test/suite/innodb/r/undo_upgrade.result
Normal file
@ -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
|
43
mysql-test/suite/innodb/r/undo_upgrade_debug.result
Normal file
43
mysql-test/suite/innodb/r/undo_upgrade_debug.result
Normal file
@ -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
|
@ -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
|
||||
|
@ -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`;
|
||||
|
@ -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;
|
||||
|
||||
|
2
mysql-test/suite/innodb/t/undo_upgrade.opt
Normal file
2
mysql-test/suite/innodb/t/undo_upgrade.opt
Normal file
@ -0,0 +1,2 @@
|
||||
--log-bin=1
|
||||
--innodb_undo_tablespaces=4
|
94
mysql-test/suite/innodb/t/undo_upgrade.test
Normal file
94
mysql-test/suite/innodb/t/undo_upgrade.test
Normal file
@ -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*;
|
70
mysql-test/suite/innodb/t/undo_upgrade_debug.test
Normal file
70
mysql-test/suite/innodb/t/undo_upgrade_debug.test
Normal file
@ -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*;
|
19
mysql-test/suite/mariabackup/undo_upgrade.result
Normal file
19
mysql-test/suite/mariabackup/undo_upgrade.result
Normal file
@ -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;
|
50
mysql-test/suite/mariabackup/undo_upgrade.test
Normal file
50
mysql-test/suite/mariabackup/undo_upgrade.test
Normal file
@ -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;
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
@return DB_SUCCESS if it is valid */
|
||||
static dberr_t srv_validate_undo_tablespaces()
|
||||
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_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.
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
byte *name= TRX_RSEG + TRX_RSEG_BINLOG_NAME + rseg_header->page.frame;
|
||||
|
||||
mtr->write<8,mtr_t::MAYBE_NOP>(*rseg_header,
|
||||
TRX_RSEG + TRX_RSEG_BINLOG_OFFSET
|
||||
+ rseg_header->page.frame,
|
||||
trx->mysql_log_offset);
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 =
|
||||
|
Reference in New Issue
Block a user