Commit 985ede92 authored by Vlad Lesin's avatar Vlad Lesin

MDEV-20755 InnoDB: Database page corruption on disk or a failed file read of...

MDEV-20755 InnoDB: Database page corruption on disk or a failed file read of tablespace upon prepare of mariabackup incremental backup

The problem:

When incremental backup is taken, delta files are created for innodb tables
which are marked as new tables during innodb ddl tracking. When such
tablespace is tried to be opened during prepare in
xb_delta_open_matching_space(), it is "created", i.e.
xb_space_create_file() is invoked, instead of opening, even if
a tablespace with the same name exists in the base backup directory.

xb_space_create_file() writes page 0 header the tablespace.
This header does not contain crypt data, as mariabackup does not have
any information about crypt data in delta file metadata for
tablespaces.

After delta file is applied, recovery process is started. As the
sequence of recovery for different pages is not defined, there can be
the situation when crypt data redo log event is executed after some
other page is read for recovery. When some page is read for recovery, it's
decrypted using crypt data stored in tablespace header in page 0, if
there is no crypt data, the page is not decryped and does not pass corruption
test.

This causes error for incremental backup --prepare for encrypted
tablespaces.

The error is not stable because crypt data redo log event updates crypt
data on page 0, and recovery for different pages can be executed in
undefined order.

The fix:

When delta file is created, the corresponding write filter copies only
the pages which LSN is greater then some incremental LSN. When new file
is created during incremental backup, the LSN of all it's pages must be
greater then incremental LSN, so there is no need to create delta for
such table, we can just copy it completely.

The fix is to copy the whole file which was tracked during incremental backup
with innodb ddl tracker, and copy it to base directory during --prepare
instead of delta applying.

There is also DBUG_EXECUTE_IF() in innodb code to avoid writing redo log
record for crypt data updating on page 0 to make the test case stable.

Note:

The issue is not reproducible in 10.5 as optimized DDL's are deprecated
in 10.5. But the fix is still useful because it allows to decrease
data copy size during backup, as delta file contains some extra info.
The test case should be removed for 10.5 as it will always pass.
parent cc1646da
This diff is collapsed.
--plugin-load-add=$FILE_KEY_MANAGEMENT_SO
--innodb-file-per-table
--innodb-encryption-threads=4
--innodb-encrypt-tables
--innodb-encrypt-log
--loose-file-key-management
--loose-file-key-management-filename=$MYSQL_TEST_DIR/std_data/keys.txt
SET @old_innodb_log_optimize_ddl=@@global.innodb_log_optimize_ddl;
SET GLOBAL innodb_log_optimize_ddl=ON;
SET @old_debug_dbug=@@global.debug_dbug;
SET GLOBAL debug_dbug="+d,ib_log_checkpoint_avoid";
SET @old_innodb_page_cleaner_disabled_debug=@@global.innodb_page_cleaner_disabled_debug;
SET GLOBAL innodb_page_cleaner_disabled_debug=ON;
CREATE TABLE t1 (c INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1);
# backup
SET GLOBAL debug_dbug="+d,ib_do_not_log_crypt_data";
INSERT INTO t1 VALUES (2);
# incremental backup
# prepare
# incremental prepare
SET GLOBAL innodb_log_optimize_ddl=@old_innodb_log_optimize_ddl;
SET GLOBAL innodb_page_cleaner_disabled_debug=@old_innodb_page_cleaner_disabled_debug;
SET GLOBAL debug_dbug=@old_debug_dbug;
# Restore and check results
# shutdown server
# remove datadir
# xtrabackup move back
# restart server
SELECT count(*) FROM t1;
count(*)
2
DROP TABLE t1;
#
# If MDEV-20755 bug is no fixed, incremental prepare will fail.
#
--source include/have_debug.inc
--source include/have_file_key_management.inc
--let $base_dir=$MYSQLTEST_VARDIR/tmp/backup
--let $inc_dir=$MYSQLTEST_VARDIR/tmp/backup_inc
SET @old_innodb_log_optimize_ddl=@@global.innodb_log_optimize_ddl;
SET GLOBAL innodb_log_optimize_ddl=ON;
# Disable pages flushing to allow redo log records to be executed on --prepare.
SET @old_debug_dbug=@@global.debug_dbug;
SET GLOBAL debug_dbug="+d,ib_log_checkpoint_avoid";
SET @old_innodb_page_cleaner_disabled_debug=@@global.innodb_page_cleaner_disabled_debug;
SET GLOBAL innodb_page_cleaner_disabled_debug=ON;
CREATE TABLE t1 (c INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1);
--echo # backup
--disable_result_log
--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$base_dir
--enable_result_log
# Do not log crypt data to avoid it's execution on --prepare.
SET GLOBAL debug_dbug="+d,ib_do_not_log_crypt_data";
INSERT INTO t1 VALUES (2);
--disable_result_log
# Execute OPTIMIZE TABLE after the table is copied to execute optimized ddl
# callback during backup, which, in turns, will create t1.new file in backup
# directory.
--let after_copy_test_t1=OPTIMIZE TABLE test.t1;
--echo # incremental backup
--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$inc_dir --incremental-basedir=$base_dir --dbug=+d,mariabackup_events
--echo # prepare
--exec $XTRABACKUP --prepare --target-dir=$base_dir
# If the tablespace is created during delta tablespace open procedure, then
# crypt data will be not written, and page corruption test will not pass
# when some redo log event is executed, and --prepare will fail.
--echo # incremental prepare
--exec $XTRABACKUP --prepare --target-dir=$base_dir --incremental-dir=$inc_dir
--enable_result_log
SET GLOBAL innodb_log_optimize_ddl=@old_innodb_log_optimize_ddl;
SET GLOBAL innodb_page_cleaner_disabled_debug=@old_innodb_page_cleaner_disabled_debug;
SET GLOBAL debug_dbug=@old_debug_dbug;
--echo # Restore and check results
--let $targetdir=$base_dir
--source include/restart_and_restore.inc
--enable_result_log
SELECT count(*) FROM t1;
# Cleanup
DROP TABLE t1;
--rmdir $base_dir
--rmdir $inc_dir
......@@ -407,6 +407,8 @@ fil_space_crypt_t::write_page0(
mlog_write_ulint(page + offset + MAGIC_SZ + 2 + len + 8, encryption,
MLOG_1BYTE, mtr);
DBUG_EXECUTE_IF("ib_do_not_log_crypt_data", return;);
byte* log_ptr = mlog_open(mtr, 11 + 17 + len);
if (log_ptr != NULL) {
......
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