Skip to content

Commit

Permalink
WL#8845 Implement an InnoDB redo log format version identifier
Browse files Browse the repository at this point in the history
InnoDB has several times changed its redo log format by introducing
new redo log record types. Format changes would lead to misleading
reports of redo log corruption when processing individual redo log
records.

In the redo log header (start of the ib_logfile0 file), we will introduce
a format version identifier and textual representation of the software
version that created the redo log files.

Furthermore, we change the checksum of redo log checkpoint pages, so that
older versions of MySQL will refuse to start up on redo log files that
were created with a MySQL server that includes this fix.

We will also remove a number of unused fields from the redo log
header and checkpoint pages (pages 0, 1, and 3).

Some tests will be expanded, because with this fix,
the server must refuse to start up with older redo log files,
unless they are dirty.

We will also replace the configuration parameter
innodb_log_checksum_algorithm with the Boolean parameter
innodb_log_checksums.
We make CRC-32C the only checksum on the InnoDB redo log pages when
innodb_log_checksums=ON (the default). Checksums on the header page
and the checkpoint pages are never disabled.

innodb_log_checksums_func_update(), innodb_log_checksums_update():
Update triggers for the new global Boolean variable innodb_log_checksums.

innodb_log_checksum_func_update(), innodb_log_checksum_algorithm_update():
Removed along with the global parameter innodb_log_checksum_algorithm.

Removed definitions:
LOG_MAX_N_GROUPS
log_group_read_checkpoint_info()
log_checkpoint_get_nth_group_info()
log_checkpoint_set_nth_group_info()
log_block_calc_checksum_innodb()
log_block_calc_checksum_crc32_legacy_big_endian()
recv_check_cp_is_consistent()
log_block_checksum_weak_validation()
log_block_checksum_what_matches()
log_block_checksum_fail_fatal()
log_block_checksum_is_ok_or_old_format()
LOG_CHECKPOINT_OFFSET_LOW32
LOG_CHECKPOINT_ARCHIVED_LSN
LOG_CHECKPOINT_GROUP_ARRAY
LOG_CHECKPOINT_ARCHIVED_FILE_NO
LOG_CHECKPOINT_ARCHIVED_OFFSET
LOG_CHECKPOINT_ARRAY_END
LOG_CHECKPOINT_CHECKSUM_1
LOG_CHECKPOINT_CHECKSUM_2
LOG_CHECKPOINT_FSP_FREE_LIMIT
LOG_CHECKPOINT_FSP_MAGIC_N
LOG_CHECKPOINT_FSP_MAGIC_N_VAL
LOG_CHECKPOINT_OFFSET_HIGH32
LOG_CHECKPOINT_SIZE
LOG_GROUP_ID
LOG_FILE_START_LSN
LOG_FILE_NO
LOG_FILE_WAS_CREATED_BY_HOT_BACKUP
LOG_FILE_ARCH_COMPLETED
LOG_FILE_END_LSN

Changed definitions:
LOG_CHECKPOINT_LOG_BUF_SIZE

Added definitions:
innodb_log_checksums: The current value of the SET GLOBAL variable.
LOG_CHECKPOINT_OFFSET
LOG_HEADER_FORMAT (repurposing LOG_GROUP_ID which was always 0)
LOG_HEADER_PAD1 (unused 4 bytes, zero-initialized)
LOG_HEADER_START_LSN
LOG_HEADER_CREATOR (renamed from LOG_FILE_WAS_CREATED_BY_HOT_BACKUP)
LOG_HEADER_CREATOR_END
LOG_HEADER_CREATOR_CURRENT
LOG_HEADER_FORMAT_CURRENT
log_group_t::format

log_block_calc_checksum_format_0(): Renamed from
log_block_calc_checksum_innodb(). This is only used when upgrading the
redo log from non-tagged format.

recv_find_max_checkpoint_0(): New function, used when upgrading the
redo log from non-tagged format.

recv_log_format_0_recover(): New function, used when upgrading the
redo log from non-tagged format. Checks if the redo log is clean.

log_group_header_read(): Replaces log_group_read_checkpoint_info().
Also used for reading the log header page (page 0).

recv_check_log_header_checksum(): Replaces recv_check_cp_is_consistent().
Also used for checking the log header page (page 0).

log_block_checksum_is_ok(): Checks a log block checksum. It must either
be CRC-32C, or we must have innodb_log_checksums=OFF.

Changed functions:

log_group_file_header_flush(): Always zero-initialize the buffer
and initialize all LOG_HEADER_ fields.

log_group_checkpoint(): Zero-initialize the checkpoint buffer,
and write the checkpoint in the new format, with CRC-32C checksum.

recv_find_max_checkpoint(): Support both the old log format
(if the old-format redo log is logically empty) and the new format.

recv_scan_log_recs(): Clean up the logic a bit. Display a message
when encountering (and terminating log parsing due to) invalid
log blocks. For now, keep the existing behaviour and do not display
a message about log block header mismatch.

recv_recovery_from_checkpoint_start(): Replace the whole "ibbackup" label.
Check if a log upgrade or a normal recovery is needed.

srv_prepare_to_delete_redo_log_files(): Display a message about
upgrading the redo log.

Other changes to startup:

If we are going to upgrade the redo log, we must avoid writing any
new redo log records before we have replaced the redo log.

dict_check_sys_tablespaces(), dict_check_sys_tables(): Avoid
updating SYS_DATAFILES if we are going to upgrade the redo log.

dict_create_or_check_sys_virtual(): Do not modify anything if we are
running in --innodb-read-only or --innodb-force-recovery=6 mode.

RB: 10096
Reviewed-by: Kevin Lewis <kevin.lewis@oracle.com>
Reviewed-by: Annamalai Gurusami <annamalai.gurusami@oracle.com>
  • Loading branch information
Marko Mäkelä committed Sep 11, 2015
1 parent a68e844 commit af0aced
Show file tree
Hide file tree
Showing 27 changed files with 652 additions and 674 deletions.
13 changes: 13 additions & 0 deletions mysql-test/suite/innodb/r/log_corruption.result
@@ -1 +1,14 @@
# redo log from before MySQL 5.7.9
# redo log from before MySQL 5.7.9, with corrupted log checkpoint
# redo log from before MySQL 5.7.9, with corrupted log block
# redo log from "after" MySQL 5.7.9, but with invalid header checksum
# distant future redo log format, with valid header checksum
# valid header, but old-format checkpoint blocks
# valid header, valid checkpoint 1, all-zero (invalid) checkpoint 2, invalid block checksum
# --innodb-force-recovery=6 (skip the entire redo log)
# valid header, valid checkpoint 1, all-zero (invalid) checkpoint 2, invalid block number
# --innodb-force-recovery=6 (skip the entire redo log)
# Test a corrupted MLOG_FILE_NAME record.
# valid header, valid checkpoint 1, all-zero (invalid) checkpoint 2
# Test a corrupted MLOG_FILE_NAME record.
# valid header, invalid checkpoint 1, valid checkpoint 2
117 changes: 109 additions & 8 deletions mysql-test/suite/innodb/t/log_corruption.test
@@ -1,18 +1,119 @@
--source include/have_innodb.inc
--source include/have_innodb_16k.inc

--echo # Test a corrupted MLOG_FILE_NAME record.

let newdir= $MYSQLTEST_VARDIR/tmp/innodb-log-debug;
--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect

let newdir= $MYSQLTEST_VARDIR/tmp/log_corruption;
--mkdir $newdir
let SEARCH_FILE = $newdir/my_restart.err;
let $args=--no-defaults --datadir=$newdir --secure-file-priv="" --loose-skip-sha256-password-auto-generate-rsa-keys --loose-console > $SEARCH_FILE 2>&1 ;

--echo # redo log from before MySQL 5.7.9
--exec unzip $MYSQL_TEST_DIR/suite/innodb/t/log_corruption.zip -d $newdir > $SEARCH_FILE
let $args=--no-defaults --datadir=$newdir --secure-file-priv="" --loose-console > $SEARCH_FILE 2>&1 ;
# On Windows, the server seems to return 0 when an InnoDB assertion fails.
--error 0,1
--error 1
--exec $MYSQLD $args
let SEARCH_PATTERN=InnoDB: Upgrade after a crash is not supported. This redo log was created before MySQL 5\\.7\\.9\\.;
--source include/search_pattern_in_file.inc

--echo # redo log from before MySQL 5.7.9, with corrupted log checkpoint
--remove_file $newdir/ib_logfile0
--copy_file $newdir/ib_logfile1 $newdir/ib_logfile0
--error 1
--exec $MYSQLD $args
let SEARCH_PATTERN=InnoDB: Upgrade after a crash is not supported. This redo log was created before MySQL 5\\.7\\.9, and we did not find a valid checkpoint;
--source include/search_pattern_in_file.inc

