Commit 95e90326 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-21216 InnoDB does dirty read of TRX_SYS page before recovery

InnoDB startup was discovering undo tablespaces in a dirty way.
It was reading a possibly stale copy of the TRX_SYS page before
processing any redo log records.

srv_start(): Do not call buf_pool_invalidate(). Invoke
trx_rseg_get_n_undo_tablespaces() after the recovery has been initiated.

recv_recovery_from_checkpoint_start(): Assert that the buffer pool is
empty. This used to be guaranteed by the buf_pool_invalidate() call.

trx_rseg_get_n_undo_tablespaces(): Move to the calling compilation unit,
and reimplement in a simpler way.

srv_undo_tablespace_create(): Remove the constant parameter
size=SRV_UNDO_TABLESPACE_SIZE_IN_PAGES.

srv_undo_tablespace_open(): Reimplement in a cleaner way, with
more robust error handling.

srv_all_undo_tablespaces_open(): Split from srv_undo_tablespaces_init().

srv_undo_tablespaces_init(): Read all "undo001","undo002" tablespace
files directly, without consulting the TRX_SYS page via calling
trx_rseg_get_n_undo_tablespaces().

This is joint work with Thirunarayanan Balathandayuthapani.
parent e5dfdc56
...@@ -217,7 +217,7 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES ...@@ -217,7 +217,7 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES
WHERE engine = 'innodb' WHERE engine = 'innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED'); AND support IN ('YES', 'DEFAULT', 'ENABLED');
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
FOUND 1 /InnoDB: Unable to open undo tablespace.*undo002/ in mysqld.1.err FOUND 1 /InnoDB: Expected to open innodb_undo_tablespaces=3 but was able to find only 1/ in mysqld.1.err
bak_ib_logfile0 bak_ib_logfile0
bak_ib_logfile1 bak_ib_logfile1
bak_ib_logfile2 bak_ib_logfile2
...@@ -255,7 +255,7 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES ...@@ -255,7 +255,7 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES
WHERE engine = 'innodb' WHERE engine = 'innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED'); AND support IN ('YES', 'DEFAULT', 'ENABLED');
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
FOUND 1 /InnoDB: Unable to open undo tablespace.*undo001/ in mysqld.1.err FOUND 1 /InnoDB: Expected to open innodb_undo_tablespaces=3 but was able to find only 0/ in mysqld.1.err
bak_ib_logfile0 bak_ib_logfile0
bak_ib_logfile1 bak_ib_logfile1
bak_ib_logfile2 bak_ib_logfile2
......
...@@ -171,7 +171,7 @@ let SEARCH_PATTERN=undo tablespace .*undo003.* exists\. Creating system tablespa ...@@ -171,7 +171,7 @@ let SEARCH_PATTERN=undo tablespace .*undo003.* exists\. Creating system tablespa
--source include/start_mysqld.inc --source include/start_mysqld.inc
eval $check_no_innodb; eval $check_no_innodb;
--source include/shutdown_mysqld.inc --source include/shutdown_mysqld.inc
let SEARCH_PATTERN=InnoDB: Unable to open undo tablespace.*undo002; let SEARCH_PATTERN=InnoDB: Expected to open innodb_undo_tablespaces=3 but was able to find only 1;
--source include/search_pattern_in_file.inc --source include/search_pattern_in_file.inc
# clean up & Restore # clean up & Restore
--source ../include/log_file_cleanup.inc --source ../include/log_file_cleanup.inc
...@@ -183,7 +183,7 @@ let SEARCH_PATTERN=InnoDB: Unable to open undo tablespace.*undo002; ...@@ -183,7 +183,7 @@ let SEARCH_PATTERN=InnoDB: Unable to open undo tablespace.*undo002;
--source include/start_mysqld.inc --source include/start_mysqld.inc
eval $check_no_innodb; eval $check_no_innodb;
--source include/shutdown_mysqld.inc --source include/shutdown_mysqld.inc
let SEARCH_PATTERN=InnoDB: Unable to open undo tablespace.*undo001; let SEARCH_PATTERN=InnoDB: Expected to open innodb_undo_tablespaces=3 but was able to find only 0;
--source include/search_pattern_in_file.inc --source include/search_pattern_in_file.inc
# clean up & Restore # clean up & Restore
......
# Create 2 UNDO TABLESPACE(UNDO003, UNDO004) # Create 2 UNDO TABLESPACE(UNDO001(space_id =3), UNDO002(space_id =4))
CREATE TABLE t1(a varchar(60)) ENGINE INNODB; CREATE TABLE t1(a varchar(60)) ENGINE INNODB;
start transaction; start transaction;
INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(1);
# xtrabackup backup # xtrabackup backup
# Display undo log files from target directory # Display undo log files from target directory
undo003 undo001
undo004 undo002
# xtrabackup prepare # xtrabackup prepare
# Display undo log files from targer directory # Display undo log files from targer directory
undo003 undo001
undo004 undo002
DROP TABLE t1; DROP TABLE t1;
--source include/have_innodb.inc --source include/have_innodb.inc
--source include/have_debug.inc --source include/have_debug.inc
--echo # Create 2 UNDO TABLESPACE(UNDO003, UNDO004) --echo # Create 2 UNDO TABLESPACE(UNDO001(space_id =3), UNDO002(space_id =4))
let $basedir=$MYSQLTEST_VARDIR/tmp/backup; let $basedir=$MYSQLTEST_VARDIR/tmp/backup;
......
...@@ -85,17 +85,6 @@ trx_rseg_create(ulint space_id) ...@@ -85,17 +85,6 @@ trx_rseg_create(ulint space_id)
void void
trx_temp_rseg_create(); trx_temp_rseg_create();
/********************************************************************
Get the number of unique rollback tablespaces in use except space id 0.
The last space id will be the sentinel value ULINT_UNDEFINED. The array
will be sorted on space id. Note: space_ids should have have space for
TRX_SYS_N_RSEGS + 1 elements.
@return number of unique rollback tablespaces in use. */
ulint
trx_rseg_get_n_undo_tablespaces(
/*============================*/
ulint* space_ids); /*!< out: array of space ids of
UNDO tablespaces */
/* Number of undo log slots in a rollback segment file copy */ /* Number of undo log slots in a rollback segment file copy */
#define TRX_RSEG_N_SLOTS (srv_page_size / 16) #define TRX_RSEG_N_SLOTS (srv_page_size / 16)
......
...@@ -3414,6 +3414,15 @@ recv_recovery_from_checkpoint_start(lsn_t flush_lsn) ...@@ -3414,6 +3414,15 @@ recv_recovery_from_checkpoint_start(lsn_t flush_lsn)
ut_ad(srv_operation == SRV_OPERATION_NORMAL ut_ad(srv_operation == SRV_OPERATION_NORMAL
|| srv_operation == SRV_OPERATION_RESTORE || srv_operation == SRV_OPERATION_RESTORE
|| srv_operation == SRV_OPERATION_RESTORE_EXPORT); || srv_operation == SRV_OPERATION_RESTORE_EXPORT);
#ifdef UNIV_DEBUG
for (ulint i= 0; i < srv_buf_pool_instances; i++) {
buf_pool_t* buf_pool = buf_pool_from_array(i);
buf_flush_list_mutex_enter(buf_pool);
ut_ad(UT_LIST_GET_LEN(buf_pool->LRU) == 0);
ut_ad(UT_LIST_GET_LEN(buf_pool->unzip_LRU) == 0);
buf_flush_list_mutex_exit(buf_pool);
}
#endif
/* Initialize red-black tree for fast insertions into the /* Initialize red-black tree for fast insertions into the
flush_list during recovery process. */ flush_list during recovery process. */
......
...@@ -488,15 +488,10 @@ create_log_files_rename( ...@@ -488,15 +488,10 @@ create_log_files_rename(
return(err); return(err);
} }
/*********************************************************************//** /** Create an undo tablespace file
Create undo tablespace. @param[in] name file name
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
static static dberr_t srv_undo_tablespace_create(const char* name)
dberr_t
srv_undo_tablespace_create(
/*=======================*/
const char* name, /*!< in: tablespace name */
ulint size) /*!< in: tablespace size in pages */
{ {
pfs_os_file_t fh; pfs_os_file_t fh;
bool ret; bool ret;
...@@ -532,18 +527,15 @@ srv_undo_tablespace_create( ...@@ -532,18 +527,15 @@ srv_undo_tablespace_create(
" be created"; " be created";
ib::info() << "Setting file " << name << " size to " ib::info() << "Setting file " << name << " size to "
<< (size >> (20 - srv_page_size_shift)) << " MB"; << (SRV_UNDO_TABLESPACE_SIZE_IN_PAGES >> (20 - srv_page_size_shift)) << " MB";
ib::info() << "Database physically writes the file full: " ib::info() << "Database physically writes the file full: "
<< "wait..."; << "wait...";
ret = os_file_set_size( if (!os_file_set_size(name, fh, os_offset_t
name, fh, os_offset_t(size) << srv_page_size_shift); {SRV_UNDO_TABLESPACE_SIZE_IN_PAGES}
<< srv_page_size_shift)) {
if (!ret) { ib::error() << "Unable to allocate " << name;
ib::info() << "Error in creating " << name
<< ": probably out of disk space";
err = DB_ERROR; err = DB_ERROR;
} }
...@@ -553,77 +545,154 @@ srv_undo_tablespace_create( ...@@ -553,77 +545,154 @@ srv_undo_tablespace_create(
return(err); return(err);
} }
/** Open an undo tablespace. /* Validate the number of undo opened undo tablespace and user given
@param[in] name tablespace file name undo tablespace
@param[in] space_id tablespace ID @return DB_SUCCESS if it is valid */
@param[in] create_new_db whether undo tablespaces are being created static dberr_t srv_validate_undo_tablespaces()
@return whether the tablespace was opened */
static bool srv_undo_tablespace_open(const char* name, ulint space_id,
bool create_new_db)
{ {
pfs_os_file_t fh; /* If the user says that there are fewer than what we find we
bool success; tolerate that discrepancy but not the inverse. Because there could
char undo_name[sizeof "innodb_undo000"]; be unused undo tablespaces for future use. */
snprintf(undo_name, sizeof(undo_name), if (srv_undo_tablespaces > srv_undo_tablespaces_open)
"innodb_undo%03u", static_cast<unsigned>(space_id)); {
ib::error() << "Expected to open innodb_undo_tablespaces="
fh = os_file_create( << srv_undo_tablespaces
innodb_data_file_key, name, OS_FILE_OPEN << " but was able to find only "
| OS_FILE_ON_ERROR_NO_EXIT | OS_FILE_ON_ERROR_SILENT, << srv_undo_tablespaces_open;
OS_FILE_AIO, OS_DATA_FILE, srv_read_only_mode, &success);
if (!success) { return DB_ERROR;
return false; }
} else if (srv_undo_tablespaces_open > 0)
{
os_offset_t size = os_file_get_size(fh); ib::info() << "Opened " << srv_undo_tablespaces_open
ut_a(size != os_offset_t(-1)); << " undo tablespaces";
/* Load the tablespace into InnoDB's internal data structures. */ if (srv_undo_tablespaces == 0)
ib::warn() << "innodb_undo_tablespaces=0 disables"
/* We set the biggest space id to the undo tablespace " dedicated undo log tablespaces";
because InnoDB hasn't opened any other tablespace apart }
from the system tablespace. */ return DB_SUCCESS;
}
fil_set_max_space_id_if_bigger(space_id);
ulint fsp_flags;
switch (srv_checksum_algorithm) {
case SRV_CHECKSUM_ALGORITHM_FULL_CRC32:
case SRV_CHECKSUM_ALGORITHM_STRICT_FULL_CRC32:
fsp_flags = (FSP_FLAGS_FCRC32_MASK_MARKER
| FSP_FLAGS_FCRC32_PAGE_SSIZE());
break;
default:
fsp_flags = FSP_FLAGS_PAGE_SSIZE();
}
fil_space_t* space = fil_space_create(undo_name, space_id, fsp_flags,
FIL_TYPE_TABLESPACE, NULL);
ut_a(fil_validate());
ut_a(space);
fil_node_t* file = space->add(name, fh, 0, false, true);
mutex_enter(&fil_system.mutex);
if (create_new_db) {
space->size = file->size = ulint(size >> srv_page_size_shift);
space->size_in_header = SRV_UNDO_TABLESPACE_SIZE_IN_PAGES;
} else {
success = file->read_page0(true);
if (!success) {
os_file_close(file->handle);
file->handle = OS_FILE_CLOSED;
ut_a(fil_system.n_open > 0);
fil_system.n_open--;
}
}
mutex_exit(&fil_system.mutex); /** @return the number of active undo tablespaces (except system tablespace) */
static ulint trx_rseg_get_n_undo_tablespaces()
{
std::set<uint32_t> space_ids;
mtr_t mtr;
mtr.start();
if (const buf_block_t *sys_header= trx_sysf_get(&mtr, false))
for (ulint rseg_id= 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++)
if (trx_sysf_rseg_get_page_no(sys_header, rseg_id) != FIL_NULL)
if (uint32_t space= trx_sysf_rseg_get_space(sys_header, rseg_id))
space_ids.insert(space);
mtr.commit();
return space_ids.size();
}
return success; /** Open an undo tablespace.
@param[in] create whether undo tablespaces are being created
@param[in] name tablespace file name
@param[in] i undo tablespace count
@return undo tablespace identifier
@retval 0 on failure */
static ulint srv_undo_tablespace_open(bool create, const char* name, ulint i)
{
pfs_os_file_t fh;
bool success;
char undo_name[sizeof "innodb_undo000"];
ulint space_id= 0;
ulint fsp_flags= 0;
if (create)
{
space_id= srv_undo_space_id_start + i;
snprintf(undo_name, sizeof(undo_name),
"innodb_undo%03u", static_cast<unsigned>(space_id));
switch (srv_checksum_algorithm) {
case SRV_CHECKSUM_ALGORITHM_FULL_CRC32:
case SRV_CHECKSUM_ALGORITHM_STRICT_FULL_CRC32:
fsp_flags= FSP_FLAGS_FCRC32_MASK_MARKER | FSP_FLAGS_FCRC32_PAGE_SSIZE();
break;
default:
fsp_flags= FSP_FLAGS_PAGE_SSIZE();
}
}
fh = os_file_create(
innodb_data_file_key, name, OS_FILE_OPEN
| OS_FILE_ON_ERROR_NO_EXIT | OS_FILE_ON_ERROR_SILENT,
OS_FILE_AIO, OS_DATA_FILE, srv_read_only_mode, &success);
if (!success)
return 0;
os_offset_t size= os_file_get_size(fh);
ut_a(size != os_offset_t(-1));
if (!create)
{
page_t *page= static_cast<byte*>(aligned_malloc(srv_page_size,
srv_page_size));
dberr_t err= os_file_read(IORequestRead, fh, page, 0, srv_page_size);
if (err != DB_SUCCESS)
{
err_exit:
ib::error() << "Unable to read first page of file " << name;
aligned_free(page);
return err;
}
fsp_flags= mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page);
uint32_t id= fsp_header_get_space_id(page);
if (id == 0 || id >= SRV_LOG_SPACE_FIRST_ID ||
buf_page_is_corrupted(false, page, fsp_flags))
{
err= DB_CORRUPTION;
goto err_exit;
}
space_id= id;
snprintf(undo_name, sizeof undo_name, "innodb_undo%03u", id);
aligned_free(page);
}
/* Load the tablespace into InnoDB's internal data structures. */
/* We set the biggest space id to the undo tablespace
because InnoDB hasn't opened any other tablespace apart
from the system tablespace. */
fil_set_max_space_id_if_bigger(space_id);
fil_space_t *space= fil_space_create(undo_name, space_id, fsp_flags,
FIL_TYPE_TABLESPACE, NULL);
ut_a(fil_validate());
ut_a(space);
fil_node_t *file= space->add(name, fh, 0, false, true);
mutex_enter(&fil_system.mutex);
if (create)
{
space->size= file->size= ulint(size >> srv_page_size_shift);
space->size_in_header= SRV_UNDO_TABLESPACE_SIZE_IN_PAGES;
}
else
{
success= file->read_page0(true);
if (!success)
{
os_file_close(file->handle);
file->handle= OS_FILE_CLOSED;
ut_a(fil_system.n_open > 0);
fil_system.n_open--;
}
}
mutex_exit(&fil_system.mutex);
return space_id;
} }
/** Check if undo tablespaces and redo log files exist before creating a /** Check if undo tablespaces and redo log files exist before creating a
...@@ -702,194 +771,125 @@ srv_check_undo_redo_logs_exists() ...@@ -702,194 +771,125 @@ srv_check_undo_redo_logs_exists()
return(DB_SUCCESS); return(DB_SUCCESS);
} }
static dberr_t srv_all_undo_tablespaces_open(bool create_new_db, 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;
for (ulint i= 0; i < n_undo; ++i)
{
char name[OS_FILE_MAX_PATH];
snprintf(name, sizeof name, "%s%cundo%03zu", srv_undo_dir,
OS_PATH_SEPARATOR, i + 1);
ulint space_id= srv_undo_tablespace_open(create_new_db, name, i);
if (!space_id)
{
if (!create_new_db)
break;
ib::error() << "Unable to open create tablespace '" << name << "'.";
return DB_ERROR;
}
/* Should be no gaps in undo tablespace ids. */
ut_a(!i || prev_id + 1 == space_id);
prev_id= space_id;
/* Note the first undo tablespace id in case of
no active undo tablespace. */
if (0 == srv_undo_tablespaces_open++)
srv_undo_space_id_start= space_id;
}
/* Open any extra unused undo tablespaces. These must be contiguous.
We stop at the first failure. These are undo tablespaces that are
not in use and therefore not required by recovery. We only check
that there are no gaps. */
for (ulint i= prev_id + 1; i < srv_undo_space_id_start + TRX_SYS_N_RSEGS;
++i)
{
char name[OS_FILE_MAX_PATH];
snprintf(name, sizeof(name),
"%s%cundo%03zu", srv_undo_dir, OS_PATH_SEPARATOR, i);
if (!srv_undo_tablespace_open(create_new_db, name, i))
break;
++srv_undo_tablespaces_open;
}
return srv_validate_undo_tablespaces();
}
/** Open the configured number of dedicated undo tablespaces. /** Open the configured number of dedicated undo tablespaces.
@param[in] create_new_db whether the database is being initialized @param[in] create_new_db whether the database is being initialized
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
dberr_t dberr_t
srv_undo_tablespaces_init(bool create_new_db) srv_undo_tablespaces_init(bool create_new_db)
{ {
ulint i; srv_undo_tablespaces_open= 0;
dberr_t err = DB_SUCCESS;
ulint prev_space_id = 0; ut_a(srv_undo_tablespaces <= TRX_SYS_N_RSEGS);
ulint n_undo_tablespaces; ut_a(!create_new_db || srv_operation == SRV_OPERATION_NORMAL);
ulint undo_tablespace_ids[TRX_SYS_N_RSEGS + 1];
if (srv_undo_tablespaces == 1)
srv_undo_tablespaces_open = 0; srv_undo_tablespaces= 0;
ut_a(srv_undo_tablespaces <= TRX_SYS_N_RSEGS); /* Create the undo spaces only if we are creating a new
ut_a(!create_new_db || srv_operation == SRV_OPERATION_NORMAL); instance. We don't allow creating of new undo tablespaces
in an existing instance (yet). */
if (srv_undo_tablespaces == 1) { /* 1 is not allowed, make it 0 */ if (create_new_db)
srv_undo_tablespaces = 0; {
} srv_undo_space_id_start= 1;
DBUG_EXECUTE_IF("innodb_undo_upgrade", srv_undo_space_id_start= 3;);
memset(undo_tablespace_ids, 0x0, sizeof(undo_tablespace_ids));
for (ulint i= 0; i < srv_undo_tablespaces; ++i)
/* Create the undo spaces only if we are creating a new {
instance. We don't allow creating of new undo tablespaces char name[OS_FILE_MAX_PATH];
in an existing instance (yet). This restriction exists because snprintf(name, sizeof name, "%s%cundo%03zu",
we check in several places for SYSTEM tablespaces to be less than srv_undo_dir, OS_PATH_SEPARATOR, i + 1);
the min of user defined tablespace ids. Once we implement saving if (dberr_t err= srv_undo_tablespace_create(name))
the location of the undo tablespaces and their space ids this {
restriction will/should be lifted. */ ib::error() << "Could not create undo tablespace '" << name << "'.";
return err;
for (i = 0; create_new_db && i < srv_undo_tablespaces; ++i) { }
char name[OS_FILE_MAX_PATH]; }
ulint space_id = i + 1; }
DBUG_EXECUTE_IF("innodb_undo_upgrade", /* Get the tablespace ids of all the undo segments excluding
space_id = i + 3;); the system tablespace (0). If we are creating a new instance then
we build the undo_tablespace_ids ourselves since they don't
snprintf( already exist. */
name, sizeof(name), srv_undo_tablespaces_active= srv_undo_tablespaces;
"%s%cundo%03zu",
srv_undo_dir, OS_PATH_SEPARATOR, space_id); ulint n_undo= (create_new_db || srv_operation == SRV_OPERATION_BACKUP ||
srv_operation == SRV_OPERATION_RESTORE_DELTA)
if (i == 0) { ? srv_undo_tablespaces : TRX_SYS_N_RSEGS;
srv_undo_space_id_start = space_id;
prev_space_id = srv_undo_space_id_start - 1; if (dberr_t err= srv_all_undo_tablespaces_open(create_new_db, n_undo))
} return err;
undo_tablespace_ids[i] = space_id; /* Initialize srv_undo_space_id_start=0 when there are no
dedicated undo tablespaces. */
err = srv_undo_tablespace_create( if (srv_undo_tablespaces_open == 0)
name, SRV_UNDO_TABLESPACE_SIZE_IN_PAGES); srv_undo_space_id_start= 0;
if (err != DB_SUCCESS) { if (create_new_db)
ib::error() << "Could not create undo tablespace '" {
<< name << "'."; mtr_t mtr;
return(err); for (ulint i= 0; i < srv_undo_tablespaces; ++i)
} {
} mtr.start();
fsp_header_init(fil_space_get(srv_undo_space_id_start + i),
/* Get the tablespace ids of all the undo segments excluding SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, &mtr);
the system tablespace (0). If we are creating a new instance then mtr.commit();
we build the undo_tablespace_ids ourselves since they don't }
already exist. */ }
n_undo_tablespaces = create_new_db
|| srv_operation == SRV_OPERATION_BACKUP return DB_SUCCESS;
|| srv_operation == SRV_OPERATION_RESTORE_DELTA
? srv_undo_tablespaces
: trx_rseg_get_n_undo_tablespaces(undo_tablespace_ids);
srv_undo_tablespaces_active = srv_undo_tablespaces;
switch (srv_operation) {
case SRV_OPERATION_RESTORE_DELTA:
case SRV_OPERATION_BACKUP:
for (i = 0; i < n_undo_tablespaces; i++) {
undo_tablespace_ids[i] = i + srv_undo_space_id_start;
}
prev_space_id = srv_undo_space_id_start - 1;
break;
case SRV_OPERATION_NORMAL:
case SRV_OPERATION_RESTORE:
case SRV_OPERATION_RESTORE_EXPORT:
break;
}
/* 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). */
for (i = 0; i < n_undo_tablespaces; ++i) {
char name[OS_FILE_MAX_PATH];
snprintf(
name, sizeof(name),
"%s%cundo%03zu",
srv_undo_dir, OS_PATH_SEPARATOR,
undo_tablespace_ids[i]);
/* Should be no gaps in undo tablespace ids. */
ut_a(!i || prev_space_id + 1 == undo_tablespace_ids[i]);
/* The system space id should not be in this array. */
ut_a(undo_tablespace_ids[i] != 0);
ut_a(undo_tablespace_ids[i] != ULINT_UNDEFINED);
if (!srv_undo_tablespace_open(name, undo_tablespace_ids[i],
create_new_db)) {
ib::error() << "Unable to open undo tablespace '"
<< name << "'.";
return DB_ERROR;
}
prev_space_id = undo_tablespace_ids[i];
/* Note the first undo tablespace id in case of
no active undo tablespace. */
if (0 == srv_undo_tablespaces_open++) {
srv_undo_space_id_start = undo_tablespace_ids[i];
}
}
/* Open any extra unused undo tablespaces. These must be contiguous.
We stop at the first failure. These are undo tablespaces that are
not in use and therefore not required by recovery. We only check
that there are no gaps. */
for (i = prev_space_id + 1;
i < srv_undo_space_id_start + TRX_SYS_N_RSEGS; ++i) {
char name[OS_FILE_MAX_PATH];
snprintf(
name, sizeof(name),
"%s%cundo%03zu", srv_undo_dir, OS_PATH_SEPARATOR, i);
if (!srv_undo_tablespace_open(name, i, create_new_db)) {
err = DB_ERROR;
break;
}
++n_undo_tablespaces;
++srv_undo_tablespaces_open;
}
/* Initialize srv_undo_space_id_start=0 when there are no
dedicated undo tablespaces. */
if (n_undo_tablespaces == 0) {
srv_undo_space_id_start = 0;
}
/* 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. */
if (srv_undo_tablespaces > n_undo_tablespaces) {
ib::error() << "Expected to open innodb_undo_tablespaces="
<< srv_undo_tablespaces
<< " but was able to find only "
<< n_undo_tablespaces;
return(err != DB_SUCCESS ? err : DB_ERROR);
} else if (n_undo_tablespaces > 0) {
ib::info() << "Opened " << n_undo_tablespaces
<< " undo tablespaces";
if (srv_undo_tablespaces == 0) {
ib::warn() << "innodb_undo_tablespaces=0 disables"
" dedicated undo log tablespaces";
}
}
if (create_new_db) {
mtr_t mtr;
for (i = 0; i < n_undo_tablespaces; ++i) {
mtr.start();
fsp_header_init(fil_space_get(undo_tablespace_ids[i]),
SRV_UNDO_TABLESPACE_SIZE_IN_PAGES,
&mtr);
mtr.commit();
}
}
return(DB_SUCCESS);
} }
/** Create the temporary file tablespace. /** Create the temporary file tablespace.
...@@ -1736,14 +1736,6 @@ dberr_t srv_start(bool create_new_db) ...@@ -1736,14 +1736,6 @@ dberr_t srv_start(bool create_new_db)
return(srv_init_abort(err)); return(srv_init_abort(err));
} }
} else { } else {
/* Work around the bug that we were performing a dirty read of
at least the TRX_SYS page into the buffer pool above, without
reading or applying any redo logs.
MDEV-19229 FIXME: Remove the dirty reads and this call.
Add an assertion that the buffer pool is empty. */
buf_pool_invalidate();
/* We always try to do a recovery, even if the database had /* We always try to do a recovery, even if the database had
been shut down normally: this is the normal startup path */ been shut down normally: this is the normal startup path */
...@@ -1767,6 +1759,12 @@ dberr_t srv_start(bool create_new_db) ...@@ -1767,6 +1759,12 @@ dberr_t srv_start(bool create_new_db)
case SRV_OPERATION_RESTORE: case SRV_OPERATION_RESTORE:
/* This must precede /* This must precede
recv_apply_hashed_log_recs(true). */ recv_apply_hashed_log_recs(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);
}
trx_lists_init_at_db_start(); trx_lists_init_at_db_start();
break; break;
case SRV_OPERATION_RESTORE_DELTA: case SRV_OPERATION_RESTORE_DELTA:
......
...@@ -31,8 +31,6 @@ Created 3/26/1996 Heikki Tuuri ...@@ -31,8 +31,6 @@ Created 3/26/1996 Heikki Tuuri
#include "trx0purge.h" #include "trx0purge.h"
#include "srv0mon.h" #include "srv0mon.h"
#include <algorithm>
#ifdef WITH_WSREP #ifdef WITH_WSREP
#include <mysql/service_wsrep.h> #include <mysql/service_wsrep.h>
...@@ -712,55 +710,6 @@ trx_temp_rseg_create() ...@@ -712,55 +710,6 @@ trx_temp_rseg_create()
} }
} }
/********************************************************************
Get the number of unique rollback tablespaces in use except space id 0.
The last space id will be the sentinel value ULINT_UNDEFINED. The array
will be sorted on space id. Note: space_ids should have have space for
TRX_SYS_N_RSEGS + 1 elements.
@return number of unique rollback tablespaces in use. */
ulint
trx_rseg_get_n_undo_tablespaces(
/*============================*/
ulint* space_ids) /*!< out: array of space ids of
UNDO tablespaces */
{
mtr_t mtr;
mtr.start();
buf_block_t* sys_header = trx_sysf_get(&mtr, false);
if (!sys_header) {
mtr.commit();
return 0;
}
ulint* end = space_ids;
for (ulint rseg_id = 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) {
uint32_t page_no = trx_sysf_rseg_get_page_no(sys_header,
rseg_id);
if (page_no == FIL_NULL) {
continue;
}
if (ulint space = trx_sysf_rseg_get_space(sys_header,
rseg_id)) {
if (std::find(space_ids, end, space) == end) {
*end++ = space;
}
}
}
mtr.commit();
ut_a(end - space_ids <= TRX_SYS_N_RSEGS);
*end = ULINT_UNDEFINED;
std::sort(space_ids, end);
return ulint(end - space_ids);
}
/** Update the offset information about the end of the binlog entry /** Update the offset information about the end of the binlog entry
which corresponds to the transaction just being committed. which corresponds to the transaction just being committed.
In a replication slave, this updates the master binlog position In a replication slave, this updates the master binlog position
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment