diff --git a/mysql-test/include/kill_mysqld.inc b/mysql-test/include/kill_mysqld.inc new file mode 100644 index 0000000000000..86ee048a0f18c --- /dev/null +++ b/mysql-test/include/kill_mysqld.inc @@ -0,0 +1,7 @@ +--let $_server_id= `SELECT @@server_id` +--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect + +--echo # Kill the server +--exec echo "wait" > $_expect_file_name +--shutdown_server 0 +--source include/wait_until_disconnected.inc diff --git a/mysql-test/suite/innodb/include/no_checkpoint_end.inc b/mysql-test/suite/innodb/include/no_checkpoint_end.inc new file mode 100644 index 0000000000000..7ca81f8ade033 --- /dev/null +++ b/mysql-test/suite/innodb/include/no_checkpoint_end.inc @@ -0,0 +1,35 @@ +# Check that the latest checkpoint in the redo log files +# is not newer than the checkpoint sampled by no_checkpoint_start.inc + +--source include/kill_mysqld.inc + +perl; +my $cp = $ENV{CHECKPOINT_LSN}; +$cp =~ s/^InnoDB\t\t//; +my $log = "$ENV{MYSQLD_DATADIR}ib_logfile0"; +open(LOG, "<$log") || die "Unable to open $log"; +seek(LOG, 512, 0) || die "Unable to seek $log"; +die unless read(LOG, $_, 16) == 16; +my ($no1hi,$no1lo,$cp1hi,$cp1lo) = unpack("N*", $_); +seek(LOG, 3 * 512, 0) || die "Unable to seek $log"; +die unless read(LOG, $_, 16) == 16; +my ($no2hi,$no2lo,$cp2hi,$cp2lo) = unpack("N*", $_); +close(LOG); + +my $cp1 = $cp1hi << 32 | $cp1lo; +my $cp2 = $cp2hi << 32 | $cp2lo; + +open(OUT, ">$ENV{MYSQLTEST_VARDIR}/log/check.txt") || die; + +if ($cp1 > $cp || $cp2 > $cp) { + print OUT "--source include/start_mysqld.inc\n"; + print OUT "$ENV{CLEANUP_IF_CHECKPOINT}\n"; + print OUT "--skip Extra checkpoint 1 after $cp"; + print OUT " ($no1hi:$no1lo=$cp1,$no2hi:$no2lo=$cp2)\n"; +} + +close(OUT); +EOF + +--source $MYSQLTEST_VARDIR/log/check.txt +--remove_file $MYSQLTEST_VARDIR/log/check.txt diff --git a/mysql-test/suite/innodb/include/no_checkpoint_start.inc b/mysql-test/suite/innodb/include/no_checkpoint_start.inc new file mode 100644 index 0000000000000..a903fee67d425 --- /dev/null +++ b/mysql-test/suite/innodb/include/no_checkpoint_start.inc @@ -0,0 +1,5 @@ +# Preparation for using no_checkpoint_end.inc + +let MYSQLD_DATADIR= `select @@datadir`; +--replace_regex /.*Last checkpoint at[ ]*([0-9]+).*/\1/ +let CHECKPOINT_LSN=`SHOW ENGINE INNODB STATUS`; diff --git a/mysql-test/suite/innodb/r/doublewrite.result b/mysql-test/suite/innodb/r/doublewrite.result new file mode 100644 index 0000000000000..aa96a5f2d9321 --- /dev/null +++ b/mysql-test/suite/innodb/r/doublewrite.result @@ -0,0 +1,241 @@ +# +# 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 +# +SET GLOBAL innodb_fast_shutdown = 0; +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 first page of user +# tablespace is full of zeroes. +select space from information_schema.innodb_sys_tables +where name = 'test/t1' into @space_id; +# 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 +# Make the first page (page_no=0) of the user tablespace +# full of zeroes. +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 user +# tablespace is corrupted. +select space from information_schema.innodb_sys_tables +where name = 'test/t1' into @space_id; +# 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. +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; +# 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. +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; +# 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. +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. +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. +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. +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. +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 +# --------------------------------------------------------------- +drop table t1; diff --git a/mysql-test/suite/innodb/t/doublewrite.test b/mysql-test/suite/innodb/t/doublewrite.test new file mode 100644 index 0000000000000..5bd4551aa1edb --- /dev/null +++ b/mysql-test/suite/innodb/t/doublewrite.test @@ -0,0 +1,368 @@ +--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/have_innodb.inc +--source include/have_debug.inc +--source include/not_embedded.inc + +# Slow shutdown and restart to make sure ibuf merge is finished +SET GLOBAL innodb_fast_shutdown = 0; +--source include/restart_mysqld.inc + +--disable_query_log +call mtr.add_suppression("InnoDB: Database page [0-9]+:1 contained only zeroes."); +call mtr.add_suppression("Header page consists of zero bytes"); +call mtr.add_suppression("Checksum mismatch in tablespace.*table test/t1"); +call mtr.add_suppression("but the innodb_page_size start-up parameter is"); +call mtr.add_suppression("Database page corruption"); +--enable_query_log + +let INNODB_PAGE_SIZE=`select @@innodb_page_size`; +let MYSQLD_DATADIR=`select @@datadir`; + +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 # --------------------------------------------------------------- +--echo # Test Begin: Test if recovery works if first 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('%', 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; + +--let CLEANUP_IF_CHECKPOINT=drop table t1; +--source ../include/no_checkpoint_end.inc + +--echo # Make the first page (page_no=0) of the user tablespace +--echo # full of zeroes. +perl; +use IO::Handle; +my $fname= "$ENV{'MYSQLD_DATADIR'}test/t1.ibd"; +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 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"; +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 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; +select f1, f2 from t1; + +--echo # Test End +--echo # --------------------------------------------------------------- + +drop table t1;