--echo # redo log from before MySQL 5.7.9, with corrupted log block
--remove_file $newdir/ib_logfile0
--exec unzip $MYSQL_TEST_DIR/suite/innodb/t/log_corruption0.zip -d $newdir > $SEARCH_FILE
--error 1
--exec $MYSQLD $args
let SEARCH_PATTERN=InnoDB: Upgrade after a crash is not supported. This redo log was created before MySQL 5\\.7\\.9, and it appears corrupted;
--source include/search_pattern_in_file.inc

--echo # redo log from "after" MySQL 5.7.9, but with invalid header checksum
--remove_file $newdir/ib_logfile0
--exec unzip $MYSQL_TEST_DIR/suite/innodb/t/log_corruption1.zip -d $newdir > $SEARCH_FILE
--error 1
--exec $MYSQLD $args
let SEARCH_PATTERN=InnoDB: Invalid redo log header checksum;
--source include/search_pattern_in_file.inc

--echo # distant future redo log format, with valid header checksum
--remove_file $newdir/ib_logfile0
--exec unzip $MYSQL_TEST_DIR/suite/innodb/t/log_corruption2.zip -d $newdir > $SEARCH_FILE
--error 1
--exec $MYSQLD $args
let SEARCH_PATTERN=InnoDB: Unsupported redo log format. The redo log was created with malicious intentions, or perhaps\. Please follow the instructions at http://dev.mysql.com/doc/refman/5.7/en/upgrading-downgrading.html;
--source include/search_pattern_in_file.inc

--echo # valid header, but old-format checkpoint blocks
--remove_file $newdir/ib_logfile0
--exec unzip $MYSQL_TEST_DIR/suite/innodb/t/log_corruption3.zip -d $newdir > $SEARCH_FILE
--error 1
--exec $MYSQLD $args
let SEARCH_PATTERN=InnoDB: No valid checkpoint found .corrupted redo log;
--source include/search_pattern_in_file.inc

--echo # valid header, valid checkpoint 1, all-zero (invalid) checkpoint 2, invalid block checksum
--remove_file $newdir/ib_logfile0
--exec unzip $MYSQL_TEST_DIR/suite/innodb/t/log_corruption4.zip -d $newdir > $SEARCH_FILE
# Anything below innodb_force_recovery=6 must find a valid redo log.
# Missing tablespace files are tolerated already with innodb_force_recovery=1.
--error 1
--exec $MYSQLD $args --innodb-force-recovery=5
let SEARCH_PATTERN=InnoDB: Log block 2372 at lsn 1213952 has valid header, but checksum field contains 144444122, should be 3362026715;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=InnoDB: Ignoring the redo log due to missing MLOG_CHECKPOINT between the checkpoint 1213964 and the end 1213952\.;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=Plugin 'InnoDB' registration as a STORAGE ENGINE failed;
--source include/search_pattern_in_file.inc
--echo # --innodb-force-recovery=6 (skip the entire redo log)
--error 1
--exec $MYSQLD $args --innodb-force-recovery=6
let SEARCH_PATTERN=InnoDB: Cannot create sys_virtual system tables. running in read-only mode;
--source include/search_pattern_in_file.inc

--echo # valid header, valid checkpoint 1, all-zero (invalid) checkpoint 2, invalid block number
--remove_file $newdir/ib_logfile0
--exec unzip $MYSQL_TEST_DIR/suite/innodb/t/log_corruption4a.zip -d $newdir > $SEARCH_FILE
# Anything below innodb_force_recovery=6 must find a valid redo log.
# Missing tablespace files are tolerated already with innodb_force_recovery=1.
--error 1
--exec $MYSQLD $args --innodb-force-recovery=5
let SEARCH_PATTERN=InnoDB: Ignoring the redo log due to missing MLOG_CHECKPOINT between the checkpoint 1213964 and the end 1213952\.;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=Plugin 'InnoDB' registration as a STORAGE ENGINE failed;
--source include/search_pattern_in_file.inc
--echo # --innodb-force-recovery=6 (skip the entire redo log)
--error 1
--exec $MYSQLD $args --innodb-force-recovery=6
let SEARCH_PATTERN=InnoDB: Cannot create sys_virtual system tables. running in read-only mode;
--source include/search_pattern_in_file.inc

