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 26, 2017
1 parent 49fe9ba commit afb4615
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 339 deletions.
9 changes: 0 additions & 9 deletions storage/innobase/log/log0recv.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3381,15 +3381,6 @@ void
recv_recovery_from_checkpoint_finish(void)
/*======================================*/
{
/* Apply the hashed log records to the respective file pages */

if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) {

recv_apply_hashed_log_recs(TRUE);
}

DBUG_PRINT("ib_log", ("apply completed"));

if (recv_needed_recovery) {
trx_sys_print_mysql_master_log_pos();
trx_sys_print_mysql_binlog_offset();
Expand Down
252 changes: 91 additions & 161 deletions storage/innobase/srv/srv0start.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2008, Google Inc.
Copyright (c) 2009, Percona Inc.
Copyright (c) 2013, 2017, MariaDB Corporation
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
Expand Down Expand Up @@ -1550,8 +1551,6 @@ innobase_start_or_create_for_mysql(void)
ulint max_arch_log_no;
#endif /* UNIV_LOG_ARCHIVE */
ulint sum_of_new_sizes;
ulint sum_of_data_file_sizes;
ulint tablespace_size_in_header;
dberr_t err;
unsigned i;
ulint srv_n_log_files_found = srv_n_log_files;
Expand All @@ -1564,6 +1563,19 @@ innobase_start_or_create_for_mysql(void)
size_t dirnamelen;
bool sys_datafiles_created = false;

/* Check that os_fast_mutexes work as expected */
os_fast_mutex_init(PFS_NOT_INSTRUMENTED, &srv_os_test_mutex);

ut_a(0 == os_fast_mutex_trylock(&srv_os_test_mutex));

os_fast_mutex_unlock(&srv_os_test_mutex);

os_fast_mutex_lock(&srv_os_test_mutex);

os_fast_mutex_unlock(&srv_os_test_mutex);

os_fast_mutex_free(&srv_os_test_mutex);

high_level_read_only = srv_read_only_mode
|| srv_force_recovery > SRV_FORCE_NO_TRX_UNDO;

Expand Down Expand Up @@ -1606,22 +1618,7 @@ innobase_start_or_create_for_mysql(void)
#endif /* PAGE_ATOMIC_REF_COUNT */
);


if (sizeof(ulint) != sizeof(void*)) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: size of InnoDB's ulint is %lu, "
"but size of void*\n", (ulong) sizeof(ulint));
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: is %lu. The sizes should be the same "
"so that on a 64-bit\n",
(ulong) sizeof(void*));
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: platforms you can allocate more than 4 GB "
"of memory.\n");
}
compile_time_assert(sizeof(ulint) == sizeof(void*));

#ifdef UNIV_DEBUG
ut_print_timestamp(stderr);
Expand Down Expand Up @@ -2332,7 +2329,6 @@ innobase_start_or_create_for_mysql(void)
trx_sys_create();

if (create_new_db) {

ut_a(!srv_read_only_mode);

mtr_start(&mtr);
Expand Down Expand Up @@ -2446,24 +2442,89 @@ innobase_start_or_create_for_mysql(void)
LOG_CHECKPOINT, LSN_MAX,
min_flushed_lsn, max_flushed_lsn);

if (err == DB_SUCCESS) {
/* Initialize the change buffer. */
err = dict_boot();
}

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

return(DB_ERROR);
if (!srv_read_only_mode) {
if (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();
}
}

/* Since the insert buffer init is in dict_boot, and the
insert buffer is needed in any disk i/o, first we call
dict_boot(). Note that trx_sys_init_at_db_start() only needs
to access space 0, and the insert buffer at this stage already
works for space 0. */
const ulint tablespace_size_in_header
= fsp_header_get_tablespace_size();

err = dict_boot();
#ifdef UNIV_DEBUG
/* buf_debug_prints = TRUE; */
#endif /* UNIV_DEBUG */
ulint sum_of_data_file_sizes = 0;

