Skip to content

Commit eca552a

Browse files
committed
MDEV-34830: LSN in the future is not being treated as serious corruption
The invariant of write-ahead logging is that before any change to a page is written to the data file, the corresponding log record must must first have been durably written. In crash recovery, there were some sloppy checks for this. Let us implement accurate checks and flag an inconsistency as a hard error, so that we can avoid further corruption of a corrupted database. For data extraction from the corrupted database, innodb_force_recovery can be used. Before recovery is reading any data pages or invoking buf_dblwr_t::recover() to recover torn pages from the doublewrite buffer, InnoDB will have parsed the log until the final LSN and updated log_sys.lsn to that. So, we can rely on log_sys.lsn at all times. The doublewrite buffer recovery has been refactored in such a way that the recv_sys.dblwr.pages may be consulted while discovering files and their page sizes, but nothing will be written back to data files before buf_dblwr_t::recover() is invoked. recv_max_page_lsn, recv_lsn_checks_on: Remove. recv_sys_t::validate_checkpoint(): Validate the write-ahead-logging condition at the end of the recovery. recv_dblwr_t::validate_page(): Keep track of the maximum LSN (if we are checking a non-doublewrite copy of a page) but do not complain LSN being in the future. The doublewrite buffer is a special case, because it will be read early during recovery. Besides, starting with commit 762bcb8 the dblwr=true copies of pages may legitimately be "too new". recv_dblwr_t::find_page(): Find a valid page with the smallest FIL_PAGE_LSN that is in the valid range for recovery. recv_dblwr_t::restore_first_page(): Replaced by find_page(). Only buf_dblwr_t::recover() will write to data files. buf_dblwr_t::recover(): Simplify the message output. Do attempt doublewrite recovery on user page read error. Ignore doublewrite pages whose FIL_PAGE_LSN is outside the usable bounds. Previously, we could wrongly recover a too new page from the doublewrite buffer. It is unlikely that this could have lead to an actual error. Write back all recovered pages from the doublewrite buffer here, including for the first page of any tablespace. buf_page_is_corrupted(): Distinguish the return values CORRUPTED_FUTURE_LSN and CORRUPTED_OTHER. buf_page_check_corrupt(): Return the error code DB_CORRUPTION in case the LSN is in the future. Datafile::read_first_page_flags(): Split from read_first_page(). Take a copy of the first page as a parameter. recv_sys_t::free_corrupted_page(): Take the file as a parameter and return whether a message was displayed. This avoids some duplicated and incomplete error messages. buf_page_t::read_complete(): Remove some redundant output and always display the name of the corrupted file. Never return DB_FAIL; use it only in internal error handling. IORequest::read_complete(): Assume that buf_page_t::read_complete() will have reported any error. fil_space_t::set_corrupted(): Return whether this is the first time the tablespace had been flagged as corrupted. Datafile::validate_first_page(), fil_node_open_file_low(), fil_node_open_file(), fil_space_t::read_page0(), fil_node_t::read_page0(): Add a parameter for a copy of the first page, and a parameter to indicate whether the FIL_PAGE_LSN check should be suppressed. Before buf_dblwr_t::recover() is invoked, we cannot validate the FIL_PAGE_LSN, but we can trust the FSP_SPACE_FLAGS and the tablespace ID that may be present in a potentially too new copy of a page. Reviewed by: Debarun Banerjee
1 parent 3b58c6b commit eca552a

File tree

61 files changed

+806
-554
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+806
-554
lines changed

extra/innochecksum.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ static bool is_page_corrupted(byte *buf, bool is_encrypted, uint32_t flags)
517517
normal method. */
518518
if (is_encrypted && key_version != 0) {
519519
is_corrupted = use_full_crc32
520-
? buf_page_is_corrupted(true, buf, flags)
520+
? !!buf_page_is_corrupted(false, buf, flags)
521521
: !fil_space_verify_crypt_checksum(buf, zip_size);
522522

523523
if (is_corrupted && log_file) {

extra/mariabackup/fil_cur.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ static bool page_is_corrupted(const byte *page, ulint page_no,
283283
}
284284

285285
if (space->full_crc32()) {
286-
return buf_page_is_corrupted(true, page, space->flags);
286+
return buf_page_is_corrupted(false, page, space->flags);
287287
}
288288

289289
/* Validate encrypted pages. The first page is never encrypted.
@@ -316,7 +316,7 @@ static bool page_is_corrupted(const byte *page, ulint page_no,
316316
}
317317

318318
if (page_type != FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
319-
return buf_page_is_corrupted(true, tmp_page,
319+
return buf_page_is_corrupted(false, tmp_page,
320320
space->flags);
321321
}
322322
}
@@ -336,11 +336,11 @@ static bool page_is_corrupted(const byte *page, ulint page_no,
336336
&& cursor->zip_size)
337337
|| page_type == FIL_PAGE_PAGE_COMPRESSED
338338
|| page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED
339-
|| buf_page_is_corrupted(true, tmp_page,
339+
|| buf_page_is_corrupted(false, tmp_page,
340340
space->flags));
341341
}
342342

343-
return buf_page_is_corrupted(true, page, space->flags);
343+
return buf_page_is_corrupted(false, page, space->flags);
344344
}
345345
PRAGMA_REENABLE_CHECK_STACK_FRAME
346346

extra/mariabackup/xtrabackup.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3912,7 +3912,7 @@ static void xb_load_single_table_tablespace(const char *dirname,
39123912

39133913
for (int i = 0; i < 10; i++) {
39143914
file->m_defer = false;
3915-
err = file->validate_first_page();
3915+
err = file->validate_first_page(file->get_first_page());
39163916

39173917
if (file->m_defer) {
39183918
if (defer_space_id) {
@@ -3954,7 +3954,7 @@ static void xb_load_single_table_tablespace(const char *dirname,
39543954
skip_node_page0 ? file->detach() : pfs_os_file_t(),
39553955
0, false, false);
39563956
node->deferred= defer;
3957-
if (!space->read_page0())
3957+
if (!space->read_page0(nullptr, true))
39583958
err = DB_CANNOT_OPEN_FILE;
39593959
mysql_mutex_unlock(&fil_system.mutex);
39603960

@@ -6852,8 +6852,10 @@ static bool xtrabackup_prepare_func(char** argv)
68526852
goto error;
68536853
}
68546854

6855-
ok = fil_system.sys_space->open(false)
6856-
&& xtrabackup_apply_deltas();
6855+
mysql_mutex_lock(&recv_sys.mutex);
6856+
ok = fil_system.sys_space->open(false);
6857+
mysql_mutex_unlock(&recv_sys.mutex);
6858+
if (ok) ok = xtrabackup_apply_deltas();
68576859

68586860
xb_data_files_close();
68596861

mysql-test/suite/encryption/r/innodb-bad-key-change.result

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ call mtr.add_suppression("Plugin 'file_key_management' registration.*failed");
33
call mtr.add_suppression("InnoDB: Table `test`\\.`t[12]` has an unreadable root page");
44
call mtr.add_suppression("Table .*t[12].* is corrupted");
55
call mtr.add_suppression("InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=[1-9][0-9]*\\] in file '.*test.t[12]\\.ibd' cannot be decrypted; key_version=1");
6-
call mtr.add_suppression("failed to read \\[page id: space=[1-9][0-9]*, page number=[1-9][0-9]*\\]");
6+
call mtr.add_suppression("InnoDB: Failed to read page [1-9][0-9]* from file '.*test/t[12]\\.ibd'");
77
call mtr.add_suppression("InnoDB: Encrypted page \\[page id: space=[1-9][0-9]*, page number=3\\] in file .*test.t1.ibd looks corrupted; key_version=1");
8+
call mtr.add_suppression("InnoDB: File '.*test/t[12]\\.ibd' is corrupted");
9+
call mtr.add_suppression("Table `test`\\.`t[12]` is corrupted");
810
call mtr.add_suppression("File '.*mysql-test.std_data.keysbad3\\.txt' not found");
911
call mtr.add_suppression("\\[ERROR\\] InnoDB: Cannot decrypt \\[page id: space=");
1012
# Start server with keys2.txt

mysql-test/suite/encryption/r/innodb-bad-key-change2.result

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ call mtr.add_suppression("InnoDB: The page \\[page id: space=[1-9][0-9]*, page n
33
call mtr.add_suppression("InnoDB: Recovery failed to read page");
44
call mtr.add_suppression("Couldn't load plugins from 'file_key_management");
55
call mtr.add_suppression("InnoDB: Tablespace for table \`test\`.\`t1\` is set as discarded\\.");
6-
call mtr.add_suppression("Table .*t1.* is corrupted");
6+
call mtr.add_suppression("InnoDB: Failed to read page 3 from file '.*test/t1\\.ibd'");
7+
call mtr.add_suppression("InnoDB: File '.*test/t1\\.ibd' is corrupted");
8+
call mtr.add_suppression("Table `test`\\.`t1` is corrupted");
79
call mtr.add_suppression("InnoDB: Cannot delete tablespace .* because it is not found in the tablespace memory cache");
810
call mtr.add_suppression("InnoDB: ALTER TABLE `test`\\.`t1` DISCARD TABLESPACE failed to find tablespace");
911
call mtr.add_suppression("\\[ERROR\\] InnoDB: Cannot decrypt \\[page id: space=");

mysql-test/suite/encryption/r/innodb-bad-key-change4.result

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ call mtr.add_suppression("InnoDB: Table `test`\\.`t1` (has an unreadable root pa
22
call mtr.add_suppression("InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=[1-9][0-9]*\\] in file '.*test.t1\\.ibd' cannot be decrypted; key_version=1");
33
call mtr.add_suppression("InnoDB: Recovery failed to read page");
44
call mtr.add_suppression("Couldn't load plugins from 'file_key_management");
5-
call mtr.add_suppression("Table .*t1.* is corrupted");
5+
call mtr.add_suppression("InnoDB: Failed to read page 3 from file '.*test/t1\\.ibd'");
6+
call mtr.add_suppression("InnoDB: File '.*test/t1\\.ibd' is corrupted");
7+
call mtr.add_suppression("Table `test`\\.`t1` is corrupted");
68
call mtr.add_suppression("\\[ERROR\\] InnoDB: Cannot decrypt \\[page id: space=");
79
# restart: --plugin-load-add=file_key_management --file-key-management --file-key-management-filename=MYSQL_TEST_DIR/std_data/keys2.txt
810
SET GLOBAL innodb_file_per_table = ON;

mysql-test/suite/encryption/r/innodb-compressed-blob.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
call mtr.add_suppression("InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=[1-9][0-9]*\\] in file '.*test.t[123]\\.ibd' cannot be decrypted; key_version=1");
22
call mtr.add_suppression("InnoDB: Recovery failed to read page");
33
call mtr.add_suppression("InnoDB: Unable to decompress ..test.t[12]\\.ibd\\[page id: space=[1-9][0-9]*, page number=[0-9]+\\]");
4-
call mtr.add_suppression("InnoDB: Database page corruption on disk or a failed read of file '.*test/t[12]\\.ibd' page \\[page id: space=[1-9][0-9]*, page number=3\\]");
4+
call mtr.add_suppression("InnoDB: Failed to read page 3 from file '.*test/t[12]\\.ibd'");
55
call mtr.add_suppression("InnoDB: File '.*test/t[12]\\.ibd' is corrupted");
66
call mtr.add_suppression("Table `test`\\.`t[12]` is corrupted");
77
# Restart mysqld --file-key-management-filename=keys2.txt

mysql-test/suite/encryption/r/innodb-encryption-disable.result

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ call mtr.add_suppression("Table `test`\\.`t[15]` (has an unreadable root page|is
22
call mtr.add_suppression("InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=[1-9][0-9]*\\] in file '.*test.t[15]\\.ibd' cannot be decrypted\\.");
33
call mtr.add_suppression("InnoDB: Recovery failed to read page");
44
call mtr.add_suppression("InnoDB: Encrypted page \\[page id: space=[1-9][0-9]*, page number=3\\] in file .*test.t[15].ibd looks corrupted; key_version=1");
5+
call mtr.add_suppression("InnoDB: Failed to read page 3 from file '.*test/t[15]\\.ibd'");
6+
call mtr.add_suppression("InnoDB: File '.*test/t[15]\\.ibd' is corrupted");
57
call mtr.add_suppression("Couldn't load plugins from 'file_key_management");
68
# restart: --innodb-encrypt-tables=ON --plugin-load-add=file_key_management --file-key-management --file-key-management-filename=MYSQL_TEST_DIR/std_data/keys2.txt
79
create table t5 (

mysql-test/suite/encryption/r/innodb-force-corrupt.result

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
call mtr.add_suppression("Table `test`\\.`t[13]` (has an unreadable root page|is corrupted)");
1+
call mtr.add_suppression("Table `test`\\.`t[123]` (has an unreadable root page|is corrupted)");
2+
call mtr.add_suppression("InnoDB: File '.*test/t[123]\\.ibd' is corrupted");
3+
call mtr.add_suppression("InnoDB: Failed to read page 3 from file '.*test/t[13]\\.ibd'");
4+
call mtr.add_suppression("InnoDB: Failed to read page 6 from file '.*test/t2\\.ibd'");
25
call mtr.add_suppression("InnoDB: Encrypted page \\[page id: space=\\d+, page number=[36]\\] in file .*test.t[123]\\.ibd looks corrupted; key_version=");
36
call mtr.add_suppression("\\[ERROR\\] InnoDB: We detected index corruption in an InnoDB type table");
4-
call mtr.add_suppression("\\[ERROR\\] (mysqld|mariadbd).*: Index for table 't2' is corrupt; try to repair it");
7+
call mtr.add_suppression("\\[ERROR\\] mariadbd.*: Index for table 't2' is corrupt; try to repair it");
58
SET GLOBAL innodb_file_per_table = ON;
69
set global innodb_compression_algorithm = 1;
710
# Create and populate tables to be corrupted

mysql-test/suite/encryption/r/innodb-missing-key.result

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ call mtr.add_suppression("InnoDB: Table `test`\\.`t1` has an unreadable root pag
22
call mtr.add_suppression("InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=[1-9][0-9]*\\] in file '.*test.t[123]\\.ibd' cannot be decrypted\\.");
33
call mtr.add_suppression("InnoDB: Recovery failed to read page");
44
call mtr.add_suppression("InnoDB: Encrypted page \\[page id: space=[1-9][0-9]*, page number=[1-9][0-9]*\\] in file .*test.t[12].ibd looks corrupted; key_version=1");
5-
call mtr.add_suppression("Table .*t1.* is corrupted");
5+
call mtr.add_suppression("InnoDB: Failed to read page 3 from file '.*test/t1\\.ibd'");
6+
call mtr.add_suppression("InnoDB: File '.*test/t1\\.ibd' is corrupted");
7+
call mtr.add_suppression("Table `test`\\.`t1` is corrupted");
68
# Start server with keys2.txt
79
# restart: --file-key-management-filename=MYSQL_TEST_DIR/std_data/keys2.txt
810
CREATE TABLE t1(a int not null primary key auto_increment, b varchar(128)) engine=innodb ENCRYPTED=YES ENCRYPTION_KEY_ID=19;

0 commit comments

Comments
 (0)