From d5a6ea36f384172205ab7a95de504b1b4cbd3136 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Thu, 7 Dec 2023 18:44:28 +0530 Subject: [PATCH] MDEV-32242 innodb.doublewrite test case always gets skipped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Split the doublewrite test into two test (doublewrite, doublewrite_debug) to reduce the execution time of the test - Removed big_test tag for the newly added test case - Made doublewrite test as non-debug test - Added search pattern to make sure that InnoDB uses doublewrite buffer - Replaced all kill_mysqld.inc with shutdown_mysqld.inc and zero shutdown timeout - Removed the case where fsp_flags got corrupted. Because from commit 3da5d047b8d8c193a872c5ae6c3fb213218117bc (MDEV-31851) onwards, doublewrite buffer removes the conversion the fsp flags from buggy 10.1 format Thanks to Marko Mäkelä for providing the non-debug test --- mysql-test/suite/innodb/r/doublewrite.result | 266 +---------- .../suite/innodb/r/doublewrite_debug.result | 92 ++++ mysql-test/suite/innodb/t/doublewrite.test | 423 ++---------------- .../innodb/t/doublewrite_debug.combinations | 7 + .../suite/innodb/t/doublewrite_debug.test | 162 +++++++ storage/innobase/handler/ha_innodb.cc | 14 +- 6 files changed, 326 insertions(+), 638 deletions(-) create mode 100644 mysql-test/suite/innodb/r/doublewrite_debug.result create mode 100644 mysql-test/suite/innodb/t/doublewrite_debug.combinations create mode 100644 mysql-test/suite/innodb/t/doublewrite_debug.test diff --git a/mysql-test/suite/innodb/r/doublewrite.result b/mysql-test/suite/innodb/r/doublewrite.result index 1f26f7d1f5f..7763aa99322 100644 --- a/mysql-test/suite/innodb/r/doublewrite.result +++ b/mysql-test/suite/innodb/r/doublewrite.result @@ -1,22 +1,6 @@ # -# Bug #17335427 INNODB CAN NOT USE THE DOUBLEWRITE BUFFER PROPERLY -# Bug #18144349 INNODB CANNOT USE THE DOUBLEWRITE BUFFER FOR THE FIRST -# PAGE OF SYSTEM TABLESPACE +# MDEV-32242 innodb.doublewrite test case always is skipped # -SET GLOBAL innodb_fast_shutdown = 0; -# restart -show variables like 'innodb_doublewrite'; -Variable_name Value -innodb_doublewrite ON -show variables like 'innodb_fil_make_page_dirty_debug'; -Variable_name Value -innodb_fil_make_page_dirty_debug 0 -show variables like 'innodb_saved_page_number_debug'; -Variable_name Value -innodb_saved_page_number_debug 0 -connect stop_purge,localhost,root,,; -START TRANSACTION WITH CONSISTENT SNAPSHOT; -connection default; create table t1 (f1 int primary key, f2 blob) engine=innodb; start transaction; insert into t1 values(1, repeat('#',12)); @@ -25,29 +9,20 @@ insert into t1 values(3, repeat('/',12)); insert into t1 values(4, repeat('-',12)); insert into t1 values(5, repeat('.',12)); commit work; -# --------------------------------------------------------------- -# Test Begin: Test if recovery works if first page of user -# tablespace is full of zeroes. -select space from information_schema.innodb_sys_tables -where name = 'test/t1' into @space_id; -Warnings: -Warning 1287 ' INTO FROM...' instead -begin; -insert into t1 values (6, repeat('%', 12)); -# Ensure that dirty pages of table t1 are flushed. -set global innodb_buf_flush_list_now = 1; -# Make the first page dirty for table t1 -set global innodb_saved_page_number_debug = 0; -set global innodb_fil_make_page_dirty_debug = @space_id; -# Ensure that dirty pages of table t1 are flushed. -set global innodb_buf_flush_list_now = 1; -# Kill the server -disconnect stop_purge; -# Make the first page (page_no=0) of the user tablespace -# full of zeroes. -# -# MDEV-11623: Use old FSP_SPACE_FLAGS in the doublewrite buffer. +SET GLOBAL innodb_fast_shutdown = 0; # restart +connect dml,localhost,root,,; +XA START 'x'; +insert into t1 values (6, repeat('%', @@innodb_page_size/2)); +XA END 'x'; +XA PREPARE 'x'; +disconnect dml; +connection default; +flush table t1 for export; +# restart +FOUND 1 /InnoDB: Restoring page \[page id: space=[1-9][0-9]*, page number=0\] of datafile/ in mysqld.1.err +FOUND 1 /InnoDB: Recovered page \[page id: space=[1-9][0-9]*, page number=3\]/ in mysqld.1.err +XA ROLLBACK 'x'; check table t1; Table Op Msg_type Msg_text test.t1 check status OK @@ -58,216 +33,5 @@ f1 f2 3 //////////// 4 ------------ 5 ............ -# Test End -# --------------------------------------------------------------- -# Test Begin: Test if recovery works if first page of user -# tablespace is corrupted. -select space from information_schema.innodb_sys_tables -where name = 'test/t1' into @space_id; -Warnings: -Warning 1287 ' INTO FROM...' instead -# Ensure that dirty pages of table t1 is flushed. -flush tables t1 for export; -unlock tables; -begin; -insert into t1 values (6, repeat('%', 12)); -# Make the first page dirty for table t1 -set global innodb_saved_page_number_debug = 0; -set global innodb_fil_make_page_dirty_debug = @space_id; -# Ensure that dirty pages of table t1 are flushed. -set global innodb_buf_flush_list_now = 1; -# Kill the server -# Corrupt the first page (page_no=0) of the user tablespace. -# restart -check table t1; -Table Op Msg_type Msg_text -test.t1 check status OK -select f1, f2 from t1; -f1 f2 -1 ############ -2 ++++++++++++ -3 //////////// -4 ------------ -5 ............ -# Test End -# --------------------------------------------------------------- -# Test Begin: Test if recovery works if 2nd page of user -# tablespace is full of zeroes. -select space from information_schema.innodb_sys_tables -where name = 'test/t1' into @space_id; -Warnings: -Warning 1287 ' INTO FROM...' instead -# Ensure that dirty pages of table t1 is flushed. -flush tables t1 for export; -unlock tables; -begin; -insert into t1 values (6, repeat('%', 400)); -# Make the 2nd page dirty for table t1 -set global innodb_saved_page_number_debug = 1; -set global innodb_fil_make_page_dirty_debug = @space_id; -# Ensure that dirty pages of table t1 are flushed. -set global innodb_buf_flush_list_now = 1; -# Kill the server -# Make the 2nd page (page_no=1) of the tablespace all zeroes. -# restart -check table t1; -Table Op Msg_type Msg_text -test.t1 check status OK -select f1, f2 from t1; -f1 f2 -1 ############ -2 ++++++++++++ -3 //////////// -4 ------------ -5 ............ -# Test End -# --------------------------------------------------------------- -# Test Begin: Test if recovery works if 2nd page of user -# tablespace is corrupted. -select space from information_schema.innodb_sys_tables -where name = 'test/t1' into @space_id; -Warnings: -Warning 1287 ' INTO FROM...' instead -# Ensure that dirty pages of table t1 is flushed. -flush tables t1 for export; -unlock tables; -begin; -insert into t1 values (6, repeat('%', 400)); -# Make the 2nd page dirty for table t1 -set global innodb_saved_page_number_debug = 1; -set global innodb_fil_make_page_dirty_debug = @space_id; -# Ensure that the dirty pages of table t1 are flushed. -set global innodb_buf_flush_list_now = 1; -# Kill the server -# Corrupt the 2nd page (page_no=1) of the user tablespace. -# restart -check table t1; -Table Op Msg_type Msg_text -test.t1 check status OK -select f1, f2 from t1; -f1 f2 -1 ############ -2 ++++++++++++ -3 //////////// -4 ------------ -5 ............ -# Test End -# --------------------------------------------------------------- -# Test Begin: Test if recovery works if first page of -# system tablespace is full of zeroes. -begin; -insert into t1 values (6, repeat('%', 400)); -# Ensure that all dirty pages in the system are flushed. -set global innodb_buf_flush_list_now = 1; -# Make the first page dirty for system tablespace -set global innodb_saved_page_number_debug = 0; -set global innodb_fil_make_page_dirty_debug = 0; -# Ensure that the dirty page of system tablespace is also flushed. -set global innodb_buf_flush_list_now = 1; -# Kill the server -# Make the first page (page_no=0) of the system tablespace -# all zeroes. -# restart -check table t1; -Table Op Msg_type Msg_text -test.t1 check status OK -select f1, f2 from t1; -f1 f2 -1 ############ -2 ++++++++++++ -3 //////////// -4 ------------ -5 ............ -# Test End -# --------------------------------------------------------------- -# Test Begin: Test if recovery works if first page of -# system tablespace is corrupted. -begin; -insert into t1 values (6, repeat('%', 400)); -# Ensure that all dirty pages in the system are flushed. -set global innodb_buf_flush_list_now = 1; -# Make the first page dirty for system tablespace -set global innodb_saved_page_number_debug = 0; -set global innodb_fil_make_page_dirty_debug = 0; -# Ensure that the dirty page of system tablespace is also flushed. -set global innodb_buf_flush_list_now = 1; -# Kill the server -# Corrupt the first page (page_no=0) of the system tablespace. -# restart -check table t1; -Table Op Msg_type Msg_text -test.t1 check status OK -select f1, f2 from t1; -f1 f2 -1 ############ -2 ++++++++++++ -3 //////////// -4 ------------ -5 ............ -# Test End -# --------------------------------------------------------------- -# Test Begin: Test if recovery works if 2nd page of -# system tablespace is full of zeroes. -begin; -insert into t1 values (6, repeat('%', 400)); -# Ensure that all dirty pages in the system are flushed. -set global innodb_buf_flush_list_now = 1; -# Make the second page dirty for system tablespace -set global innodb_saved_page_number_debug = 1; -set global innodb_fil_make_page_dirty_debug = 0; -# Ensure that the dirty page of system tablespace is also flushed. -set global innodb_buf_flush_list_now = 1; -# Kill the server -# Make the 2nd page (page_no=1) of the system tablespace -# all zeroes. -# restart -check table t1; -Table Op Msg_type Msg_text -test.t1 check status OK -select f1, f2 from t1; -f1 f2 -1 ############ -2 ++++++++++++ -3 //////////// -4 ------------ -5 ............ -# Test End -# --------------------------------------------------------------- -# Test Begin: Test if recovery works if 2nd page of -# system tablespace is corrupted. -begin; -insert into t1 values (6, repeat('%', 400)); -# Ensure that all dirty pages in the system are flushed. -set global innodb_buf_flush_list_now = 1; -# Make the second page dirty for system tablespace -set global innodb_saved_page_number_debug = 1; -set global innodb_fil_make_page_dirty_debug = 0; -# Ensure that the dirty page of system tablespace is also flushed. -set global innodb_buf_flush_list_now = 1; -# Kill the server -# Make the 2nd page (page_no=1) of the system tablespace -# all zeroes. -# restart -check table t1; -Table Op Msg_type Msg_text -test.t1 check status OK -FOUND 1 /InnoDB: .*test.t1\.ibd/ in mysqld.1.err -select f1, f2 from t1; -f1 f2 -1 ############ -2 ++++++++++++ -3 //////////// -4 ------------ -5 ............ drop table t1; -# -# MDEV-12600 crash during install_db with innodb_page_size=32K -# and ibdata1=3M -# -# restart: --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/doublewrite --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/doublewrite --innodb-data-file-path=ibdata1:1M;ibdata2:1M:autoextend -SELECT * FROM INFORMATION_SCHEMA.ENGINES -WHERE engine = 'innodb' -AND support IN ('YES', 'DEFAULT', 'ENABLED'); -ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS -FOUND 1 /\[ERROR\] InnoDB: Cannot create doublewrite buffer/ in mysqld.1.err -# restart +# End of 10.5 tests diff --git a/mysql-test/suite/innodb/r/doublewrite_debug.result b/mysql-test/suite/innodb/r/doublewrite_debug.result new file mode 100644 index 00000000000..960610a7091 --- /dev/null +++ b/mysql-test/suite/innodb/r/doublewrite_debug.result @@ -0,0 +1,92 @@ +# +# Bug #17335427 INNODB CAN NOT USE THE DOUBLEWRITE BUFFER PROPERLY +# Bug #18144349 INNODB CANNOT USE THE DOUBLEWRITE BUFFER FOR THE FIRST +# PAGE OF SYSTEM TABLESPACE +# +show variables like 'innodb_doublewrite'; +Variable_name Value +innodb_doublewrite ON +show variables like 'innodb_fil_make_page_dirty_debug'; +Variable_name Value +innodb_fil_make_page_dirty_debug 0 +show variables like 'innodb_saved_page_number_debug'; +Variable_name Value +innodb_saved_page_number_debug 0 +create table t1 (f1 int primary key, f2 blob) engine=innodb; +start transaction; +insert into t1 values(1, repeat('#',12)); +insert into t1 values(2, repeat('+',12)); +insert into t1 values(3, repeat('/',12)); +insert into t1 values(4, repeat('-',12)); +insert into t1 values(5, repeat('.',12)); +commit work; +# Test Begin: Test if recovery works if 1st page and 2nd page +# of system tablespace is full of zeroes. +SET GLOBAL innodb_fast_shutdown = 0; +# restart +begin; +insert into t1 values (6, repeat('%', 400)); +# Make the first page dirty for system tablespace +set global innodb_saved_page_number_debug = 0; +set global innodb_fil_make_page_dirty_debug = 0; +# Make the second page dirty for system tablespace +set global innodb_saved_page_number_debug = 1; +set global innodb_fil_make_page_dirty_debug = 0; +set global innodb_buf_flush_list_now = 1; +# Make the 1st page (page_no=0) and 2nd page (page_no=1) +# of the system tablespace all zeroes. +# restart +FOUND 1 /InnoDB: Restoring page \[page id: space=0, page number=0\] of datafile/ in mysqld.1.err +FOUND 1 /InnoDB: Recovered page \[page id: space=0, page number=1\]/ in mysqld.1.err +check table t1; +Table Op Msg_type Msg_text +test.t1 check status OK +select f1, f2 from t1; +f1 f2 +1 ############ +2 ++++++++++++ +3 //////////// +4 ------------ +5 ............ +# Test End +# --------------------------------------------------------------- +# Test Begin: Test if recovery works if 1st page of +# system tablespace is corrupted and 2nd page as corrupted. +set global innodb_log_checkpoint_now = 1; +begin; +insert into t1 values (6, repeat('%', 400)); +# Make the first page dirty for system tablespace +set global innodb_saved_page_number_debug = 0; +set global innodb_fil_make_page_dirty_debug = 0; +# Make the second page dirty for system tablespace +set global innodb_saved_page_number_debug = 1; +set global innodb_fil_make_page_dirty_debug = 0; +set global innodb_buf_flush_list_now = 1; +# Corrupt the 1st page (page_no=0) and 2nd page of the system tablespace. +# restart +FOUND 2 /InnoDB: Restoring page \[page id: space=0, page number=0\] of datafile/ in mysqld.1.err +FOUND 2 /InnoDB: Recovered page \[page id: space=0, page number=1\]/ in mysqld.1.err +check table t1; +Table Op Msg_type Msg_text +test.t1 check status OK +select f1, f2 from t1; +f1 f2 +1 ############ +2 ++++++++++++ +3 //////////// +4 ------------ +5 ............ +drop table t1; +# Test End +# --------------------------------------------------------------- +# +# MDEV-12600 crash during install_db with innodb_page_size=32K +# and ibdata1=3M +# +# restart: --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/doublewrite --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/doublewrite --innodb-data-file-path=ibdata1:1M;ibdata2:1M:autoextend +SELECT * FROM INFORMATION_SCHEMA.ENGINES +WHERE engine = 'innodb' +AND support IN ('YES', 'DEFAULT', 'ENABLED'); +ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS +FOUND 1 /\[ERROR\] InnoDB: Cannot create doublewrite buffer/ in mysqld.1.err +# restart diff --git a/mysql-test/suite/innodb/t/doublewrite.test b/mysql-test/suite/innodb/t/doublewrite.test index bd4f5fadcc3..89e6577b434 100644 --- a/mysql-test/suite/innodb/t/doublewrite.test +++ b/mysql-test/suite/innodb/t/doublewrite.test @@ -1,17 +1,11 @@ + --echo # ---echo # Bug #17335427 INNODB CAN NOT USE THE DOUBLEWRITE BUFFER PROPERLY ---echo # Bug #18144349 INNODB CANNOT USE THE DOUBLEWRITE BUFFER FOR THE FIRST ---echo # PAGE OF SYSTEM TABLESPACE +--echo # MDEV-32242 innodb.doublewrite test case always is skipped --echo # --source include/innodb_page_size.inc ---source include/have_debug.inc --source include/not_embedded.inc -# This test is slow on buildbot. ---source include/big_test.inc -# Slow shutdown and restart to make sure ibuf merge is finished -SET GLOBAL innodb_fast_shutdown = 0; --disable_query_log call mtr.add_suppression("InnoDB: Data file .* uses page size .* but the innodb_page_size start-up parameter is"); call mtr.add_suppression("InnoDB: adjusting FSP_SPACE_FLAGS"); @@ -22,22 +16,14 @@ call mtr.add_suppression("Plugin 'InnoDB' (init function returned error|registra call mtr.add_suppression("InnoDB: A bad Space ID was found in datafile"); call mtr.add_suppression("InnoDB: Checksum mismatch in datafile: "); call mtr.add_suppression("InnoDB: Inconsistent tablespace ID in .*t1\\.ibd"); +call mtr.add_suppression("\\[Warning\\] Found 1 prepared XA transactions"); --enable_query_log ---source include/restart_mysqld.inc let INNODB_PAGE_SIZE=`select @@innodb_page_size`; let MYSQLD_DATADIR=`select @@datadir`; let ALGO=`select @@innodb_checksum_algorithm`; let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err; -show variables like 'innodb_doublewrite'; -show variables like 'innodb_fil_make_page_dirty_debug'; -show variables like 'innodb_saved_page_number_debug'; - -connect (stop_purge,localhost,root,,); -START TRANSACTION WITH CONSISTENT SNAPSHOT; -connection default; - create table t1 (f1 int primary key, f2 blob) engine=innodb; start transaction; @@ -48,35 +34,24 @@ insert into t1 values(4, repeat('-',12)); insert into t1 values(5, repeat('.',12)); commit work; ---echo # --------------------------------------------------------------- ---echo # Test Begin: Test if recovery works if first page of user ---echo # tablespace is full of zeroes. +# Slow shutdown and restart to make sure ibuf merge is finished +SET GLOBAL innodb_fast_shutdown = 0; +let $shutdown_timeout=; +--source include/restart_mysqld.inc -select space from information_schema.innodb_sys_tables -where name = 'test/t1' into @space_id; +connect (dml,localhost,root,,); +XA START 'x'; +insert into t1 values (6, repeat('%', @@innodb_page_size/2)); +XA END 'x'; +XA PREPARE 'x'; +disconnect dml; +connection default; -begin; -insert into t1 values (6, repeat('%', 12)); ---echo # Ensure that dirty pages of table t1 are flushed. -set global innodb_buf_flush_list_now = 1; +flush table t1 for export; ---source ../include/no_checkpoint_start.inc - ---echo # Make the first page dirty for table t1 -set global innodb_saved_page_number_debug = 0; -set global innodb_fil_make_page_dirty_debug = @space_id; - ---echo # Ensure that dirty pages of table t1 are flushed. -set global innodb_buf_flush_list_now = 1; - ---let CLEANUP_IF_CHECKPOINT=drop table t1; ---source ../include/no_checkpoint_end.inc -disconnect stop_purge; - ---echo # Make the first page (page_no=0) of the user tablespace ---echo # full of zeroes. ---echo # ---echo # MDEV-11623: Use old FSP_SPACE_FLAGS in the doublewrite buffer. +let $restart_parameters=; +let $shutdown_timeout=0; +--source include/shutdown_mysqld.inc perl; use IO::Handle; @@ -90,16 +65,15 @@ my $page_size = $ENV{INNODB_PAGE_SIZE}; my $page; do "$ENV{MTR_SUITE_DIR}/../innodb/include/crc32.pl"; open(FILE, "+<", $fname) or die; +sysseek(FILE, ($page_size/2), 0); +syswrite(FILE, chr(0) x ($page_size/2)); +sysseek(FILE, 3*$page_size, 0); sysread(FILE, $page, $page_size)==$page_size||die "Unable to read $name\n"; -my $page1 = $page; -substr($page1, 34, 4) = pack("N", 0); -my $polynomial0 = 0x82f63b78; # CRC-32C -my $ck0 = mycrc32(substr($page1, 0, ($page_size-4)), 0, $polynomial0); -substr($page1, ($page_size - 4), 4) = pack("N", $ck0); -sysseek(FILE, 0, 0)||die "Unable to seek $fname\n"; -die unless syswrite(FILE, $page1, $page_size) == $page_size; +sysseek(FILE, 3*$page_size, 0)||die "Unable to seek $fname\n"; +syswrite(FILE, chr(0) x ($page_size/2)); close FILE; +# Change the flag offset of page 0 in doublewrite buffer open(FILE, "+<", "$ENV{MYSQLD_DATADIR}ibdata1")||die "cannot open ibdata1\n"; sysseek(FILE, 6 * $page_size - 190, 0)||die "Unable to seek ibdata1\n"; sysread(FILE, $_, 12) == 12||die "Unable to read TRX_SYS\n"; @@ -112,28 +86,23 @@ for (my $d = $d1; $d < $d2 + 64; $d++) sysread(FILE, $_, $page_size)==$page_size||die "Cannot read doublewrite\n"; next unless $_ eq $page; sysseek(FILE, $d * $page_size, 0)||die "Unable to seek ibdata1\n"; - # Write buggy MariaDB 10.1.x FSP_SPACE_FLAGS to the doublewrite buffer - my($flags) = unpack "x[54]N", $_; - my $badflags = ($flags & 0x3f); - my $compression_level=6; - $badflags |= 1<<6|$compression_level<<7 if ($flags & 1 << 16); - $badflags |= ($flags & 15 << 6) << 7; # PAGE_SSIZE - + # Write buggy FSP_SPACE_FLAGS to the doublewrite buffer for page + my $badflags = 0x0006FFFF; substr ($_, 54, 4) = pack("N", $badflags); if ($algo =~ /full_crc32/) { - my $ck = mycrc32(substr($_, 0, $page_size - 4), 0, $polynomial); - substr($_, $page_size - 4, 4) = pack("N", $ck); + my $ck = mycrc32(substr($_, 0, $page_size - 4), 0, $polynomial); + substr($_, $page_size - 4, 4) = pack("N", $ck); } else { - # Replace the innodb_checksum_algorithm=crc32 checksum - my $ck= pack("N", - mycrc32(substr($_, 4, 22), 0, $polynomial) ^ - mycrc32(substr($_, 38, $page_size - 38 - 8), 0, - $polynomial)); - substr ($_, 0, 4) = $ck; - substr ($_, $page_size - 8, 4) = $ck; + # Replace the innodb_checksum_algorithm=crc32 checksum + my $ck= pack("N", + mycrc32(substr($_, 4, 22), 0, $polynomial) ^ + mycrc32(substr($_, 38, $page_size - 38 - 8), 0, + $polynomial)); + substr ($_, 0, 4) = $ck; + substr ($_, $page_size - 8, 4) = $ck; } syswrite(FILE, $_, $page_size)==$page_size||die; close(FILE); @@ -143,323 +112,13 @@ die "Did not find the page in the doublewrite buffer ($d1,$d2)\n"; EOF --source include/start_mysqld.inc - -check table t1; -select f1, f2 from t1; - ---echo # Test End ---echo # --------------------------------------------------------------- ---echo # Test Begin: Test if recovery works if first page of user ---echo # tablespace is corrupted. - -select space from information_schema.innodb_sys_tables -where name = 'test/t1' into @space_id; - ---echo # Ensure that dirty pages of table t1 is flushed. -flush tables t1 for export; -unlock tables; - -begin; -insert into t1 values (6, repeat('%', 12)); - ---source ../include/no_checkpoint_start.inc - ---echo # Make the first page dirty for table t1 -set global innodb_saved_page_number_debug = 0; -set global innodb_fil_make_page_dirty_debug = @space_id; - ---echo # Ensure that dirty pages of table t1 are flushed. -set global innodb_buf_flush_list_now = 1; - ---source include/no_checkpoint_end.inc - ---echo # Corrupt the first page (page_no=0) of the user tablespace. -perl; -use IO::Handle; -my $fname= "$ENV{'MYSQLD_DATADIR'}test/t1.ibd"; -my $page_size = $ENV{INNODB_PAGE_SIZE}; -open(FILE, "+<", $fname) or die; -sysread(FILE, $page, $page_size)==$page_size||die "Unable to read $name\n"; -substr($page, 28, 4) = pack("N", 1000); -sysseek(FILE, 0, 0)||die "Unable to seek $fname\n"; -die unless syswrite(FILE, $page, $page_size) == $page_size; -close FILE; -EOF - ---source include/start_mysqld.inc - -check table t1; -select f1, f2 from t1; - ---echo # Test End ---echo # --------------------------------------------------------------- ---echo # Test Begin: Test if recovery works if 2nd page of user ---echo # tablespace is full of zeroes. - -select space from information_schema.innodb_sys_tables -where name = 'test/t1' into @space_id; - ---echo # Ensure that dirty pages of table t1 is flushed. -flush tables t1 for export; -unlock tables; - -begin; -insert into t1 values (6, repeat('%', 400)); - ---source ../include/no_checkpoint_start.inc - ---echo # Make the 2nd page dirty for table t1 -set global innodb_saved_page_number_debug = 1; -set global innodb_fil_make_page_dirty_debug = @space_id; - ---echo # Ensure that dirty pages of table t1 are flushed. -set global innodb_buf_flush_list_now = 1; - ---source include/no_checkpoint_end.inc - ---echo # Make the 2nd page (page_no=1) of the tablespace all zeroes. -perl; -use IO::Handle; -my $fname= "$ENV{'MYSQLD_DATADIR'}test/t1.ibd"; -open(FILE, "+<", $fname) or die; -FILE->autoflush(1); -binmode FILE; -seek(FILE, $ENV{'INNODB_PAGE_SIZE'}, SEEK_SET); -print FILE chr(0) x ($ENV{'INNODB_PAGE_SIZE'}); -close FILE; -EOF - ---source include/start_mysqld.inc - -check table t1; -select f1, f2 from t1; - ---echo # Test End ---echo # --------------------------------------------------------------- ---echo # Test Begin: Test if recovery works if 2nd page of user ---echo # tablespace is corrupted. - -select space from information_schema.innodb_sys_tables -where name = 'test/t1' into @space_id; - ---echo # Ensure that dirty pages of table t1 is flushed. -flush tables t1 for export; -unlock tables; - -begin; -insert into t1 values (6, repeat('%', 400)); - ---source ../include/no_checkpoint_start.inc - ---echo # Make the 2nd page dirty for table t1 -set global innodb_saved_page_number_debug = 1; -set global innodb_fil_make_page_dirty_debug = @space_id; - ---echo # Ensure that the dirty pages of table t1 are flushed. -set global innodb_buf_flush_list_now = 1; - ---source include/no_checkpoint_end.inc - ---echo # Corrupt the 2nd page (page_no=1) of the user tablespace. -perl; -use IO::Handle; -my $fname= "$ENV{'MYSQLD_DATADIR'}test/t1.ibd"; -open(FILE, "+<", $fname) or die; -FILE->autoflush(1); -binmode FILE; -seek(FILE, $ENV{'INNODB_PAGE_SIZE'}, SEEK_SET); -print FILE chr(0) x ($ENV{'INNODB_PAGE_SIZE'}/2); -close FILE; -EOF - ---source include/start_mysqld.inc - -check table t1; -select f1, f2 from t1; - ---echo # Test End ---echo # --------------------------------------------------------------- ---echo # Test Begin: Test if recovery works if first page of ---echo # system tablespace is full of zeroes. - -begin; -insert into t1 values (6, repeat('%', 400)); - ---echo # Ensure that all dirty pages in the system are flushed. -set global innodb_buf_flush_list_now = 1; - ---echo # Make the first page dirty for system tablespace -set global innodb_saved_page_number_debug = 0; -set global innodb_fil_make_page_dirty_debug = 0; - ---echo # Ensure that the dirty page of system tablespace is also flushed. -# We do this after the transaction starts and all dirty pages have been flushed -# already. So flushing of this specified dirty page will surely keep the -# copy in doublewrite buffer, and no more writes to doublewrite buffer would -# overwrite the copy. Thus, we can safely modify the original page when server -# is down. So do the following testings. -set global innodb_buf_flush_list_now = 1; - ---source include/kill_mysqld.inc - ---echo # Make the first page (page_no=0) of the system tablespace ---echo # all zeroes. -perl; -use IO::Handle; -my $fname= "$ENV{'MYSQLD_DATADIR'}ibdata1"; -open(FILE, "+<", $fname) or die; -FILE->autoflush(1); -binmode FILE; -print FILE chr(0) x ($ENV{'INNODB_PAGE_SIZE'}); -close FILE; -EOF - ---source include/start_mysqld.inc - -check table t1; -select f1, f2 from t1; - ---echo # Test End ---echo # --------------------------------------------------------------- ---echo # Test Begin: Test if recovery works if first page of ---echo # system tablespace is corrupted. - -begin; -insert into t1 values (6, repeat('%', 400)); - ---echo # Ensure that all dirty pages in the system are flushed. -set global innodb_buf_flush_list_now = 1; - ---echo # Make the first page dirty for system tablespace -set global innodb_saved_page_number_debug = 0; -set global innodb_fil_make_page_dirty_debug = 0; - ---echo # Ensure that the dirty page of system tablespace is also flushed. -set global innodb_buf_flush_list_now = 1; - ---source include/kill_mysqld.inc - ---echo # Corrupt the first page (page_no=0) of the system tablespace. -perl; -use IO::Handle; -my $fname= "$ENV{'MYSQLD_DATADIR'}ibdata1"; -open(FILE, "+<", $fname) or die; -FILE->autoflush(1); -binmode FILE; -print FILE chr(0) x ($ENV{'INNODB_PAGE_SIZE'}/2); -close FILE; -EOF - ---source include/start_mysqld.inc - -check table t1; -select f1, f2 from t1; - ---echo # Test End ---echo # --------------------------------------------------------------- ---echo # Test Begin: Test if recovery works if 2nd page of ---echo # system tablespace is full of zeroes. - -begin; -insert into t1 values (6, repeat('%', 400)); - ---echo # Ensure that all dirty pages in the system are flushed. -set global innodb_buf_flush_list_now = 1; - ---echo # Make the second page dirty for system tablespace -set global innodb_saved_page_number_debug = 1; -set global innodb_fil_make_page_dirty_debug = 0; - ---echo # Ensure that the dirty page of system tablespace is also flushed. -set global innodb_buf_flush_list_now = 1; - ---source include/kill_mysqld.inc - ---echo # Make the 2nd page (page_no=1) of the system tablespace ---echo # all zeroes. -perl; -use IO::Handle; -my $fname= "$ENV{'MYSQLD_DATADIR'}ibdata1"; -open(FILE, "+<", $fname) or die; -FILE->autoflush(1); -binmode FILE; -seek(FILE, $ENV{'INNODB_PAGE_SIZE'}, SEEK_SET); -print FILE chr(0) x ($ENV{'INNODB_PAGE_SIZE'}); -close FILE; -EOF - ---source include/start_mysqld.inc - -check table t1; -select f1, f2 from t1; - ---echo # Test End ---echo # --------------------------------------------------------------- ---echo # Test Begin: Test if recovery works if 2nd page of ---echo # system tablespace is corrupted. - -begin; -insert into t1 values (6, repeat('%', 400)); - ---echo # Ensure that all dirty pages in the system are flushed. -set global innodb_buf_flush_list_now = 1; - ---echo # Make the second page dirty for system tablespace -set global innodb_saved_page_number_debug = 1; -set global innodb_fil_make_page_dirty_debug = 0; - ---echo # Ensure that the dirty page of system tablespace is also flushed. -set global innodb_buf_flush_list_now = 1; - ---source include/kill_mysqld.inc - ---echo # Make the 2nd page (page_no=1) of the system tablespace ---echo # all zeroes. -perl; -use IO::Handle; -my $fname= "$ENV{'MYSQLD_DATADIR'}ibdata1"; -open(FILE, "+<", $fname) or die; -FILE->autoflush(1); -binmode FILE; -seek(FILE, $ENV{'INNODB_PAGE_SIZE'}, SEEK_SET); -print FILE chr(0) x ($ENV{'INNODB_PAGE_SIZE'}/2); -close FILE; -EOF - ---source include/start_mysqld.inc - -check table t1; ---let SEARCH_PATTERN= InnoDB: .*test.t1\\.ibd +let SEARCH_PATTERN=InnoDB: Restoring page \[page id: space=[1-9][0-9]*, page number=0\] of datafile; --source include/search_pattern_in_file.inc - +let SEARCH_PATTERN=InnoDB: Recovered page \[page id: space=[1-9][0-9]*, page number=3\]; +--source include/search_pattern_in_file.inc +XA ROLLBACK 'x'; +check table t1; select f1, f2 from t1; - drop table t1; ---echo # ---echo # MDEV-12600 crash during install_db with innodb_page_size=32K ---echo # and ibdata1=3M ---echo # -let bugdir= $MYSQLTEST_VARDIR/tmp/doublewrite; ---mkdir $bugdir - -let $check_no_innodb=SELECT * FROM INFORMATION_SCHEMA.ENGINES -WHERE engine = 'innodb' -AND support IN ('YES', 'DEFAULT', 'ENABLED'); - ---let $ibp=--innodb-log-group-home-dir=$bugdir --innodb-data-home-dir=$bugdir ---let $ibd=$ibp --innodb-undo-tablespaces=0 ---let $ibp=$ibp --innodb-data-file-path=ibdata1:1M;ibdata2:1M:autoextend - ---let $restart_parameters= $ibp ---source include/restart_mysqld.inc -eval $check_no_innodb; ---let SEARCH_PATTERN= \[ERROR\] InnoDB: Cannot create doublewrite buffer ---source include/search_pattern_in_file.inc ---let $restart_parameters= ---source include/restart_mysqld.inc - ---remove_file $bugdir/ibdata1 ---remove_file $bugdir/ibdata2 ---remove_file $bugdir/ib_logfile0 ---rmdir $bugdir +--echo # End of 10.5 tests diff --git a/mysql-test/suite/innodb/t/doublewrite_debug.combinations b/mysql-test/suite/innodb/t/doublewrite_debug.combinations new file mode 100644 index 00000000000..4f52013f6fc --- /dev/null +++ b/mysql-test/suite/innodb/t/doublewrite_debug.combinations @@ -0,0 +1,7 @@ +[strict_crc32] +--innodb-checksum-algorithm=strict_crc32 +--innodb-use-atomic-writes=0 + +[strict_full_crc32] +--innodb-checksum-algorithm=strict_full_crc32 +--innodb-use-atomic-writes=0 diff --git a/mysql-test/suite/innodb/t/doublewrite_debug.test b/mysql-test/suite/innodb/t/doublewrite_debug.test new file mode 100644 index 00000000000..86809cc43c0 --- /dev/null +++ b/mysql-test/suite/innodb/t/doublewrite_debug.test @@ -0,0 +1,162 @@ +--echo # +--echo # Bug #17335427 INNODB CAN NOT USE THE DOUBLEWRITE BUFFER PROPERLY +--echo # Bug #18144349 INNODB CANNOT USE THE DOUBLEWRITE BUFFER FOR THE FIRST +--echo # PAGE OF SYSTEM TABLESPACE +--echo # + +--source include/innodb_page_size.inc +--source include/have_debug.inc +--source include/not_embedded.inc +--disable_query_log +call mtr.add_suppression("InnoDB: Data file .* uses page size .* but the innodb_page_size start-up parameter is"); +call mtr.add_suppression("InnoDB: adjusting FSP_SPACE_FLAGS"); +call mtr.add_suppression("InnoDB: New log files created"); +call mtr.add_suppression("InnoDB: Cannot create doublewrite buffer: the first file in innodb_data_file_path must be at least (3|6|12)M\\."); +call mtr.add_suppression("InnoDB: Database creation was aborted"); +call mtr.add_suppression("Plugin 'InnoDB' (init function returned error|registration as a STORAGE ENGINE failed)"); +call mtr.add_suppression("InnoDB: A bad Space ID was found in datafile"); +call mtr.add_suppression("InnoDB: Checksum mismatch in datafile: "); +call mtr.add_suppression("InnoDB: Inconsistent tablespace ID in .*t1\\.ibd"); +--enable_query_log + +let INNODB_PAGE_SIZE=`select @@innodb_page_size`; +let MYSQLD_DATADIR=`select @@datadir`; +let ALGO=`select @@innodb_checksum_algorithm`; +let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err; + +show variables like 'innodb_doublewrite'; +show variables like 'innodb_fil_make_page_dirty_debug'; +show variables like 'innodb_saved_page_number_debug'; + +create table t1 (f1 int primary key, f2 blob) engine=innodb; + +start transaction; +insert into t1 values(1, repeat('#',12)); +insert into t1 values(2, repeat('+',12)); +insert into t1 values(3, repeat('/',12)); +insert into t1 values(4, repeat('-',12)); +insert into t1 values(5, repeat('.',12)); +commit work; + +--echo # Test Begin: Test if recovery works if 1st page and 2nd page +--echo # of system tablespace is full of zeroes. + +# Slow shutdown and restart to make sure ibuf merge is finished +SET GLOBAL innodb_fast_shutdown = 0; +let $shutdown_timeout=; +--source include/restart_mysqld.inc +begin; +insert into t1 values (6, repeat('%', 400)); + +--echo # Make the first page dirty for system tablespace +set global innodb_saved_page_number_debug = 0; +set global innodb_fil_make_page_dirty_debug = 0; + +--echo # Make the second page dirty for system tablespace +set global innodb_saved_page_number_debug = 1; +set global innodb_fil_make_page_dirty_debug = 0; + +set global innodb_buf_flush_list_now = 1; +let $shutdown_timeout=0; +--source include/shutdown_mysqld.inc + +--echo # Make the 1st page (page_no=0) and 2nd page (page_no=1) +--echo # of the system tablespace all zeroes. +perl; +use IO::Handle; +my $fname= "$ENV{'MYSQLD_DATADIR'}ibdata1"; +open(FILE, "+<", $fname) or die; +FILE->autoflush(1); +binmode FILE; +print FILE chr(0) x ($ENV{'INNODB_PAGE_SIZE'}); +seek(FILE, $ENV{'INNODB_PAGE_SIZE'}, SEEK_SET); +print FILE chr(0) x ($ENV{'INNODB_PAGE_SIZE'}); +close FILE; +EOF + +--source include/start_mysqld.inc + +let SEARCH_PATTERN=InnoDB: Restoring page \[page id: space=0, page number=0\] of datafile; +--source include/search_pattern_in_file.inc + +let SEARCH_PATTERN=InnoDB: Recovered page \[page id: space=0, page number=1\]; +--source include/search_pattern_in_file.inc + +check table t1; +select f1, f2 from t1; + +--echo # Test End +--echo # --------------------------------------------------------------- +--echo # Test Begin: Test if recovery works if 1st page of +--echo # system tablespace is corrupted and 2nd page as corrupted. + +set global innodb_log_checkpoint_now = 1; +begin; +insert into t1 values (6, repeat('%', 400)); + +--echo # Make the first page dirty for system tablespace +set global innodb_saved_page_number_debug = 0; +set global innodb_fil_make_page_dirty_debug = 0; + +--echo # Make the second page dirty for system tablespace +set global innodb_saved_page_number_debug = 1; +set global innodb_fil_make_page_dirty_debug = 0; + +set global innodb_buf_flush_list_now = 1; +let $shutdown_timeout=0; +--source include/shutdown_mysqld.inc + +--echo # Corrupt the 1st page (page_no=0) and 2nd page of the system tablespace. +perl; +use IO::Handle; +my $fname= "$ENV{'MYSQLD_DATADIR'}ibdata1"; +open(FILE, "+<", $fname) or die; +FILE->autoflush(1); +binmode FILE; +print FILE chr(0) x ($ENV{'INNODB_PAGE_SIZE'}/2); +seek(FILE, $ENV{'INNODB_PAGE_SIZE'}, SEEK_SET); +print FILE chr(0) x ($ENV{'INNODB_PAGE_SIZE'}/2); +close FILE; +EOF + +--source include/start_mysqld.inc + +let SEARCH_PATTERN=InnoDB: Restoring page \[page id: space=0, page number=0\] of datafile; +--source include/search_pattern_in_file.inc + +let SEARCH_PATTERN=InnoDB: Recovered page \[page id: space=0, page number=1\]; +--source include/search_pattern_in_file.inc + +check table t1; +select f1, f2 from t1; +drop table t1; +let $shutdown_timeout=; +--echo # Test End +--echo # --------------------------------------------------------------- +--echo # +--echo # MDEV-12600 crash during install_db with innodb_page_size=32K +--echo # and ibdata1=3M +--echo # +let bugdir= $MYSQLTEST_VARDIR/tmp/doublewrite; +--mkdir $bugdir + +let $check_no_innodb=SELECT * FROM INFORMATION_SCHEMA.ENGINES +WHERE engine = 'innodb' +AND support IN ('YES', 'DEFAULT', 'ENABLED'); + +--let $ibp=--innodb-log-group-home-dir=$bugdir --innodb-data-home-dir=$bugdir +--let $ibd=$ibp --innodb-undo-tablespaces=0 +--let $ibp=$ibp --innodb-data-file-path=ibdata1:1M;ibdata2:1M:autoextend + +--let $restart_parameters= $ibp +--source include/restart_mysqld.inc +eval $check_no_innodb; +--let SEARCH_PATTERN= \[ERROR\] InnoDB: Cannot create doublewrite buffer +--source include/search_pattern_in_file.inc +--let $restart_parameters= +--source include/restart_mysqld.inc + +--remove_file $bugdir/ibdata1 +--remove_file $bugdir/ibdata2 +--remove_file $bugdir/ib_logfile0 +--rmdir $bugdir diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index efeddda3dec..5e239633f22 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -18326,11 +18326,15 @@ static void buf_flush_list_now_set(THD*, st_mysql_sys_var*, void*, const void* save) { - if (*(my_bool*) save) { - mysql_mutex_unlock(&LOCK_global_system_variables); - buf_flush_sync(); - mysql_mutex_lock(&LOCK_global_system_variables); - } + if (!*(my_bool*) save) + return; + const uint s= srv_fil_make_page_dirty_debug; + mysql_mutex_unlock(&LOCK_global_system_variables); + if (s) + buf_flush_sync(); + else + while (buf_flush_list_space(fil_system.sys_space, nullptr)); + mysql_mutex_lock(&LOCK_global_system_variables); } /** Override current MERGE_THRESHOLD setting for all indexes at dictionary