--echo # Test a corrupted MLOG_FILE_NAME record.
--echo # valid header, valid checkpoint 1, all-zero (invalid) checkpoint 2
--remove_file $newdir/ib_logfile0
--exec unzip $MYSQL_TEST_DIR/suite/innodb/t/log_corruption5.zip -d $newdir > $SEARCH_FILE
--error 1
--exec $MYSQLD $args
let SEARCH_PATTERN=InnoDB: Log scan progressed past the checkpoint lsn 1213964;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=InnoDB: ############### CORRUPT LOG RECORD FOUND ##################;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=InnoDB: Log record type 55, page 151:488\. Log parsing proceeded successfully up to 1213973\. Previous log record type 56, is multi 0 Recv offset 9, prev 0;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN= len 22. hex 38000000000012860cb7809781e80006626f67757300. asc 8 bogus ;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=InnoDB: Set innodb_force_recovery to ignore this error;
--source include/search_pattern_in_file.inc

--echo # Test a corrupted MLOG_FILE_NAME record.
--echo # valid header, invalid checkpoint 1, valid checkpoint 2
--remove_file $newdir/ib_logfile0
--exec unzip $MYSQL_TEST_DIR/suite/innodb/t/log_corruption6.zip -d $newdir > $SEARCH_FILE
--error 1
--exec $MYSQLD $args
let SEARCH_PATTERN=InnoDB: The log file was created by mysqlbackup --apply-log at ibbackup was here!!!1!\. The following crash recovery is part of a normal restore\.;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=InnoDB: ############### CORRUPT LOG RECORD FOUND ##################;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=InnoDB: Log record type 55, page 151:488\. Log parsing proceeded successfully up to 1213973\. Previous log record type 56, is multi 0 Recv offset 9, prev 0;
Expand Down
Binary file modified mysql-test/suite/innodb/t/log_corruption.zip
Binary file not shown.
Binary file added mysql-test/suite/innodb/t/log_corruption0.zip
Binary file not shown.
Binary file added mysql-test/suite/innodb/t/log_corruption1.zip
Binary file not shown.
Binary file added mysql-test/suite/innodb/t/log_corruption2.zip
Binary file not shown.
Binary file added mysql-test/suite/innodb/t/log_corruption3.zip
Binary file not shown.
Binary file added mysql-test/suite/innodb/t/log_corruption4.zip
Binary file not shown.
Binary file added mysql-test/suite/innodb/t/log_corruption4a.zip
Binary file not shown.
Binary file added mysql-test/suite/innodb/t/log_corruption5.zip
Binary file not shown.
Binary file added mysql-test/suite/innodb/t/log_corruption6.zip
Binary file not shown.
2 changes: 1 addition & 1 deletion mysql-test/suite/perfschema/t/show_sanity.test
Expand Up @@ -458,7 +458,7 @@ insert into test.sanity values
("JUNK: GLOBAL-ONLY", "I_S.SESSION_VARIABLES", "INNODB_LOCKS_UNSAFE_FOR_BINLOG"),
("JUNK: GLOBAL-ONLY", "I_S.SESSION_VARIABLES", "INNODB_LOG_BUFFER_SIZE"),
("JUNK: GLOBAL-ONLY", "I_S.SESSION_VARIABLES", "INNODB_LOG_CHECKPOINT_NOW"),
("JUNK: GLOBAL-ONLY", "I_S.SESSION_VARIABLES", "INNODB_LOG_CHECKSUM_ALGORITHM"),
("JUNK: GLOBAL-ONLY", "I_S.SESSION_VARIABLES", "INNODB_LOG_CHECKSUMS"),
("JUNK: GLOBAL-ONLY", "I_S.SESSION_VARIABLES", "INNODB_LOG_COMPRESSED_PAGES"),
("JUNK: GLOBAL-ONLY", "I_S.SESSION_VARIABLES", "INNODB_LOG_FILES_IN_GROUP"),
("JUNK: GLOBAL-ONLY", "I_S.SESSION_VARIABLES", "INNODB_LOG_FILE_SIZE"),
Expand Down

This file was deleted.

