mirror of
https://github.com/MariaDB/server.git
synced 2025-01-18 04:53:01 +01:00
8a715a5990
storage/maria/ma_loghandler.c: Normally, when we log LOGREC_LONG_TRANSACTION_ID, undo_lsn should be 0: assert this. storage/maria/ma_test_force_start.pl: this script does not work with mtr2, make it use mtr1 storage/maria/trnman.c: The bug's cause was that: transaction would log LOGREC_LONG_TRANSACTION_ID, then Checkpoint would run and skip it (because its undo_lsn is still 0), then transaction would log REDO+UNDO, then crash. At Recovery, REDO phase would start from Checkpoint's record LSN, so wouldn't see LOGREC_LONG_TRANSACTION_ID, and as Checkpoint record does not mention transaction, transaction would be unknown, so its REDO+UNDO would be thrown away (assumed to belong to a transaction committed long ago as unknown), so transaction would not be rolled back, which is wrong. Fix is: it was wrong to skip a transaction if undo_lsn is 0; as soon as LOGREC_LONG_TRANSACTION_ID has been logged, it becomes potentially invisible to the REDO phase, and so we must include this transaction in the checkpoint record.
238 lines
7.1 KiB
Perl
Executable file
238 lines
7.1 KiB
Perl
Executable file
#!/usr/bin/env perl
|
|
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
my $usage= <<EOF;
|
|
This program tests that the options
|
|
--maria-force-start-after-recovery-failures --maria-recover work as
|
|
expected.
|
|
It has to be run from directory mysql-test, and works with non-debug
|
|
and debug binaries.
|
|
Pass it option -d or -i (to test corruption of data or index file).
|
|
EOF
|
|
|
|
# -d currently exhibits BUG#36578
|
|
# "Maria: maria-recover may fail to autorepair a table"
|
|
|
|
die($usage) if (@ARGV == 0);
|
|
|
|
my $corrupt_index;
|
|
|
|
if ($ARGV[0] eq '-d')
|
|
{
|
|
$corrupt_index= 0;
|
|
}
|
|
elsif ($ARGV[0] eq '-i')
|
|
{
|
|
$corrupt_index= 1;
|
|
}
|
|
else
|
|
{
|
|
die($usage);
|
|
}
|
|
|
|
my $force_after= 3;
|
|
my $corrupt_file= $corrupt_index ? "MAI" : "MAD";
|
|
my $corrupt_message=
|
|
"\\[ERROR\\] mysqld(.exe)*: Table '..test.t1' is marked as crashed and should be repaired";
|
|
|
|
my $sql_name= "./var/tmp/create_table.sql";
|
|
my $error_log_name= "./var/log/master.err";
|
|
my @cmd_output;
|
|
my $whatever; # garbage data
|
|
$ENV{MTR_VERSION} = 1; # MTR2 does not have --start-and-exit
|
|
my $base_server_cmd= "perl mysql-test-run.pl --mysqld=--maria-force-start-after-recovery-failures=$force_after --suite=maria maria.maria-recover ";
|
|
if ($^O =~ /^mswin/i)
|
|
{
|
|
print <<EOF;
|
|
WARNING: with Activestate Perl, mysql-test-run.pl --start-and-exit has a bug:
|
|
it does not exit; cygwin perl recommended
|
|
EOF
|
|
}
|
|
my $iswindows= ( $^O =~ /win/i && $^O !~ /darwin/i );
|
|
$base_server_cmd.= ($iswindows ? "--mysqld=--console" : "--mem");
|
|
my $server_cmd;
|
|
my $server_pid_name="./var/run/master.pid";
|
|
my $server_pid;
|
|
my $i; # count of server restarts
|
|
sub kill_server;
|
|
|
|
my $suffix= ($iswindows ? ".exe" : "");
|
|
my $client_exe_path= "../client/release";
|
|
# we use -f, sometimes -x is unexpectedly false in Cygwin
|
|
if ( ! -f "$client_exe_path/mysql$suffix" )
|
|
{
|
|
$client_exe_path= "../client/relwithdebinfo";
|
|
if ( ! -f "$client_exe_path/mysql$suffix" )
|
|
{
|
|
$client_exe_path= "../client/debug";
|
|
if ( ! -f "$client_exe_path/mysql$suffix" )
|
|
{
|
|
$client_exe_path= "../client";
|
|
if ( ! -f "$client_exe_path/mysql$suffix" )
|
|
{
|
|
die("Cannot find 'mysql' executable\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
print "starting mysqld\n";
|
|
$server_cmd= $base_server_cmd . " --start-and-exit 2>&1";
|
|
@cmd_output=`$server_cmd`;
|
|
die if $?;
|
|
my $master_port= (grep (/Using MASTER_MYPORT .*= (\d+)$/, @cmd_output))[0];
|
|
$master_port =~ s/.*= //;
|
|
chomp $master_port;
|
|
die unless $master_port > 0;
|
|
|
|
my $client_cmd= "$client_exe_path/mysql -u root -h 127.0.0.1 -P $master_port test < $sql_name";
|
|
|
|
open(FILE, ">", $sql_name) or die;
|
|
|
|
# To exhibit BUG#36578 with -d, we don't create an index if -d. This is
|
|
# because the presence of an index will cause repair-by-sort to be used,
|
|
# where sort_get_next_record() is only called inside
|
|
#_ma_create_index_by_sort(), so the latter function fails and in this
|
|
# case retry_repair is set, so bug does not happen. Whereas without
|
|
# an index, repair-with-key-cache is called, which calls
|
|
# sort_get_next_record() whose failure itself does not cause a retry.
|
|
|
|
print FILE "create table t1 (a varchar(1000)".
|
|
($corrupt_index ? ", index(a)" : "") .") engine=maria;\n";
|
|
print FILE <<EOF;
|
|
insert into t1 values("ThursdayMorningsMarket");
|
|
# If Recovery executes REDO_INDEX_NEW_PAGE it will overwrite our
|
|
# intentional corruption; we make Recovery skip this record by bumping
|
|
# create_rename_lsn using OPTIMIZE TABLE. This also makes sure to put
|
|
# the pages on disk, so that we can corrupt them.
|
|
optimize table t1;
|
|
# mark table open, so that --maria-recover repairs it
|
|
insert into t1 select concat(a,'b') from t1 limit 1;
|
|
EOF
|
|
close FILE;
|
|
|
|
print "creating table\n";
|
|
`$client_cmd`;
|
|
die if $?;
|
|
|
|
print "killing mysqld hard\n";
|
|
kill_server(9);
|
|
|
|
print "ruining " .
|
|
($corrupt_index ? "first page of keys" : "bitmap page") .
|
|
" in table to test maria-recover\n";
|
|
open(FILE, "+<", "./var/master-data/test/t1.$corrupt_file") or die;
|
|
$whatever= ("\xAB" x 100);
|
|
sysseek (FILE, $corrupt_index ? 8192 : (8192-100-100), 0) or die;
|
|
syswrite (FILE, $whatever) or die;
|
|
close FILE;
|
|
|
|
print "ruining log to make recovery fail; mysqld should fail the $force_after first restarts\n";
|
|
open(FILE, "+<", "./var/tmp/maria_log.00000001") or die;
|
|
$whatever= ("\xAB" x 8192);
|
|
sysseek (FILE, 99, 0) or die;
|
|
syswrite (FILE, $whatever) or die;
|
|
close FILE;
|
|
|
|
$server_cmd= $base_server_cmd . " --start-dirty 2>&1";
|
|
for($i= 1; $i <= $force_after; $i= $i + 1)
|
|
{
|
|
print "mysqld restart number $i... ";
|
|
unlink($error_log_name) or die;
|
|
`$server_cmd`;
|
|
# mysqld should return 1 when can't read log
|
|
die unless (($? >> 8) == 1);
|
|
open(FILE, "<", $error_log_name) or die;
|
|
@cmd_output= <FILE>;
|
|
close FILE;
|
|
die unless grep(/\[ERROR\] mysqld(.exe)*: Maria engine: log initialization failed/, @cmd_output);
|
|
die unless grep(/\[ERROR\] Plugin 'MARIA' init function returned error./, @cmd_output);
|
|
print "failed - ok\n";
|
|
}
|
|
|
|
print "mysqld restart number $i... ";
|
|
unlink($error_log_name) or die;
|
|
@cmd_output=`$server_cmd`;
|
|
die if $?;
|
|
open(FILE, "<", $error_log_name) or die;
|
|
@cmd_output= <FILE>;
|
|
close FILE;
|
|
die unless grep(/\[Warning\] mysqld(.exe)*: Maria engine: removed all logs after [\d]+ consecutive failures of recovery from logs/, @cmd_output);
|
|
die unless grep(/\[ERROR\] mysqld(.exe)*: File '.*tmp.maria_log.00000001' not found \(Errcode: 2\)/, @cmd_output);
|
|
print "success - ok\n";
|
|
|
|
open(FILE, ">", $sql_name) or die;
|
|
print FILE <<EOF;
|
|
set global maria_recover=normal;
|
|
insert into t1 values('aaa');
|
|
EOF
|
|
close FILE;
|
|
|
|
# verify corruption has not yet been noticed
|
|
open(FILE, "<", $error_log_name) or die;
|
|
@cmd_output= <FILE>;
|
|
close FILE;
|
|
die if grep(/$corrupt_message/, @cmd_output);
|
|
|
|
print "inserting in table\n";
|
|
`$client_cmd`;
|
|
die if $?;
|
|
print "table is usable - ok\n";
|
|
|
|
open(FILE, "<", $error_log_name) or die;
|
|
@cmd_output= <FILE>;
|
|
close FILE;
|
|
die unless grep(/$corrupt_message/, @cmd_output);
|
|
die unless grep(/\[Warning\] Recovering table: '..test.t1'/, @cmd_output);
|
|
print "was corrupted and automatically repaired - ok\n";
|
|
|
|
# remove our traces
|
|
kill_server(15);
|
|
|
|
print "TEST ALL OK\n";
|
|
|
|
# kills mysqld with signal given in parameter
|
|
sub kill_server
|
|
{
|
|
my ($sig)= @_;
|
|
my $wait_count= 0;
|
|
my $kill_cmd;
|
|
my @kill_output;
|
|
open(FILE, "<", $server_pid_name) or die;
|
|
@cmd_output= <FILE>;
|
|
close FILE;
|
|
$server_pid= $cmd_output[0];
|
|
chomp $server_pid;
|
|
die unless $server_pid > 0;
|
|
if ($iswindows)
|
|
{
|
|
# On Windows, server_pid_name is not the "main" process id
|
|
# so perl's kill() does not see this process id.
|
|
# But taskkill works, though only with /F ("-9"-style kill).
|
|
$kill_cmd= "taskkill /F /PID $server_pid 2>&1";
|
|
@kill_output= `$kill_cmd`;
|
|
die unless grep(/has been terminated/, @kill_output);
|
|
}
|
|
else
|
|
{
|
|
kill($sig, $server_pid) or die;
|
|
}
|
|
while (1) # wait until mysqld process gone
|
|
{
|
|
if ($iswindows)
|
|
{
|
|
@kill_output= `$kill_cmd`;
|
|
last if grep(/not found/, @kill_output);
|
|
}
|
|
else
|
|
{
|
|
kill (0, $server_pid) or last;
|
|
}
|
|
print "waiting for mysqld to die\n" if ($wait_count > 30);
|
|
$wait_count= $wait_count + 1;
|
|
select(undef, undef, undef, 0.1);
|
|
}
|
|
}
|