for (ulint d = 0; d < srv_n_data_files; d++) {
sum_of_data_file_sizes += srv_data_file_sizes[d];
}

/* 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_auto_extend_last_data_file
|| sum_of_data_file_sizes
< tablespace_size_in_header) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Tablespace size stored in header is " ULINTPF
" pages, but the sum of data file sizes is "
ULINTPF " pages",
tablespace_size_in_header,
sum_of_data_file_sizes);

if (srv_force_recovery == 0
&& sum_of_data_file_sizes
< tablespace_size_in_header) {
ib_logf(IB_LOG_LEVEL_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.");

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

/* This must precede recv_apply_hashed_log_recs(TRUE). */
ib_bh = 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(). */

recv_apply_hashed_log_recs(TRUE);
DBUG_PRINT("ib_log", ("apply completed"));
}

n_recovered_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list);

/* The purge system needs to create the purge view and
Expand Down Expand Up @@ -2610,20 +2671,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);

#ifdef UNIV_LOG_ARCHIVE
/* Archiving is always off under MySQL */
Expand Down Expand Up @@ -2766,125 +2815,6 @@ innobase_start_or_create_for_mysql(void)
buf_flush_page_cleaner_thread_started = true;
}

#ifdef UNIV_DEBUG
/* buf_debug_prints = TRUE; */
#endif /* UNIV_DEBUG */
sum_of_data_file_sizes = 0;

for (i = 0; i < srv_n_data_files; i++) {
sum_of_data_file_sizes += srv_data_file_sizes[i];
}

tablespace_size_in_header = fsp_header_get_tablespace_size();

if (!srv_read_only_mode
&& !srv_auto_extend_last_data_file
&& sum_of_data_file_sizes != tablespace_size_in_header) {

ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: tablespace size"
" stored in header is %lu pages, but\n",
(ulong) tablespace_size_in_header);
ut_print_timestamp(stderr);
fprintf(stderr,
"InnoDB: the sum of data file sizes is %lu pages\n",
(ulong) sum_of_data_file_sizes);

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 */

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

return(DB_ERROR);
}
}

if (!srv_read_only_mode
&& srv_auto_extend_last_data_file
&& sum_of_data_file_sizes < tablespace_size_in_header) {

ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: tablespace size stored in header"
" is %lu pages, but\n",
(ulong) tablespace_size_in_header);
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: the sum of data file sizes"
" is only %lu pages\n",
(ulong) sum_of_data_file_sizes);

if (srv_force_recovery == 0) {

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

return(DB_ERROR);
}
}

/* Check that os_fast_mutexes work as expected */
os_fast_mutex_init(PFS_NOT_INSTRUMENTED, &srv_os_test_mutex);

if (0 != os_fast_mutex_trylock(&srv_os_test_mutex)) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: pthread_mutex_trylock returns"
" an unexpected value on\n");
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: success! Cannot continue.\n");
exit(1);
}

os_fast_mutex_unlock(&srv_os_test_mutex);

os_fast_mutex_lock(&srv_os_test_mutex);

os_fast_mutex_unlock(&srv_os_test_mutex);

os_fast_mutex_free(&srv_os_test_mutex);

if (srv_print_verbose_log) {
ib_logf(IB_LOG_LEVEL_INFO,
"%s started; log sequence number " LSN_PF "",
Expand Down
9 changes: 0 additions & 9 deletions storage/xtradb/log/log0recv.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3478,15 +3478,6 @@ void
recv_recovery_from_checkpoint_finish(void)
/*======================================*/
{
/* Apply the hashed log records to the respective file pages */

if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) {

recv_apply_hashed_log_recs(TRUE);
}

DBUG_PRINT("ib_log", ("apply completed"));

if (recv_needed_recovery) {
trx_sys_print_mysql_master_log_pos();
trx_sys_print_mysql_binlog_offset();
Expand Down
Loading

0 comments on commit afb4615

Please sign in to comment.