42 changes: 42 additions & 0 deletions mysql-test/suite/sys_vars/r/innodb_log_checksums_basic.result
@@ -0,0 +1,42 @@
SET @orig = @@global.innodb_log_checksums;
SELECT @orig;
@orig
1
SET GLOBAL innodb_log_checksums = 'crc32';
ERROR 42000: Variable 'innodb_log_checksums' can't be set to the value of 'crc32'
SELECT @@global.innodb_log_checksums;
@@global.innodb_log_checksums
1
SET GLOBAL innodb_log_checksums = 2;
ERROR 42000: Variable 'innodb_log_checksums' can't be set to the value of '2'
SELECT @@global.innodb_log_checksums;
@@global.innodb_log_checksums
1
SET GLOBAL innodb_log_checksums = 1e2;
ERROR 42000: Incorrect argument type to variable 'innodb_log_checksums'
SELECT @@global.innodb_log_checksums;
@@global.innodb_log_checksums
1
SET GLOBAL innodb_log_checksums = 1.0;
ERROR 42000: Incorrect argument type to variable 'innodb_log_checksums'
SELECT @@global.innodb_log_checksums;
@@global.innodb_log_checksums
1
SET innodb_log_checksums = OFF;
ERROR HY000: Variable 'innodb_log_checksums' is a GLOBAL variable and should be set with SET GLOBAL
SELECT @@global.innodb_log_checksums;
@@global.innodb_log_checksums
1
SET GLOBAL innodb_log_checksums = OFF;
SELECT @@global.innodb_log_checksums;
@@global.innodb_log_checksums
0
SET GLOBAL innodb_log_checksums = default;
SET GLOBAL innodb_log_checksums = ON;
SELECT @@global.innodb_log_checksums;
@@global.innodb_log_checksums
1
SET GLOBAL innodb_log_checksums = @orig;
SELECT @@global.innodb_log_checksums;
@@global.innodb_log_checksums
1

This file was deleted.

36 changes: 36 additions & 0 deletions mysql-test/suite/sys_vars/t/innodb_log_checksums_basic.test
@@ -0,0 +1,36 @@
--source include/have_innodb.inc

# Check the default value
SET @orig = @@global.innodb_log_checksums;
SELECT @orig;

-- error ER_WRONG_VALUE_FOR_VAR
SET GLOBAL innodb_log_checksums = 'crc32';
SELECT @@global.innodb_log_checksums;

-- error ER_WRONG_VALUE_FOR_VAR
SET GLOBAL innodb_log_checksums = 2;
SELECT @@global.innodb_log_checksums;

-- error ER_WRONG_TYPE_FOR_VAR
SET GLOBAL innodb_log_checksums = 1e2;
SELECT @@global.innodb_log_checksums;

-- error ER_WRONG_TYPE_FOR_VAR
SET GLOBAL innodb_log_checksums = 1.0;
SELECT @@global.innodb_log_checksums;

-- error ER_GLOBAL_VARIABLE
SET innodb_log_checksums = OFF;
SELECT @@global.innodb_log_checksums;

SET GLOBAL innodb_log_checksums = OFF;
SELECT @@global.innodb_log_checksums;

SET GLOBAL innodb_log_checksums = default;

SET GLOBAL innodb_log_checksums = ON;
SELECT @@global.innodb_log_checksums;

SET GLOBAL innodb_log_checksums = @orig;
SELECT @@global.innodb_log_checksums;
7 changes: 7 additions & 0 deletions storage/innobase/dict/dict0crea.cc
Expand Up @@ -1892,6 +1892,13 @@ dict_create_or_check_sys_virtual()
return(DB_SUCCESS);
}

if (srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO
|| srv_read_only_mode) {
ib::error() << "Cannot create sys_virtual system tables;"
" running in read-only mode.";
return(DB_ERROR);
}

trx = trx_allocate_for_mysql();

trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
Expand Down
4 changes: 2 additions & 2 deletions storage/innobase/dict/dict0load.cc
Expand Up @@ -1218,7 +1218,7 @@ dict_check_sys_tablespaces(
/* Check that the .ibd file exists. */
dberr_t err = fil_ibd_open(
validate,
!srv_read_only_mode,
!srv_read_only_mode && srv_log_file_size != 0,
FIL_TYPE_TABLESPACE,
space_id,
fsp_flags,
Expand Down Expand Up @@ -1469,7 +1469,7 @@ dict_check_sys_tables(
ulint fsp_flags = dict_tf_to_fsp_flags(flags, is_temp);
dberr_t err = fil_ibd_open(
validate,
!srv_read_only_mode,
!srv_read_only_mode && srv_log_file_size != 0,
FIL_TYPE_TABLESPACE,
space_id,
fsp_flags,
Expand Down
2 changes: 2 additions & 0 deletions storage/innobase/fil/fil0fil.cc
Expand Up @@ -3716,6 +3716,8 @@ fil_ibd_open(
ut_ad(!fix_dict || rw_lock_own(dict_operation_lock, RW_LOCK_X));

ut_ad(!fix_dict || mutex_own(&dict_sys->mutex));
ut_ad(!fix_dict || !srv_read_only_mode);
ut_ad(!fix_dict || srv_log_file_size != 0);
ut_ad(fil_type_is_data(purpose));

if (!fsp_flags_is_valid(flags)) {
Expand Down

0 comments on commit af0aced

Please sign in to comment.