Skip to content

Commit

Permalink
MDEV-11915 Detect InnoDB system tablespace size mismatch early
Browse files Browse the repository at this point in the history
InnoDB would refuse to start up if there is a mismatch on
the size of the system tablespace files. However, before this
check is conducted, the system tablespace may already have been
heavily modified.

InnoDB should perform the size check as early as possible.

recv_recovery_from_checkpoint_finish():
Move the recv_apply_hashed_log_recs() call to
innobase_start_or_create_for_mysql().

innobase_start_or_create_for_mysql(): Test the mutex functionality
before doing anything else. Use a compile_time_assert() for a
sizeof() constraint. Check the size of the system tablespace as
early as possible.
  • Loading branch information
dr-m committed Jan 27, 2017
1 parent 3271da1 commit 406e113
Showing 1 changed file with 68 additions and 100 deletions.
168 changes: 68 additions & 100 deletions storage/innobase/srv/srv0start.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1411,8 +1411,6 @@ innobase_start_or_create_for_mysql(void)
{
bool create_new_db = false;
lsn_t flushed_lsn;
ulint sum_of_data_file_sizes;
ulint tablespace_size_in_header;
dberr_t err;
ulint srv_n_log_files_found = srv_n_log_files;
mtr_t mtr;
Expand Down Expand Up @@ -1447,13 +1445,7 @@ innobase_start_or_create_for_mysql(void)
}
#endif /* HAVE_LZO1X */

if (sizeof(ulint) != sizeof(void*)) {
ib::error() << "Size of InnoDB's ulint is " << sizeof(ulint)
<< ", but size of void* is " << sizeof(void*)
<< ". The sizes should be the same so that on"
" a 64-bit platforms you can allocate more than 4 GB"
" of memory.";
}
compile_time_assert(sizeof(ulint) == sizeof(void*));

#ifdef UNIV_DEBUG
ib::info() << "!!!!!!!! UNIV_DEBUG switched on !!!!!!!!!";
Expand Down Expand Up @@ -2089,8 +2081,6 @@ innobase_start_or_create_for_mysql(void)
trx_sys_create();

if (create_new_db) {
dberr_t err = DB_SUCCESS;

ut_a(!srv_read_only_mode);

mtr_start(&mtr);
Expand Down Expand Up @@ -2182,23 +2172,25 @@ innobase_start_or_create_for_mysql(void)
if (err == DB_SUCCESS) {
/* Initialize the change buffer. */
err = dict_boot();
} else {
}

if (err != DB_SUCCESS) {
return(srv_init_abort(err));
}

/* This must precede recv_apply_hashed_log_recs(TRUE). */
purge_queue = trx_sys_init_at_db_start();

if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) {
/* Apply the hashed log records to the
respective file pages, for the last batch of
recv_group_scan_log_recs(). */

dberr_t err = recv_apply_hashed_log_recs(TRUE);
err = recv_apply_hashed_log_recs(TRUE);
DBUG_PRINT("ib_log", ("apply completed"));

if (err != DB_SUCCESS) {
ib::warn() << "recv_apply_hashed_log_recs "
<< " failed with error " << err;
return(srv_init_abort(err));
}

if (recv_needed_recovery) {
Expand All @@ -2217,6 +2209,65 @@ innobase_start_or_create_for_mysql(void)
" InnoDB database from a backup!";
}

if (!srv_read_only_mode) {
const ulint flags = FSP_FLAGS_PAGE_SSIZE();
for (ulint id = 0; id <= srv_undo_tablespaces; id++) {
if (fil_space_get(id)) {
fsp_flags_try_adjust(id, flags);
}
}

if (sum_of_new_sizes > 0) {
/* New data file(s) were added */
mtr.start();
fsp_header_inc_size(0, sum_of_new_sizes, &mtr);
mtr.commit();
/* Immediately write the log record about
increased tablespace size to disk, so that it
is durable even if mysqld would crash
quickly */
log_buffer_flush_to_disk();
}
}

const ulint tablespace_size_in_header
= fsp_header_get_tablespace_size();
const ulint sum_of_data_file_sizes
= srv_sys_space.get_sum_of_sizes();
/* Compare the system tablespace file size to what is
stored in FSP_SIZE. In open_or_create_data_files()
we already checked that the file sizes match the
innodb_data_file_path specification. */
if (srv_read_only_mode
|| sum_of_data_file_sizes == tablespace_size_in_header) {
/* Do not complain about the size. */
} else if (!srv_sys_space.can_auto_extend_last_file()
|| sum_of_data_file_sizes
< tablespace_size_in_header) {
ib::error() << "Tablespace size stored in header is "
<< tablespace_size_in_header
<< " pages, but the sum of data file sizes is "
<< sum_of_data_file_sizes << " pages";

if (srv_force_recovery == 0
&& sum_of_data_file_sizes
< tablespace_size_in_header) {
ib::error() <<
"Cannot start InnoDB. The tail of"
" the system tablespace is"
" missing. Have you edited"
" innodb_data_file_path in my.cnf"
" in an inappropriate way, removing"
" data files from there?"
" You can set innodb_force_recovery=1"
" in my.cnf to force"
" a startup if you are trying to"
" recover a badly corrupt database.";

return(srv_init_abort(DB_ERROR));
}
}

/* The purge system needs to create the purge view and
therefore requires that the trx_sys is inited. */

Expand Down Expand Up @@ -2285,8 +2336,6 @@ innobase_start_or_create_for_mysql(void)
&& !recv_sys->found_corrupt_log
&& (srv_log_file_size_requested != srv_log_file_size
|| srv_n_log_files_found != srv_n_log_files)) {
dberr_t err = DB_SUCCESS;

/* Prepare to replace the redo log files. */

if (srv_read_only_mode) {
Expand Down Expand Up @@ -2352,20 +2401,8 @@ innobase_start_or_create_for_mysql(void)
trx_sys_file_format_tag_init();
}

if (!create_new_db && sum_of_new_sizes > 0) {
/* New data file(s) were added */
mtr_start(&mtr);

fsp_header_inc_size(0, sum_of_new_sizes, &mtr);

mtr_commit(&mtr);

/* Immediately write the log record about increased tablespace
size to disk, so that it is durable even if mysqld would crash
quickly */

log_buffer_flush_to_disk();
}
ut_ad(err == DB_SUCCESS);
ut_a(sum_of_new_sizes != ULINT_UNDEFINED);

/* Open temp-tablespace and keep it open until shutdown. */

Expand Down Expand Up @@ -2417,13 +2454,6 @@ innobase_start_or_create_for_mysql(void)
srv_startup_is_before_trx_rollback_phase = false;

if (!srv_read_only_mode) {
const ulint flags = FSP_FLAGS_PAGE_SSIZE();
for (ulint id = 0; id <= srv_undo_tablespaces; id++) {
if (fil_space_get(id)) {
fsp_flags_try_adjust(id, flags);
}
}

/* Create the thread which watches the timeouts
for lock waits */
thread_handles[2 + SRV_MAX_N_IO_THREADS] = os_thread_create(
Expand Down Expand Up @@ -2528,68 +2558,6 @@ innobase_start_or_create_for_mysql(void)
}
}

sum_of_data_file_sizes = srv_sys_space.get_sum_of_sizes();
ut_a(sum_of_new_sizes != ULINT_UNDEFINED);

tablespace_size_in_header = fsp_header_get_tablespace_size();

if (!srv_read_only_mode
&& !srv_sys_space.can_auto_extend_last_file()
&& sum_of_data_file_sizes != tablespace_size_in_header) {

ib::error() << "Tablespace size stored in header is "
<< tablespace_size_in_header << " pages, but the sum"
" of data file sizes is " << sum_of_data_file_sizes
<< " pages";

if (srv_force_recovery == 0
&& sum_of_data_file_sizes < tablespace_size_in_header) {
/* This is a fatal error, the tail of a tablespace is
missing */

ib::error()
<< "Cannot start InnoDB."
" The tail of the system tablespace is"
" missing. Have you edited"
" innodb_data_file_path in my.cnf in an"
" inappropriate way, removing"
" ibdata files from there?"
" You can set innodb_force_recovery=1"
" in my.cnf to force"
" a startup if you are trying"
" to recover a badly corrupt database.";

return(srv_init_abort(DB_ERROR));
}
}

if (!srv_read_only_mode
&& srv_sys_space.can_auto_extend_last_file()
&& sum_of_data_file_sizes < tablespace_size_in_header) {

ib::error() << "Tablespace size stored in header is "
<< tablespace_size_in_header << " pages, but the sum"
" of data file sizes is only "
<< sum_of_data_file_sizes << " pages";

if (srv_force_recovery == 0) {

ib::error()
<< "Cannot start InnoDB. The tail of"
" the system tablespace is"
" missing. Have you edited"
" innodb_data_file_path in my.cnf in an"
" InnoDB: inappropriate way, removing"
" ibdata files from there?"
" You can set innodb_force_recovery=1"
" in my.cnf to force"
" InnoDB: a startup if you are trying to"
" recover a badly corrupt database.";

return(srv_init_abort(DB_ERROR));
}
}

if (srv_print_verbose_log) {
ib::info() << INNODB_VERSION_STR
<< " started; log sequence number "
Expand Down

0 comments on commit 406e113

Please sign in to comment.