Commit a5bcb63f authored by Guilhem Bichot's avatar Guilhem Bichot

WL#4374 "Maria - force start if Recovery fails multiple times"

http://forge.mysql.com/worklog/task.php?id=4374
new option --maria-force-start-after-recovery-failures=N; number of consecutive recovery failures (failures
of log reading or recovery processing, anything in [translog_init(),maria_recovery_from_log()])
is stored in the control file; if at a Maria start they are more than N, logs are removed. This is for automated
systems which have to run whatever happens. As tables risk staying corrupted, --maria-recover should also
be used on them: this revision makes maria-recover work (it was disabled).
Fixed bug in translog_is_log_files(). translog_init() now prints message to error log if failed.
Removed \0 in the output of SHOW ENGINE MARIA LOGS; removed hard-coded engine name there.

KNOWN_BUGS.txt:
  As option --maria-force-start-after-recovery-failures is added, it corresponds to the wish "we should fix that if this happens etc".
  LOAD INDEX is not ignored since a few weeks. Listed concurrency bugs have been fixed some time ago.
  Recovery of fulltext and GIS indexes works since a few weeks.
mysql-test/include/maria_make_snapshot.inc:
  configurable prefix in table's name (so far 't' or 't_corrupted')
mysql-test/include/maria_make_snapshot_for_comparison.inc:
  configurable prefix in table's name (so far 't' or 't_corrupted')
mysql-test/include/maria_make_snapshot_for_feeding_recovery.inc:
  configurable prefix in table's name (so far 't' or 't_corrupted')
mysql-test/include/maria_verify_recovery.inc:
  configurable prefix in table's name (so far 't' or 't_corrupted')
mysql-test/lib/mtr_report.pl:
  new test maria-recover.test generates expected corruption warnings in the error log. maria-recovery.test's corrupted table is renamed to t_corrupted1 instead of t1.
mysql-test/r/maria-preload.result:
  result update. maria_pagecache_read* values are similar to the previous version of this file, though a bit bigger
  because using the information_schema and the join leads to some internal maria temp table being used, and thus some
  blocks of it being read.
mysql-test/r/maria-purge.result:
  engine's name in SHOW ENGINE MARIA LOGS changed.
mysql-test/r/maria-recover.result:
  result for new test. We see corruption messages at first SELECT and then none at second SELECT, expected.
mysql-test/r/maria-recovery.result:
  result update
mysql-test/r/maria.result:
  new variables show up
mysql-test/t/disabled.def:
  BUG#34911 is not fixed but the test had been made independent of the bug (workaround). A new bug (crash) has popped recently, so it has to stay
  disabled (BUG#35107).
mysql-test/t/maria-preload.test:
  Work around BUG#34911 "FLUSH STATUS doesn't flush what it should":
  compute differences in status variables before and after relevant queries
mysql-test/t/maria-recover-master.opt:
  test --maria-recover
mysql-test/t/maria-recover.test:
  Test of the --maria-recover option (build a corrupted table and see if it is auto-repaired)
mysql-test/t/maria-recovery-big.test:
  update for new API of include/maria*.inc
mysql-test/t/maria-recovery-bitmap.test:
  update for new API of include/maria*.inc
mysql-test/t/maria-recovery.test:
  update for new API of include/maria*.inc. Corrupted table t1 renamed to t_corrupted1, so that mtr_report.pl
  does not blindly remove all corruption messages for t1 which is
  a common name.
storage/maria/ha_maria.cc:
  Enabling maria-recover.
  Adding option and global variable --maria_force_start_after_recovery_failures: ha_maria_init()
  calls mark_recovery_start() and mark_recovery_success() to keep track of failed consecutive recoveries
  and remove logs if needed.
  Removed \0 in the output of SHOW ENGINE MARIA LOGS; removed hard-coded engine name there.
storage/maria/ma_checkpoint.c:
  new prototype
storage/maria/ma_control_file.c:
  Storing in one byte in the control file, the number of consecutive recovery failures.
storage/maria/ma_control_file.h:
  new prototype
storage/maria/ma_init.c:
  new prototype
storage/maria/ma_locking.c:
  Need to update open_count on disk at first write and close for transactional tables, like we already did for
  non-transactional tables, otherwise we cannot notice that the table is dubious.
storage/maria/ma_loghandler.c:
  translog_is_log_files() is made more generic to serve either to search or to delete logs (the latter is
  for --maria-force-start-after-recovery-failures). It also had a bug (always returned FALSE).
storage/maria/ma_loghandler.h:
  export function because ha_maria::mark_recovery_start() needs it
storage/maria/ma_recovery.c:
  changing name of maria_recover() to distinguish from the maria-recover option.
storage/maria/ma_recovery.h:
  changing name of maria_recover() to distinguish from the maria-recover option.
storage/maria/ma_test_force_start.pl:
  Test of --maria-force-start-after-recovery-failures (and also, to be realistic, of --maria-recover).
  This is standalone because mysql-test-run does not support testing that multiple mysqld restarts expectedly failed.
  I'll have to run it on my machine and also on a Windows machine.
storage/maria/unittest/ma_control_file-t.c:
  adding recovery_failures to the test
storage/maria/unittest/ma_test_loghandler_multigroup-t.c:
  fix for compiler warning (unused variable in non-debug build)
parent 2d64cd05
...@@ -24,23 +24,9 @@ or in the worst case add it here for others to know! ...@@ -24,23 +24,9 @@ or in the worst case add it here for others to know!
Known bugs that we are working on and will be fixed shortly Known bugs that we are working on and will be fixed shortly
=========================================================== ===========================================================
- If the log files are damaged or inconsistent, Maria may fail to start.
We should fix that if this happens and mysqld is restarted (thanks to
mysqld_safe, instance manager or other script) it should disregard the
old logs, start anyway and automaticly repair any tables that was found
to be crashed on open.
Temporary fix is to remove or maria_log.???????? files from the data
directory, restart mysqld and run CHECK TABLE / REPAIR TABLE or
mysqlcheck on your Maria tables
- We have some instabilities in log writing that is under investigatation - We have some instabilities in log writing that is under investigatation
This causes mainly assert to triggers in the code and sometimes This causes mainly assert to triggers in the code and sometimes
the log handler doesn't start up after restart. the log handler doesn't start up after restart.
- LOAD INDEX commands are for the moment ignored for Maria tables
(The code needs to be rewritten to do all reads through page cache to
avoid half-block reads)
- Some concurrency bugs in Maria's page cache which sometimes show up
under load http://bugs.mysql.com/bug.php?id=34161 and
http://bugs.mysql.com/bug.php?id=34634 .
Known bugs that are planned to be fixed before Beta Known bugs that are planned to be fixed before Beta
=================================================== ===================================================
...@@ -61,19 +47,15 @@ Known bugs that are planned to be fixed before Beta ...@@ -61,19 +47,15 @@ Known bugs that are planned to be fixed before Beta
Missing features that is planned to fix before Beta Missing features that is planned to fix before Beta
=================================================== ===================================================
- We will add an maria-recover option to automaticly repair any
crashed tables on open. (This is needed for not transactional tables
and also in edge cases for transactional tables when the table
crashed because of a bug in MySQL or Maria code)
- Multiple concurrent inserts & multiple concurrent readers at same time - Multiple concurrent inserts & multiple concurrent readers at same time
with full MVCC control. Note that UPDATE and DELETE will still be with full MVCC control. Note that UPDATE and DELETE will still be
blocking (as with MyISAM) blocking (as with MyISAM)
- COUNT(*) and TABLE CHECKSUM under MVCC (ie, they are instant and kept up - COUNT(*) and TABLE CHECKSUM under MVCC (ie, they are instant and kept up
to date even with multiple inserter) to date even with multiple inserter)
- Recovery of fulltext and GIS indexes.
Features planned for future releases Features planned for future releases
==================================== ====================================
http://forge.mysql.com/worklog/ http://forge.mysql.com/worklog/
(you can enter "maria" in the "quick search" field there).
...@@ -10,28 +10,29 @@ ...@@ -10,28 +10,29 @@
# $mms_copy : to copy table from database to spare directory # $mms_copy : to copy table from database to spare directory
# $mms_reverse : to copy it back # $mms_reverse : to copy it back
# $mms_compare_physically : to compare both byte-for-byte # $mms_compare_physically : to compare both byte-for-byte
# 2) set $mms_table_to_use to a number N: table will be mysqltest.tN # 2) set $mms_tname to a string and set $mms_table_to_use to a number: tables
# will be mysqltest.$mms_tname$mms_table_to_use.
# 3) set $mms_purpose to say what this copy is for (influences the naming # 3) set $mms_purpose to say what this copy is for (influences the naming
# of the spare directory). # of the spare directory).
if ($mms_copy) if ($mms_copy)
{ {
--echo * copied t$mms_table_to_use for $mms_purpose --echo * copied $mms_tname$mms_table_to_use for $mms_purpose
copy_file $MYSQLTEST_VARDIR/master-data/mysqltest/t$mms_table_to_use.MAD $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/t$mms_table_to_use.MAD; copy_file $MYSQLTEST_VARDIR/master-data/mysqltest/$mms_tname$mms_table_to_use.MAD $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/$mms_tname$mms_table_to_use.MAD;
copy_file $MYSQLTEST_VARDIR/master-data/mysqltest/t$mms_table_to_use.MAI $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/t$mms_table_to_use.MAI; copy_file $MYSQLTEST_VARDIR/master-data/mysqltest/$mms_tname$mms_table_to_use.MAI $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/$mms_tname$mms_table_to_use.MAI;
copy_file $MYSQLTEST_VARDIR/master-data/mysqltest/t$mms_table_to_use.frm $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/t$mms_table_to_use.frm; copy_file $MYSQLTEST_VARDIR/master-data/mysqltest/$mms_tname$mms_table_to_use.frm $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/$mms_tname$mms_table_to_use.frm;
} }
if ($mms_reverse_copy) if ($mms_reverse_copy)
{ {
# do not call this without flushing target table first! # do not call this without flushing target table first!
--echo * copied t$mms_table_to_use back for $mms_purpose --echo * copied $mms_tname$mms_table_to_use back for $mms_purpose
-- error 0,1 -- error 0,1
remove_file $MYSQLTEST_VARDIR/master-data/mysqltest/t$mms_table_to_use.MAD; remove_file $MYSQLTEST_VARDIR/master-data/mysqltest/$mms_tname$mms_table_to_use.MAD;
copy_file $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/t$mms_table_to_use.MAD $MYSQLTEST_VARDIR/master-data/mysqltest/t$mms_table_to_use.MAD; copy_file $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/$mms_tname$mms_table_to_use.MAD $MYSQLTEST_VARDIR/master-data/mysqltest/$mms_tname$mms_table_to_use.MAD;
-- error 0,1 -- error 0,1
remove_file $MYSQLTEST_VARDIR/master-data/mysqltest/t$mms_table_to_use.MAI; remove_file $MYSQLTEST_VARDIR/master-data/mysqltest/$mms_tname$mms_table_to_use.MAI;
copy_file $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/t$mms_table_to_use.MAI $MYSQLTEST_VARDIR/master-data/mysqltest/t$mms_table_to_use.MAI; copy_file $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/$mms_tname$mms_table_to_use.MAI $MYSQLTEST_VARDIR/master-data/mysqltest/$mms_tname$mms_table_to_use.MAI;
} }
if ($mms_compare_physically) if ($mms_compare_physically)
...@@ -41,8 +42,8 @@ if ($mms_compare_physically) ...@@ -41,8 +42,8 @@ if ($mms_compare_physically)
# So, do this only when testing REDO phase. # So, do this only when testing REDO phase.
# If UNDO phase, we nevertheless compare checksums # If UNDO phase, we nevertheless compare checksums
# (see maria_verify_recovery.inc). # (see maria_verify_recovery.inc).
--echo * compared t$mms_table_to_use to old version --echo * compared $mms_tname$mms_table_to_use to old version
diff_files $MYSQLTEST_VARDIR/master-data/mysqltest/t$mms_table_to_use.MAD $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/t$mms_table_to_use.MAD; diff_files $MYSQLTEST_VARDIR/master-data/mysqltest/$mms_tname$mms_table_to_use.MAD $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/$mms_tname$mms_table_to_use.MAD;
# index file not yet recovered # index file not yet recovered
# diff_files $MYSQLTEST_VARDIR/master-data/mysqltest/t$mms_table_to_use.MAI $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/t$mms_table_to_use.MAI; # diff_files $MYSQLTEST_VARDIR/master-data/mysqltest/$mms_tname$mms_table_to_use.MAI $MYSQLTEST_VARDIR/master-data/mysqltest_for_$mms_purpose/$mms_tname$mms_table_to_use.MAI;
} }
# Maria helper script # Maria helper script
# Copies clean tables' data and index file to other directory # Copies clean tables' data and index file to other directory
# Tables are t1...t[$mms_tables] # Tables are $mms_tname1...$mms_tname[$mms_tables]
# They are later used as a reference to see if recovery works. # They are later used as a reference to see if recovery works.
# API: # API:
# set $mms_tables to N, the script will cover tables mysqltest.t1,...tN # set $mms_tname to a string, and $mms_tables to a number N, the script will
# cover tables mysqltest.$mms_tname1,...$mms_tnameN
connection admin; connection admin;
...@@ -22,7 +23,7 @@ eval create database mysqltest_for_$mms_purpose; ...@@ -22,7 +23,7 @@ eval create database mysqltest_for_$mms_purpose;
while ($mms_table_to_use) while ($mms_table_to_use)
{ {
# to serve as a reference, table must be in a clean state # to serve as a reference, table must be in a clean state
eval flush table t$mms_table_to_use; eval flush table $mms_tname$mms_table_to_use;
-- source include/maria_make_snapshot.inc -- source include/maria_make_snapshot.inc
dec $mms_table_to_use; dec $mms_table_to_use;
} }
......
# Maria helper script # Maria helper script
# Copies tables' data and index file to other directory, and control file. # Copies tables' data and index file to other directory, and control file.
# Tables are t1...t[$mms_tables]. # Tables are $mms_tname1...$mms_tname[$mms_tables].
# Later, mysqld is shutdown, and that snapshot is put back into the # Later, mysqld is shutdown, and that snapshot is put back into the
# datadir, control file too ("flashing recovery's brain"), and recovery is let # datadir, control file too ("flashing recovery's brain"), and recovery is let
# to run on it (see maria_verify_recovery.inc). # to run on it (see maria_verify_recovery.inc).
# API: # API:
# set $mms_tables to N, the script will cover tables mysqltest.t1,...tN # set $mms_tname to a string, and $mms_tables to a number N, the script will
# cover tables mysqltest.$mms_tname1,...$mms_tnameN
connection admin; connection admin;
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
# Runs recovery, compare with expected table data. # Runs recovery, compare with expected table data.
# API: # API:
# 1) set $mms_tables to N, the script will cover tables mysqltest.t1,...tN # 1) set $mms_tname to a string, and $mms_tables to a number N, the script
# will cover tables mysqltest.$mms_tname1,...$mms_tnameN
# 2) set $mvr_debug_option to the crash way # 2) set $mvr_debug_option to the crash way
# 3) set $mvr_crash_statement to the statement which will trigger a crash # 3) set $mvr_crash_statement to the statement which will trigger a crash
# 4) set $mvr_restore_old_snapshot to 1 if you want recovery to run on # 4) set $mvr_restore_old_snapshot to 1 if you want recovery to run on
...@@ -77,10 +78,10 @@ let $mms_purpose=comparison; ...@@ -77,10 +78,10 @@ let $mms_purpose=comparison;
let $mms_compare_physically=$mms_compare_physically_save; let $mms_compare_physically=$mms_compare_physically_save;
while ($mms_table_to_use) while ($mms_table_to_use)
{ {
eval check table t$mms_table_to_use extended; eval check table $mms_tname$mms_table_to_use extended;
--echo * testing that checksum after recovery is as expected --echo * testing that checksum after recovery is as expected
let $new_checksum=`CHECKSUM TABLE t$mms_table_to_use`; let $new_checksum=`CHECKSUM TABLE $mms_tname$mms_table_to_use`;
let $old_checksum=`CHECKSUM TABLE mysqltest_for_$mms_purpose.t$mms_table_to_use`; let $old_checksum=`CHECKSUM TABLE mysqltest_for_$mms_purpose.$mms_tname$mms_table_to_use`;
# the $ text variables above are of the form "db.tablename\tchecksum", # the $ text variables above are of the form "db.tablename\tchecksum",
# as db differs, we use substring(). # as db differs, we use substring().
--disable_query_log --disable_query_log
......
...@@ -405,7 +405,12 @@ sub mtr_report_stats ($) { ...@@ -405,7 +405,12 @@ sub mtr_report_stats ($) {
# maria-recovery.test has warning about missing log file # maria-recovery.test has warning about missing log file
/File '.*maria_log.000.*' not found \(Errcode: 2\)/ or /File '.*maria_log.000.*' not found \(Errcode: 2\)/ or
# and about marked-corrupted table # and about marked-corrupted table
/Table '.\/mysqltest\/t1' is crashed, skipping it. Please repair it with maria_chk -r/ /Table '.\/mysqltest\/t_corrupted1' is crashed, skipping it. Please repair it with maria_chk -r/ or
# maria-recover.test corrupts tables on purpose
/Checking table: '.\/mysqltest\/t_corrupted2'/ or
/Recovering table: '.\/mysqltest\/t_corrupted2'/ or
/Table '.\/mysqltest\/t_corrupted2' is marked as crashed and should be repaired/ or
/Incorrect key file for table '.\/mysqltest\/t_corrupted2.MAI'; try to repair it/
) )
{ {
next; # Skip these lines next; # Skip these lines
......
drop table if exists t1, t2; drop table if exists t1, t2;
create temporary table initial
select variable_name,variable_value from
information_schema.global_status where variable_name like "Maria_pagecache_read%";
create table t1 ( create table t1 (
a int not null auto_increment, a int not null auto_increment,
b char(16) not null, b char(16) not null,
...@@ -46,24 +49,24 @@ count(*) ...@@ -46,24 +49,24 @@ count(*)
20672 20672
flush tables; flush tables;
flush status; flush status;
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211388 MARIA_PAGECACHE_READ_REQUESTS 211644
Maria_pagecache_reads 115 MARIA_PAGECACHE_READS 3
select count(*) from t1 where b = 'test1'; select count(*) from t1 where b = 'test1';
count(*) count(*)
4181 4181
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211414 MARIA_PAGECACHE_READ_REQUESTS 211926
Maria_pagecache_reads 122 MARIA_PAGECACHE_READS 11
select count(*) from t1 where b = 'test1'; select count(*) from t1 where b = 'test1';
count(*) count(*)
4181 4181
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211440 MARIA_PAGECACHE_READ_REQUESTS 212208
Maria_pagecache_reads 122 MARIA_PAGECACHE_READS 12
flush tables; flush tables;
flush status; flush status;
select @@preload_buffer_size; select @@preload_buffer_size;
...@@ -72,23 +75,23 @@ select @@preload_buffer_size; ...@@ -72,23 +75,23 @@ select @@preload_buffer_size;
load index into cache t1; load index into cache t1;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.t1 preload_keys status OK test.t1 preload_keys status OK
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211511 MARIA_PAGECACHE_READ_REQUESTS 212535
Maria_pagecache_reads 193 MARIA_PAGECACHE_READS 84
select count(*) from t1 where b = 'test1'; select count(*) from t1 where b = 'test1';
count(*) count(*)
4181 4181
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211537 MARIA_PAGECACHE_READ_REQUESTS 212817
Maria_pagecache_reads 193 MARIA_PAGECACHE_READS 85
flush tables; flush tables;
flush status; flush status;
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211537 MARIA_PAGECACHE_READ_REQUESTS 213073
Maria_pagecache_reads 193 MARIA_PAGECACHE_READS 86
set session preload_buffer_size=256*1024; set session preload_buffer_size=256*1024;
select @@preload_buffer_size; select @@preload_buffer_size;
@@preload_buffer_size @@preload_buffer_size
...@@ -96,23 +99,23 @@ select @@preload_buffer_size; ...@@ -96,23 +99,23 @@ select @@preload_buffer_size;
load index into cache t1 ignore leaves; load index into cache t1 ignore leaves;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.t1 preload_keys status OK test.t1 preload_keys status OK
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211608 MARIA_PAGECACHE_READ_REQUESTS 213400
Maria_pagecache_reads 264 MARIA_PAGECACHE_READS 158
select count(*) from t1 where b = 'test1'; select count(*) from t1 where b = 'test1';
count(*) count(*)
4181 4181
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211634 MARIA_PAGECACHE_READ_REQUESTS 213682
Maria_pagecache_reads 270 MARIA_PAGECACHE_READS 165
flush tables; flush tables;
flush status; flush status;
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211634 MARIA_PAGECACHE_READ_REQUESTS 213938
Maria_pagecache_reads 270 MARIA_PAGECACHE_READS 166
set session preload_buffer_size=1*1024; set session preload_buffer_size=1*1024;
select @@preload_buffer_size; select @@preload_buffer_size;
@@preload_buffer_size @@preload_buffer_size
...@@ -121,52 +124,53 @@ load index into cache t1, t2 key (primary,b) ignore leaves; ...@@ -121,52 +124,53 @@ load index into cache t1, t2 key (primary,b) ignore leaves;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.t1 preload_keys status OK test.t1 preload_keys status OK
test.t2 preload_keys status OK test.t2 preload_keys status OK
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211748 MARIA_PAGECACHE_READ_REQUESTS 214308
Maria_pagecache_reads 384 MARIA_PAGECACHE_READS 281
select count(*) from t1 where b = 'test1'; select count(*) from t1 where b = 'test1';
count(*) count(*)
4181 4181
select count(*) from t2 where b = 'test1'; select count(*) from t2 where b = 'test1';
count(*) count(*)
2584 2584
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211788 MARIA_PAGECACHE_READ_REQUESTS 214604
Maria_pagecache_reads 387 MARIA_PAGECACHE_READS 285
flush tables; flush tables;
flush status; flush status;
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211788 MARIA_PAGECACHE_READ_REQUESTS 214860
Maria_pagecache_reads 387 MARIA_PAGECACHE_READS 286
load index into cache t3, t2 key (primary,b) ; load index into cache t3, t2 key (primary,b) ;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.t3 preload_keys Error Table 'test.t3' doesn't exist test.t3 preload_keys Error Table 'test.t3' doesn't exist
test.t3 preload_keys error Corrupt test.t3 preload_keys error Corrupt
test.t2 preload_keys status OK test.t2 preload_keys status OK
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211831 MARIA_PAGECACHE_READ_REQUESTS 215159
Maria_pagecache_reads 430 MARIA_PAGECACHE_READS 330
flush tables; flush tables;
flush status; flush status;
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211831 MARIA_PAGECACHE_READ_REQUESTS 215415
Maria_pagecache_reads 430 MARIA_PAGECACHE_READS 331
load index into cache t3 key (b), t2 key (c) ; load index into cache t3 key (b), t2 key (c) ;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.t3 preload_keys Error Table 'test.t3' doesn't exist test.t3 preload_keys Error Table 'test.t3' doesn't exist
test.t3 preload_keys error Corrupt test.t3 preload_keys error Corrupt
test.t2 preload_keys Error Key 'c' doesn't exist in table 't2' test.t2 preload_keys Error Key 'c' doesn't exist in table 't2'
test.t2 preload_keys status Operation failed test.t2 preload_keys status Operation failed
show status like "maria_pagecache_read%"; select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
Variable_name Value variable_name g.variable_value-i.variable_value
Maria_pagecache_read_requests 211831 MARIA_PAGECACHE_READ_REQUESTS 215671
Maria_pagecache_reads 430 MARIA_PAGECACHE_READS 332
drop table t1, t2; drop table t1, t2;
drop temporary table initial;
show status like "key_read%"; show status like "key_read%";
Variable_name Value Variable_name Value
Key_read_requests 0 Key_read_requests 0
......
...@@ -37,13 +37,13 @@ set global maria_log_file_size=16777216; ...@@ -37,13 +37,13 @@ set global maria_log_file_size=16777216;
set global maria_checkpoint_interval=30; set global maria_checkpoint_interval=30;
SHOW ENGINE maria logs; SHOW ENGINE maria logs;
Type Name Status Type Name Status
maria master-data/maria_log.00000002 in use MARIA master-data/maria_log.00000002 in use
insert into t2 select * from t1; insert into t2 select * from t1;
insert into t1 select * from t2; insert into t1 select * from t2;
set global maria_checkpoint_interval=30; set global maria_checkpoint_interval=30;
SHOW ENGINE maria logs; SHOW ENGINE maria logs;
Type Name Status Type Name Status
maria master-data/maria_log.00000004 in use MARIA master-data/maria_log.00000004 in use
set global maria_log_file_size=16777216; set global maria_log_file_size=16777216;
select @@global.maria_log_file_size; select @@global.maria_log_file_size;
@@global.maria_log_file_size @@global.maria_log_file_size
...@@ -51,7 +51,7 @@ select @@global.maria_log_file_size; ...@@ -51,7 +51,7 @@ select @@global.maria_log_file_size;
set global maria_checkpoint_interval=30; set global maria_checkpoint_interval=30;
SHOW ENGINE maria logs; SHOW ENGINE maria logs;
Type Name Status Type Name Status
maria master-data/maria_log.00000004 in use MARIA master-data/maria_log.00000004 in use
set global maria_log_file_size=8388608; set global maria_log_file_size=8388608;
select @@global.maria_log_file_size; select @@global.maria_log_file_size;
@@global.maria_log_file_size @@global.maria_log_file_size
...@@ -61,32 +61,32 @@ insert into t1 select * from t2; ...@@ -61,32 +61,32 @@ insert into t1 select * from t2;
set global maria_checkpoint_interval=30; set global maria_checkpoint_interval=30;
SHOW ENGINE maria logs; SHOW ENGINE maria logs;
Type Name Status Type Name Status
maria master-data/maria_log.00000004 free MARIA master-data/maria_log.00000004 free
maria master-data/maria_log.00000005 free MARIA master-data/maria_log.00000005 free
maria master-data/maria_log.00000006 free MARIA master-data/maria_log.00000006 free
maria master-data/maria_log.00000007 free MARIA master-data/maria_log.00000007 free
maria master-data/maria_log.00000008 in use MARIA master-data/maria_log.00000008 in use
flush logs; flush logs;
SHOW ENGINE maria logs; SHOW ENGINE maria logs;
Type Name Status Type Name Status
maria master-data/maria_log.00000008 in use MARIA master-data/maria_log.00000008 in use
set global maria_log_file_size=16777216; set global maria_log_file_size=16777216;
set global maria_log_purge_type=external; set global maria_log_purge_type=external;
insert into t1 select * from t2; insert into t1 select * from t2;
set global maria_checkpoint_interval=30; set global maria_checkpoint_interval=30;
SHOW ENGINE maria logs; SHOW ENGINE maria logs;
Type Name Status Type Name Status
maria master-data/maria_log.00000008 free MARIA master-data/maria_log.00000008 free
maria master-data/maria_log.00000009 in use MARIA master-data/maria_log.00000009 in use
flush logs; flush logs;
SHOW ENGINE maria logs; SHOW ENGINE maria logs;
Type Name Status Type Name Status
maria master-data/maria_log.00000008 free MARIA master-data/maria_log.00000008 free
maria master-data/maria_log.00000009 in use MARIA master-data/maria_log.00000009 in use
set global maria_log_purge_type=immediate; set global maria_log_purge_type=immediate;
insert into t1 select * from t2; insert into t1 select * from t2;
set global maria_checkpoint_interval=30; set global maria_checkpoint_interval=30;
SHOW ENGINE maria logs; SHOW ENGINE maria logs;
Type Name Status Type Name Status
maria master-data/maria_log.00000011 in use MARIA master-data/maria_log.00000011 in use
drop table t1, t2; drop table t1, t2;
select @@global.maria_recover;
@@global.maria_recover
BACKUP
set global maria_recover=off;
select @@global.maria_recover;
@@global.maria_recover
OFF
set global maria_recover=default;
select @@global.maria_recover;
@@global.maria_recover
OFF
set global maria_recover=normal;
select @@global.maria_recover;
@@global.maria_recover
NORMAL
drop database if exists mysqltest;
create database mysqltest;
use mysqltest;
create table t1 (a varchar(1000), index(a)) engine=maria;
insert into t1 values("ThursdayMorningsMarket");
flush table t1;
insert into t1 select concat(a,'b') from t1 limit 1;
select * from t_corrupted2;
a
ThursdayMorningsMarket
Warnings:
Error 145 Table './mysqltest/t_corrupted2' is marked as crashed and should be repaired
Error 1194 Table 't_corrupted2' is marked as crashed and should be repaired
Error 1034 1 client is using or hasn't closed the table properly
Error 126 Incorrect key file for table './mysqltest/t_corrupted2.MAI'; try to repair it
Error 1034 Wrong base information on indexpage at page: 1
select * from t_corrupted2;
a
ThursdayMorningsMarket
drop database mysqltest;
...@@ -362,24 +362,24 @@ Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_par ...@@ -362,24 +362,24 @@ Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_par
t1 1 a 1 a A 1 NULL NULL YES BTREE t1 1 a 1 a A 1 NULL NULL YES BTREE
drop table t1; drop table t1;
* TEST of recovery when OPTIMIZE has replaced the index file and crash * TEST of recovery when OPTIMIZE has replaced the index file and crash
create table t1 (a varchar(100), key(a)) engine=maria; create table t_corrupted1 (a varchar(100), key(a)) engine=maria;
insert into t1 select (rand()) from t2; insert into t_corrupted1 select (rand()) from t2;
flush table t1; flush table t_corrupted1;
* copied t1 for comparison * copied t_corrupted1 for comparison
SET SESSION debug="+d,maria_flush_whole_log,maria_flush_whole_page_cache,maria_crash_sort_index"; SET SESSION debug="+d,maria_flush_whole_log,maria_flush_whole_page_cache,maria_crash_sort_index";
* crashing mysqld intentionally * crashing mysqld intentionally
optimize table t1; optimize table t_corrupted1;
ERROR HY000: Lost connection to MySQL server during query ERROR HY000: Lost connection to MySQL server during query
* recovery happens * recovery happens
check table t1 extended; check table t_corrupted1 extended;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
mysqltest.t1 check warning Table is marked as crashed and last repair failed mysqltest.t_corrupted1 check warning Table is marked as crashed and last repair failed
mysqltest.t1 check status OK mysqltest.t_corrupted1 check status OK
* testing that checksum after recovery is as expected * testing that checksum after recovery is as expected
Checksum-check Checksum-check
ok ok
use mysqltest; use mysqltest;
drop table t1, t2; drop table t_corrupted1, t2;
drop database mysqltest_for_feeding_recovery; drop database mysqltest_for_feeding_recovery;
drop database mysqltest_for_comparison; drop database mysqltest_for_comparison;
drop database mysqltest; drop database mysqltest;
...@@ -2121,6 +2121,7 @@ show variables like 'maria%'; ...@@ -2121,6 +2121,7 @@ show variables like 'maria%';
Variable_name Value Variable_name Value
maria_block_size 8192 maria_block_size 8192
maria_checkpoint_interval 30 maria_checkpoint_interval 30
maria_force_start_after_recovery_failures 0
maria_log_file_size 4294959104 maria_log_file_size 4294959104
maria_log_purge_type immediate maria_log_purge_type immediate
maria_max_sort_file_size 9223372036854775807 maria_max_sort_file_size 9223372036854775807
...@@ -2128,6 +2129,7 @@ maria_page_checksum OFF ...@@ -2128,6 +2129,7 @@ maria_page_checksum OFF
maria_pagecache_age_threshold 300 maria_pagecache_age_threshold 300
maria_pagecache_buffer_size 8388600 maria_pagecache_buffer_size 8388600
maria_pagecache_division_limit 100 maria_pagecache_division_limit 100
maria_recover OFF
maria_repair_threads 1 maria_repair_threads 1
maria_sort_buffer_size 8388608 maria_sort_buffer_size 8388608
maria_stats_method nulls_unequal maria_stats_method nulls_unequal
......
...@@ -19,4 +19,4 @@ ctype_create : Bug#32965 main.ctype_create fails ...@@ -19,4 +19,4 @@ ctype_create : Bug#32965 main.ctype_create fails
status : Bug#32966 main.status fails status : Bug#32966 main.status fails
ps_ddl : Bug#12093 2007-12-14 pending WL#4165 / WL#4166 ps_ddl : Bug#12093 2007-12-14 pending WL#4165 / WL#4166
csv_alter_table : Bug#33696 2008-01-21 pcrews no .result file - bug allows NULL columns in CSV tables csv_alter_table : Bug#33696 2008-01-21 pcrews no .result file - bug allows NULL columns in CSV tables
maria-preload : Bug#34911 unrepeatable output of SHOW STATUS maria-preload : Bug#35107 crashes
...@@ -8,6 +8,11 @@ ...@@ -8,6 +8,11 @@
drop table if exists t1, t2; drop table if exists t1, t2;
--enable_warnings --enable_warnings
# Work around BUG#34911 "FLUSH STATUS doesn't flush what it should":
# compute differences in status variables before and after relevant queries
create temporary table initial
select variable_name,variable_value from
information_schema.global_status where variable_name like "Maria_pagecache_read%";
# we don't use block-format because we want page cache stats # we don't use block-format because we want page cache stats
# about indices and not data pages. # about indices and not data pages.
...@@ -59,50 +64,50 @@ select count(*) from t1; ...@@ -59,50 +64,50 @@ select count(*) from t1;
select count(*) from t2; select count(*) from t2;
flush tables; flush status; flush tables; flush status;
show status like "maria_pagecache_read%"; let $show_stat=select g.variable_name,g.variable_value-i.variable_value from information_schema.global_status as g,initial as i where g.variable_name like "Maria_pagecache_read%" and g.variable_name=i.variable_name order by g.variable_name desc;
eval $show_stat;
select count(*) from t1 where b = 'test1'; select count(*) from t1 where b = 'test1';
show status like "maria_pagecache_read%"; eval $show_stat;
select count(*) from t1 where b = 'test1'; select count(*) from t1 where b = 'test1';
show status like "maria_pagecache_read%"; eval $show_stat;
flush tables; flush status; flush tables; flush status;
select @@preload_buffer_size; select @@preload_buffer_size;
load index into cache t1; load index into cache t1;
show status like "maria_pagecache_read%"; eval $show_stat;
select count(*) from t1 where b = 'test1'; select count(*) from t1 where b = 'test1';
show status like "maria_pagecache_read%"; eval $show_stat;
flush tables; flush status; flush tables; flush status;
show status like "maria_pagecache_read%"; eval $show_stat;
set session preload_buffer_size=256*1024; set session preload_buffer_size=256*1024;
select @@preload_buffer_size; select @@preload_buffer_size;
load index into cache t1 ignore leaves; load index into cache t1 ignore leaves;
show status like "maria_pagecache_read%"; eval $show_stat;
select count(*) from t1 where b = 'test1'; select count(*) from t1 where b = 'test1';
show status like "maria_pagecache_read%"; eval $show_stat;
flush tables; flush status; flush tables; flush status;
show status like "maria_pagecache_read%"; eval $show_stat;
set session preload_buffer_size=1*1024; set session preload_buffer_size=1*1024;
select @@preload_buffer_size; select @@preload_buffer_size;
load index into cache t1, t2 key (primary,b) ignore leaves; load index into cache t1, t2 key (primary,b) ignore leaves;
show status like "maria_pagecache_read%"; eval $show_stat;
select count(*) from t1 where b = 'test1'; select count(*) from t1 where b = 'test1';
select count(*) from t2 where b = 'test1'; select count(*) from t2 where b = 'test1';
show status like "maria_pagecache_read%"; eval $show_stat;
flush tables; flush status; flush tables; flush status;
show status like "maria_pagecache_read%"; eval $show_stat;
load index into cache t3, t2 key (primary,b) ; load index into cache t3, t2 key (primary,b) ;
show status like "maria_pagecache_read%"; eval $show_stat;
flush tables; flush status; flush tables; flush status;
show status like "maria_pagecache_read%"; eval $show_stat;
load index into cache t3 key (b), t2 key (c) ; load index into cache t3 key (b), t2 key (c) ;
show status like "maria_pagecache_read%"; eval $show_stat;
drop table t1, t2; drop table t1, t2;
drop temporary table initial;
# check that Maria didn't use key cache # check that Maria didn't use key cache
show status like "key_read%"; show status like "key_read%";
--maria-recover=backup --maria-log-dir-path=../tmp
# Test of the --maria-recover option.
--source include/have_maria.inc
select @@global.maria_recover;
set global maria_recover=off;
select @@global.maria_recover;
set global maria_recover=default;
select @@global.maria_recover;
set global maria_recover=normal;
select @@global.maria_recover;
--disable_warnings
drop database if exists mysqltest;
--enable_warnings
create database mysqltest;
use mysqltest;
create table t1 (a varchar(1000), index(a)) engine=maria;
insert into t1 values("ThursdayMorningsMarket");
flush table t1; # put index page on disk
insert into t1 select concat(a,'b') from t1 limit 1;
# now t1 has its open_count>0 and so will t2_corrupted.
# It is not named t2 because the corruption messages which will be put
# in the error log need to be detected in mtr_process.pl, and we want
# a specific name to do specific detection (don't want to ignore
# any corruption messages of other tests using "t2" as table).
copy_file $MYSQLTEST_VARDIR/master-data/mysqltest/t1.frm $MYSQLTEST_VARDIR/master-data/mysqltest/t_corrupted2.frm;
copy_file $MYSQLTEST_VARDIR/master-data/mysqltest/t1.MAD $MYSQLTEST_VARDIR/master-data/mysqltest/t_corrupted2.MAD;
copy_file $MYSQLTEST_VARDIR/master-data/mysqltest/t1.MAI $MYSQLTEST_VARDIR/master-data/mysqltest/t_corrupted2.MAI;
# Ruin the index file.
# If maria-block-size is smaller than the default, the corruption
# messages will differ.
perl;
use strict;
use warnings;
my $fname= "$ENV{'MYSQLTEST_VARDIR'}/master-data/mysqltest/t_corrupted2.MAI";
open(FILE, "+<", $fname) or die;
my $whatever= ("\xAB" x 100);
sysseek (FILE, 8192, 0) or die;
syswrite (FILE, $whatever) or die;
close FILE;
EOF
select * from t_corrupted2; # should show corruption and repair messages
select * from t_corrupted2; # should show just rows
drop database mysqltest;
...@@ -15,6 +15,7 @@ set global maria_log_file_size=4294967295; ...@@ -15,6 +15,7 @@ set global maria_log_file_size=4294967295;
drop database if exists mysqltest; drop database if exists mysqltest;
--enable_warnings --enable_warnings
create database mysqltest; create database mysqltest;
let $mms_tname=t;
# Include scripts can perform SQL. For it to not influence the main test # Include scripts can perform SQL. For it to not influence the main test
# they use a separate connection. This way if they use a DDL it would # they use a separate connection. This way if they use a DDL it would
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
drop database if exists mysqltest; drop database if exists mysqltest;
--enable_warnings --enable_warnings
create database mysqltest; create database mysqltest;
let $mms_tname=t;
# Include scripts can perform SQL. For it to not influence the main test # Include scripts can perform SQL. For it to not influence the main test
# they use a separate connection. This way if they use a DDL it would # they use a separate connection. This way if they use a DDL it would
......
...@@ -14,6 +14,7 @@ let $MARIA_LOG=.; ...@@ -14,6 +14,7 @@ let $MARIA_LOG=.;
drop database if exists mysqltest; drop database if exists mysqltest;
--enable_warnings --enable_warnings
create database mysqltest; create database mysqltest;
let $mms_tname=t;
# Include scripts can perform SQL. For it to not influence the main test # Include scripts can perform SQL. For it to not influence the main test
# they use a separate connection. This way if they use a DDL it would # they use a separate connection. This way if they use a DDL it would
......
...@@ -12,6 +12,7 @@ let $MARIA_LOG=../tmp; ...@@ -12,6 +12,7 @@ let $MARIA_LOG=../tmp;
drop database if exists mysqltest; drop database if exists mysqltest;
--enable_warnings --enable_warnings
create database mysqltest; create database mysqltest;
let $mms_tname=t;
# Include scripts can perform SQL. For it to not influence the main test # Include scripts can perform SQL. For it to not influence the main test
# they use a separate connection. This way if they use a DDL it would # they use a separate connection. This way if they use a DDL it would
...@@ -297,19 +298,25 @@ show keys from t1; # should be enabled ...@@ -297,19 +298,25 @@ show keys from t1; # should be enabled
drop table t1; drop table t1;
--echo * TEST of recovery when OPTIMIZE has replaced the index file and crash --echo * TEST of recovery when OPTIMIZE has replaced the index file and crash
create table t1 (a varchar(100), key(a)) engine=maria; create table t_corrupted1 (a varchar(100), key(a)) engine=maria;
# we use a special name because this test portion will generate
# corruption warnings, which we tell mtr_report.pl to ignore by
# putting the message in mtr_report.pl, but we don't want to it ignore
# corruption messages of other tests, hence the special name
# 't_corrupted' and not just 't'.
let $mms_tname=t_corrupted;
let $mvr_restore_old_snapshot=0; let $mvr_restore_old_snapshot=0;
let $mms_compare_physically=0; let $mms_compare_physically=0;
let $mvr_crash_statement= optimize table t1; let $mvr_crash_statement= optimize table t_corrupted1;
let $mvr_debug_option="+d,maria_flush_whole_log,maria_flush_whole_page_cache,maria_crash_sort_index"; let $mvr_debug_option="+d,maria_flush_whole_log,maria_flush_whole_page_cache,maria_crash_sort_index";
insert into t1 select (rand()) from t2; insert into t_corrupted1 select (rand()) from t2;
-- source include/maria_make_snapshot_for_comparison.inc -- source include/maria_make_snapshot_for_comparison.inc
# Recovery will not fix the table, but we expect to see it marked # Recovery will not fix the table, but we expect to see it marked
# "crashed on repair". # "crashed on repair".
# Because crash is mild, the table is actually not corrupted, so the # Because crash is mild, the table is actually not corrupted, so the
# "check table extended" done below fixes the table. # "check table extended" done below fixes the table.
-- source include/maria_verify_recovery.inc -- source include/maria_verify_recovery.inc
drop table t1, t2; drop table t_corrupted1, t2;
# clean up everything # clean up everything
let $mms_purpose=feeding_recovery; let $mms_purpose=feeding_recovery;
......
...@@ -49,13 +49,11 @@ ulong pagecache_division_limit, pagecache_age_threshold; ...@@ -49,13 +49,11 @@ ulong pagecache_division_limit, pagecache_age_threshold;
ulonglong pagecache_buffer_size; ulonglong pagecache_buffer_size;
/** /**
@todo For now there is no way for a user to set a different value of As the auto-repair is initiated when opened from the SQL layer
maria_recover_options, i.e. auto-check-and-repair is always disabled. (open_unireg_entry(), check_and_repair()), it does not happen when Maria's
We could enable it. As the auto-repair is initiated when opened from the Recovery internally opens the table to apply log records to it, which is
SQL layer (open_unireg_entry(), check_and_repair()), it does not happen good. It would happen only after Recovery, if the table is still
when Maria's Recovery internally opens the table to apply log records to corrupted.
it, which is good. It would happen only after Recovery, if the table is
still corrupted.
*/ */
ulong maria_recover_options= HA_RECOVER_NONE; ulong maria_recover_options= HA_RECOVER_NONE;
handlerton *maria_hton; handlerton *maria_hton;
...@@ -63,7 +61,14 @@ handlerton *maria_hton; ...@@ -63,7 +61,14 @@ handlerton *maria_hton;
/* bits in maria_recover_options */ /* bits in maria_recover_options */
const char *maria_recover_names[]= const char *maria_recover_names[]=
{ {
"DEFAULT", "BACKUP", "FORCE", "QUICK", NullS /*
Compared to MyISAM, "default" was renamed to "normal" as it collided with
SET var=default which sets to the var's default i.e. what happens when the
var is not set i.e. HA_RECOVER_NONE.
Another change is that OFF is used to disable, not ""; this is to have OFF
display in SHOW VARIABLES which is better than "".
*/
"OFF", "NORMAL", "BACKUP", "FORCE", "QUICK", NullS
}; };
TYPELIB maria_recover_typelib= TYPELIB maria_recover_typelib=
{ {
...@@ -103,11 +108,13 @@ TYPELIB maria_sync_log_dir_typelib= ...@@ -103,11 +108,13 @@ TYPELIB maria_sync_log_dir_typelib=
maria_sync_log_dir_names, NULL maria_sync_log_dir_names, NULL
}; };
/** @brief Interval between background checkpoints in seconds */ /** Interval between background checkpoints in seconds */
static ulong checkpoint_interval; static ulong checkpoint_interval;
static void update_checkpoint_interval(MYSQL_THD thd, static void update_checkpoint_interval(MYSQL_THD thd,
struct st_mysql_sys_var *var, struct st_mysql_sys_var *var,
void *var_ptr, const void *save); void *var_ptr, const void *save);
/** After that many consecutive recovery failures, remove logs */
static ulong force_start_after_recovery_failures;
static void update_log_file_size(MYSQL_THD thd, static void update_log_file_size(MYSQL_THD thd,
struct st_mysql_sys_var *var, struct st_mysql_sys_var *var,
void *var_ptr, const void *save); void *var_ptr, const void *save);
...@@ -124,6 +131,17 @@ static MYSQL_SYSVAR_ULONG(checkpoint_interval, checkpoint_interval, ...@@ -124,6 +131,17 @@ static MYSQL_SYSVAR_ULONG(checkpoint_interval, checkpoint_interval,
" 'no automatic checkpoints' which makes sense only for testing.", " 'no automatic checkpoints' which makes sense only for testing.",
NULL, update_checkpoint_interval, 30, 0, UINT_MAX, 1); NULL, update_checkpoint_interval, 30, 0, UINT_MAX, 1);
static MYSQL_SYSVAR_ULONG(force_start_after_recovery_failures,
force_start_after_recovery_failures,
/*
Read-only because setting it on the fly has no useful effect,
should be set on command-line.
*/
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
"Number of consecutive log recovery failures after which logs will be"
" automatically deleted to cure the problem; 0 (the default) disables"
" the feature.", NULL, NULL, 0, 0, UINT_MAX8, 1);
static MYSQL_SYSVAR_BOOL(page_checksum, maria_page_checksums, 0, static MYSQL_SYSVAR_BOOL(page_checksum, maria_page_checksums, 0,
"Maintain page checksums (can be overridden per table " "Maintain page checksums (can be overridden per table "
"with PAGE_CHECKSUM clause in CREATE TABLE)", 0, 0, 1); "with PAGE_CHECKSUM clause in CREATE TABLE)", 0, 0, 1);
...@@ -175,6 +193,12 @@ static MYSQL_SYSVAR_ULONG(pagecache_division_limit, pagecache_division_limit, ...@@ -175,6 +193,12 @@ static MYSQL_SYSVAR_ULONG(pagecache_division_limit, pagecache_division_limit,
"The minimum percentage of warm blocks in key cache", 0, 0, "The minimum percentage of warm blocks in key cache", 0, 0,
100, 1, 100, 1); 100, 1, 100, 1);
static MYSQL_SYSVAR_ENUM(recover, maria_recover_options, PLUGIN_VAR_OPCMDARG,
"Specifies how corrupted tables should be automatically repaired."
" Possible values are \"NORMAL\" (the default), \"BACKUP\", \"FORCE\","
" \"QUICK\", or \"OFF\" which is like not using the option.",
NULL, NULL, HA_RECOVER_NONE, &maria_recover_typelib);
static MYSQL_THDVAR_ULONG(repair_threads, PLUGIN_VAR_RQCMDARG, static MYSQL_THDVAR_ULONG(repair_threads, PLUGIN_VAR_RQCMDARG,
"Number of threads to use when repairing maria tables. The value of 1 " "Number of threads to use when repairing maria tables. The value of 1 "
"disables parallel repair.", "disables parallel repair.",
...@@ -186,7 +210,7 @@ static MYSQL_THDVAR_ULONG(sort_buffer_size, PLUGIN_VAR_RQCMDARG, ...@@ -186,7 +210,7 @@ static MYSQL_THDVAR_ULONG(sort_buffer_size, PLUGIN_VAR_RQCMDARG,
0, 0, 8192*1024, 4, ~0L, 1); 0, 0, 8192*1024, 4, ~0L, 1);
static MYSQL_THDVAR_ENUM(stats_method, PLUGIN_VAR_RQCMDARG, static MYSQL_THDVAR_ENUM(stats_method, PLUGIN_VAR_RQCMDARG,
"Specifies how maria index statistics collection code should threat " "Specifies how maria index statistics collection code should treat "
"NULLs. Possible values are \"nulls_unequal\", \"nulls_equal\", " "NULLs. Possible values are \"nulls_unequal\", \"nulls_equal\", "
"and \"nulls_ignored\".", 0, 0, 0, &maria_stats_method_typelib); "and \"nulls_ignored\".", 0, 0, 0, &maria_stats_method_typelib);
...@@ -870,6 +894,12 @@ int ha_maria::open(const char *name, int mode, uint test_if_locked) ...@@ -870,6 +894,12 @@ int ha_maria::open(const char *name, int mode, uint test_if_locked)
test_if_locked|= HA_OPEN_MMAP; test_if_locked|= HA_OPEN_MMAP;
#endif #endif
if (unlikely(maria_recover_options != HA_RECOVER_NONE))
{
/* user asked to trigger a repair if table was not properly closed */
test_if_locked|= HA_OPEN_ABORT_IF_CRASHED;
}
if (!(file= maria_open(name, mode, test_if_locked | HA_OPEN_FROM_SQL_LAYER))) if (!(file= maria_open(name, mode, test_if_locked | HA_OPEN_FROM_SQL_LAYER)))
return (my_errno ? my_errno : -1); return (my_errno ? my_errno : -1);
...@@ -2728,7 +2758,7 @@ bool maria_show_status(handlerton *hton, ...@@ -2728,7 +2758,7 @@ bool maria_show_status(handlerton *hton,
stat_print_fn *print, stat_print_fn *print,
enum ha_stat_type stat) enum ha_stat_type stat)
{ {
char engine_name[]= "maria"; const LEX_STRING *engine_name= hton_name(hton);
switch (stat) { switch (stat) {
case HA_ENGINE_LOGS: case HA_ENGINE_LOGS:
{ {
...@@ -2745,8 +2775,8 @@ bool maria_show_status(handlerton *hton, ...@@ -2745,8 +2775,8 @@ bool maria_show_status(handlerton *hton,
if (first_file == 0) if (first_file == 0)
{ {
const char error[]= "error"; const char error[]= "error";
print(thd, engine_name, sizeof(engine_name), print(thd, engine_name->str, engine_name->length,
STRING_WITH_LEN(""), error, sizeof(error)); STRING_WITH_LEN(""), error, sizeof(error) - 1);
break; break;
} }
...@@ -2762,7 +2792,7 @@ bool maria_show_status(handlerton *hton, ...@@ -2762,7 +2792,7 @@ bool maria_show_status(handlerton *hton,
if (!(stat= my_stat(file, &stat_buff, MYF(MY_WME)))) if (!(stat= my_stat(file, &stat_buff, MYF(MY_WME))))
{ {
status= error; status= error;
status_len= sizeof(error); status_len= sizeof(error) - 1;
length= my_snprintf(object, SHOW_MSG_LEN, "Size unknown ; %s", file); length= my_snprintf(object, SHOW_MSG_LEN, "Size unknown ; %s", file);
} }
else else
...@@ -2770,23 +2800,23 @@ bool maria_show_status(handlerton *hton, ...@@ -2770,23 +2800,23 @@ bool maria_show_status(handlerton *hton,
if (first_needed == 0) if (first_needed == 0)
{ {
status= unknown; status= unknown;
status_len= sizeof(unknown); status_len= sizeof(unknown) - 1;
} }
else if (i < first_needed) else if (i < first_needed)
{ {
status= unneeded; status= unneeded;
status_len= sizeof(unneeded); status_len= sizeof(unneeded) - 1;
} }
else else
{ {
status= needed; status= needed;
status_len= sizeof(needed); status_len= sizeof(needed) - 1;
} }
length= my_snprintf(object, SHOW_MSG_LEN, "Size %12lu ; %s", length= my_snprintf(object, SHOW_MSG_LEN, "Size %12lu ; %s",
(ulong) stat->st_size, file); (ulong) stat->st_size, file);
} }
print(thd, engine_name, sizeof(engine_name), print(thd, engine_name->str, engine_name->length,
object, length, status, status_len); object, length, status, status_len);
} }
break; break;
...@@ -2799,9 +2829,90 @@ bool maria_show_status(handlerton *hton, ...@@ -2799,9 +2829,90 @@ bool maria_show_status(handlerton *hton,
return 0; return 0;
} }
/**
Callback to delete all logs in directory. This is lower-level than other
functions in ma_loghandler.c which delete logs, as it does not rely on
translog_init() having been called first.
@param directory directory where file is
@param filename base name of the file to delete
*/
static my_bool translog_callback_delete_all(const char *directory,
const char *filename)
{
char complete_name[FN_REFLEN];
fn_format(complete_name, filename, directory, "", MYF(MY_UNPACK_FILENAME));
return my_delete(complete_name, MYF(MY_WME));
}
/**
Helper function for option maria-force-start-after-recovery-failures.
Deletes logs if too many failures. Otherwise, increments the counter of
failures in the control file.
Notice how this has to be called _before_ translog_init() (if log is
corrupted, translog_init() might crash the server, so we need to remove logs
before).
@param log_dir directory where logs to be deleted are
*/
static int mark_recovery_start(const char* log_dir)
{
int res;
DBUG_ENTER("mark_recovery_start");
if (unlikely(maria_recover_options == HA_RECOVER_NONE))
ma_message_no_user(ME_JUST_WARNING, "Please consider using option"
" --maria-recover[=...] to automatically check and"
" repair tables when logs are removed by option"
" --maria-force-start-after-recovery-failures=#");
if (recovery_failures >= force_start_after_recovery_failures)
{
/*
Remove logs which cause the problem; keep control file which has
critical info like uuid, max_trid (removing control file may make
correct tables look corrupted!).
*/
char msg[100];
res= translog_walk_filenames(log_dir, &translog_callback_delete_all);
my_snprintf(msg, sizeof(msg),
"%s logs after %u consecutive failures of"
" recovery from logs",
(res ? "failed to remove some" : "removed all"),
recovery_failures);
ma_message_no_user((res ? 0 : ME_JUST_WARNING), msg);
}
else
res= ma_control_file_write_and_force(last_checkpoint_lsn, last_logno,
max_trid_in_control_file,
recovery_failures + 1);
DBUG_RETURN(res);
}
/**
Helper function for option maria-force-start-after-recovery-failures.
Records in the control file that recovery was a success, so that it's not
counted for maria-force-start-after-recovery-failures.
*/
static int mark_recovery_success(void)
{
/* success of recovery, reset recovery_failures: */
int res;
DBUG_ENTER("mark_recovery_success");
res= ma_control_file_write_and_force(last_checkpoint_lsn, last_logno,
max_trid_in_control_file, 0);
DBUG_RETURN(res);
}
static int ha_maria_init(void *p) static int ha_maria_init(void *p)
{ {
int res; int res;
const char *log_dir= maria_data_root;
maria_hton= (handlerton *)p; maria_hton= (handlerton *)p;
maria_hton->state= SHOW_OPTION_YES; maria_hton->state= SHOW_OPTION_YES;
maria_hton->db_type= DB_TYPE_UNKNOWN; maria_hton->db_type= DB_TYPE_UNKNOWN;
...@@ -2816,6 +2927,8 @@ static int ha_maria_init(void *p) ...@@ -2816,6 +2927,8 @@ static int ha_maria_init(void *p)
bzero(maria_log_pagecache, sizeof(*maria_log_pagecache)); bzero(maria_log_pagecache, sizeof(*maria_log_pagecache));
maria_tmpdir= &mysql_tmpdir_list; /* For REDO */ maria_tmpdir= &mysql_tmpdir_list; /* For REDO */
res= maria_init() || ma_control_file_open(TRUE, TRUE) || res= maria_init() || ma_control_file_open(TRUE, TRUE) ||
((force_start_after_recovery_failures != 0) &&
mark_recovery_start(log_dir)) ||
!init_pagecache(maria_pagecache, !init_pagecache(maria_pagecache,
(size_t) pagecache_buffer_size, pagecache_division_limit, (size_t) pagecache_buffer_size, pagecache_division_limit,
pagecache_age_threshold, maria_block_size, 0) || pagecache_age_threshold, maria_block_size, 0) ||
...@@ -2825,7 +2938,8 @@ static int ha_maria_init(void *p) ...@@ -2825,7 +2938,8 @@ static int ha_maria_init(void *p)
translog_init(maria_data_root, log_file_size, translog_init(maria_data_root, log_file_size,
MYSQL_VERSION_ID, server_id, maria_log_pagecache, MYSQL_VERSION_ID, server_id, maria_log_pagecache,
TRANSLOG_DEFAULT_FLAGS, 0) || TRANSLOG_DEFAULT_FLAGS, 0) ||
maria_recover() || maria_recovery_from_log() ||
((force_start_after_recovery_failures != 0) && mark_recovery_success()) ||
ma_checkpoint_init(checkpoint_interval); ma_checkpoint_init(checkpoint_interval);
maria_multi_threaded= TRUE; maria_multi_threaded= TRUE;
return res ? HA_ERR_INITIALIZATION : 0; return res ? HA_ERR_INITIALIZATION : 0;
...@@ -2913,6 +3027,7 @@ my_bool ha_maria::register_query_cache_table(THD *thd, char *table_name, ...@@ -2913,6 +3027,7 @@ my_bool ha_maria::register_query_cache_table(THD *thd, char *table_name,
static struct st_mysql_sys_var* system_variables[]= { static struct st_mysql_sys_var* system_variables[]= {
MYSQL_SYSVAR(block_size), MYSQL_SYSVAR(block_size),
MYSQL_SYSVAR(checkpoint_interval), MYSQL_SYSVAR(checkpoint_interval),
MYSQL_SYSVAR(force_start_after_recovery_failures),
MYSQL_SYSVAR(page_checksum), MYSQL_SYSVAR(page_checksum),
MYSQL_SYSVAR(log_dir_path), MYSQL_SYSVAR(log_dir_path),
MYSQL_SYSVAR(log_file_size), MYSQL_SYSVAR(log_file_size),
...@@ -2921,6 +3036,7 @@ static struct st_mysql_sys_var* system_variables[]= { ...@@ -2921,6 +3036,7 @@ static struct st_mysql_sys_var* system_variables[]= {
MYSQL_SYSVAR(pagecache_age_threshold), MYSQL_SYSVAR(pagecache_age_threshold),
MYSQL_SYSVAR(pagecache_buffer_size), MYSQL_SYSVAR(pagecache_buffer_size),
MYSQL_SYSVAR(pagecache_division_limit), MYSQL_SYSVAR(pagecache_division_limit),
MYSQL_SYSVAR(recover),
MYSQL_SYSVAR(repair_threads), MYSQL_SYSVAR(repair_threads),
MYSQL_SYSVAR(sort_buffer_size), MYSQL_SYSVAR(sort_buffer_size),
MYSQL_SYSVAR(stats_method), MYSQL_SYSVAR(stats_method),
......
...@@ -245,7 +245,8 @@ static int really_execute_checkpoint(void) ...@@ -245,7 +245,8 @@ static int really_execute_checkpoint(void)
that log was flushed before we write to the control file). that log was flushed before we write to the control file).
*/ */
if (unlikely(ma_control_file_write_and_force(lsn, last_logno, if (unlikely(ma_control_file_write_and_force(lsn, last_logno,
max_trid_in_control_file))) max_trid_in_control_file,
recovery_failures)))
{ {
translog_unlock(); translog_unlock();
goto err; goto err;
......
...@@ -39,6 +39,8 @@ Start of changeable part: ...@@ -39,6 +39,8 @@ Start of changeable part:
- Checksum of changeable part - Checksum of changeable part
- LSN of last checkpoint - LSN of last checkpoint
- Number of last log file - Number of last log file
- Max trid in control file (since Maria 1.5 May 2008)
- Number of consecutive recovery failures (since Maria 1.5 May 2008)
..... Here we can add new variables without changing format ..... Here we can add new variables without changing format
The idea is that one can add new variables to the control file and still The idea is that one can add new variables to the control file and still
...@@ -80,7 +82,9 @@ one should increment the control file version number. ...@@ -80,7 +82,9 @@ one should increment the control file version number.
#define CF_FILENO_SIZE 4 #define CF_FILENO_SIZE 4
#define CF_MAX_TRID_OFFSET (CF_FILENO_OFFSET + CF_FILENO_SIZE) #define CF_MAX_TRID_OFFSET (CF_FILENO_OFFSET + CF_FILENO_SIZE)
#define CF_MAX_TRID_SIZE TRANSID_SIZE #define CF_MAX_TRID_SIZE TRANSID_SIZE
#define CF_CHANGEABLE_TOTAL_SIZE (CF_MAX_TRID_OFFSET + CF_MAX_TRID_SIZE) #define CF_RECOV_FAIL_OFFSET (CF_MAX_TRID_OFFSET + CF_MAX_TRID_SIZE)
#define CF_RECOV_FAIL_SIZE 1
#define CF_CHANGEABLE_TOTAL_SIZE (CF_RECOV_FAIL_OFFSET + CF_RECOV_FAIL_SIZE)
/* /*
The following values should not be changed, except when changing version The following values should not be changed, except when changing version
...@@ -108,6 +112,12 @@ uint32 last_logno= FILENO_IMPOSSIBLE; ...@@ -108,6 +112,12 @@ uint32 last_logno= FILENO_IMPOSSIBLE;
*/ */
TrID max_trid_in_control_file= 0; TrID max_trid_in_control_file= 0;
/**
Number of consecutive log or recovery failures. Reset to 0 after recovery's
success.
*/
uint8 recovery_failures= 0;
/** /**
@brief If log's lock should be asserted when writing to control file. @brief If log's lock should be asserted when writing to control file.
...@@ -188,7 +198,7 @@ static CONTROL_FILE_ERROR create_control_file(const char *name, ...@@ -188,7 +198,7 @@ static CONTROL_FILE_ERROR create_control_file(const char *name,
/* init the file with these "undefined" values */ /* init the file with these "undefined" values */
DBUG_RETURN(ma_control_file_write_and_force(LSN_IMPOSSIBLE, DBUG_RETURN(ma_control_file_write_and_force(LSN_IMPOSSIBLE,
FILENO_IMPOSSIBLE, 0)); FILENO_IMPOSSIBLE, 0, 0));
} }
...@@ -420,6 +430,9 @@ CONTROL_FILE_ERROR ma_control_file_open(my_bool create_if_missing, ...@@ -420,6 +430,9 @@ CONTROL_FILE_ERROR ma_control_file_open(my_bool create_if_missing,
if (new_cf_changeable_size >= (CF_MAX_TRID_OFFSET + CF_MAX_TRID_SIZE)) if (new_cf_changeable_size >= (CF_MAX_TRID_OFFSET + CF_MAX_TRID_SIZE))
max_trid_in_control_file= max_trid_in_control_file=
transid_korr(buffer + new_cf_create_time_size + CF_MAX_TRID_OFFSET); transid_korr(buffer + new_cf_create_time_size + CF_MAX_TRID_OFFSET);
if (new_cf_changeable_size >= (CF_RECOV_FAIL_OFFSET + CF_RECOV_FAIL_SIZE))
recovery_failures=
(buffer + new_cf_create_time_size + CF_RECOV_FAIL_OFFSET)[0];
ok: ok:
DBUG_RETURN(0); DBUG_RETURN(0);
...@@ -436,19 +449,21 @@ CONTROL_FILE_ERROR ma_control_file_open(my_bool create_if_missing, ...@@ -436,19 +449,21 @@ CONTROL_FILE_ERROR ma_control_file_open(my_bool create_if_missing,
/* /*
Write information durably to the control file; stores this information into Write information durably to the control file; stores this information into
the last_checkpoint_lsn, last_logno, max_trid_in_control_file global the last_checkpoint_lsn, last_logno, max_trid_in_control_file,
variables. recovery_failures global variables.
Called when we have created a new log (after syncing this log's creation), Called when we have created a new log (after syncing this log's creation),
when we have written a checkpoint (after syncing this log record), and at when we have written a checkpoint (after syncing this log record), at
shutdown (for storing trid in case logs are soon removed by user). shutdown (for storing trid in case logs are soon removed by user), and
before and after recovery (to store recovery_failures).
Variables last_checkpoint_lsn and last_logno must be protected by caller Variables last_checkpoint_lsn and last_logno must be protected by caller
using log's lock, unless this function is called at startup. using log's lock, unless this function is called at startup.
SYNOPSIS SYNOPSIS
ma_control_file_write_and_force() ma_control_file_write_and_force()
checkpoint_lsn LSN of last checkpoint last_checkpoint_lsn_arg LSN of last checkpoint
logno last log file number last_logno_arg last log file number
trid maximum transaction longid. max_trid_arg maximum transaction longid
recovery_failures_arg consecutive recovery failures
NOTE NOTE
We always want to do one single my_pwrite() here to be as atomic as We always want to do one single my_pwrite() here to be as atomic as
...@@ -459,17 +474,26 @@ CONTROL_FILE_ERROR ma_control_file_open(my_bool create_if_missing, ...@@ -459,17 +474,26 @@ CONTROL_FILE_ERROR ma_control_file_open(my_bool create_if_missing,
1 - Error 1 - Error
*/ */
int ma_control_file_write_and_force(LSN checkpoint_lsn, uint32 logno, int ma_control_file_write_and_force(LSN last_checkpoint_lsn_arg,
TrID trid) uint32 last_logno_arg,
TrID max_trid_arg,
uint8 recovery_failures_arg)
{ {
uchar buffer[CF_MAX_SIZE]; uchar buffer[CF_MAX_SIZE];
uint32 sum; uint32 sum;
my_bool no_need_sync;
DBUG_ENTER("ma_control_file_write_and_force"); DBUG_ENTER("ma_control_file_write_and_force");
if ((last_checkpoint_lsn == checkpoint_lsn) && /*
(last_logno == logno) && We don't need to sync if this is just an increase of
(max_trid_in_control_file == trid)) recovery_failures: it's even good if that counter is not increased on disk
DBUG_RETURN(0); /* no need to write */ in case of power or hardware failure (less false positives when removing
logs).
*/
no_need_sync= ((last_checkpoint_lsn == last_checkpoint_lsn_arg) &&
(last_logno == last_logno_arg) &&
(max_trid_in_control_file == max_trid_arg) &&
(recovery_failures_arg > 0));
if (control_file_fd < 0) if (control_file_fd < 0)
DBUG_RETURN(1); DBUG_RETURN(1);
...@@ -479,9 +503,10 @@ int ma_control_file_write_and_force(LSN checkpoint_lsn, uint32 logno, ...@@ -479,9 +503,10 @@ int ma_control_file_write_and_force(LSN checkpoint_lsn, uint32 logno,
translog_lock_handler_assert_owner(); translog_lock_handler_assert_owner();
#endif #endif
lsn_store(buffer + CF_LSN_OFFSET, checkpoint_lsn); lsn_store(buffer + CF_LSN_OFFSET, last_checkpoint_lsn_arg);
int4store(buffer + CF_FILENO_OFFSET, logno); int4store(buffer + CF_FILENO_OFFSET, last_logno_arg);
transid_store(buffer + CF_MAX_TRID_OFFSET, trid); transid_store(buffer + CF_MAX_TRID_OFFSET, max_trid_arg);
(buffer + CF_RECOV_FAIL_OFFSET)[0]= recovery_failures_arg;
if (cf_changeable_size > CF_CHANGEABLE_TOTAL_SIZE) if (cf_changeable_size > CF_CHANGEABLE_TOTAL_SIZE)
{ {
...@@ -514,12 +539,13 @@ int ma_control_file_write_and_force(LSN checkpoint_lsn, uint32 logno, ...@@ -514,12 +539,13 @@ int ma_control_file_write_and_force(LSN checkpoint_lsn, uint32 logno,
if (my_pwrite(control_file_fd, buffer, cf_changeable_size, if (my_pwrite(control_file_fd, buffer, cf_changeable_size,
cf_create_time_size, MYF(MY_FNABP | MY_WME)) || cf_create_time_size, MYF(MY_FNABP | MY_WME)) ||
my_sync(control_file_fd, MYF(MY_WME))) (!no_need_sync && my_sync(control_file_fd, MYF(MY_WME))))
DBUG_RETURN(1); DBUG_RETURN(1);
last_checkpoint_lsn= checkpoint_lsn; last_checkpoint_lsn= last_checkpoint_lsn_arg;
last_logno= logno; last_logno= last_logno_arg;
max_trid_in_control_file= trid; max_trid_in_control_file= max_trid_arg;
recovery_failures= recovery_failures_arg;
cf_changeable_size= CF_CHANGEABLE_TOTAL_SIZE; /* no more warning */ cf_changeable_size= CF_CHANGEABLE_TOTAL_SIZE; /* no more warning */
DBUG_RETURN(0); DBUG_RETURN(0);
...@@ -558,7 +584,7 @@ int ma_control_file_end(void) ...@@ -558,7 +584,7 @@ int ma_control_file_end(void)
*/ */
last_checkpoint_lsn= LSN_IMPOSSIBLE; last_checkpoint_lsn= LSN_IMPOSSIBLE;
last_logno= FILENO_IMPOSSIBLE; last_logno= FILENO_IMPOSSIBLE;
max_trid_in_control_file= 0; max_trid_in_control_file= recovery_failures= 0;
DBUG_RETURN(close_error); DBUG_RETURN(close_error);
} }
......
...@@ -44,6 +44,8 @@ extern uint32 last_logno; ...@@ -44,6 +44,8 @@ extern uint32 last_logno;
extern TrID max_trid_in_control_file; extern TrID max_trid_in_control_file;
extern uint8 recovery_failures;
extern my_bool maria_multi_threaded, maria_in_recovery; extern my_bool maria_multi_threaded, maria_in_recovery;
typedef enum enum_control_file_error { typedef enum enum_control_file_error {
...@@ -63,7 +65,9 @@ typedef enum enum_control_file_error { ...@@ -63,7 +65,9 @@ typedef enum enum_control_file_error {
C_MODE_START C_MODE_START
CONTROL_FILE_ERROR ma_control_file_open(my_bool create_if_missing, CONTROL_FILE_ERROR ma_control_file_open(my_bool create_if_missing,
my_bool print_error); my_bool print_error);
int ma_control_file_write_and_force(LSN checkpoint_lsn, uint32 logno, TrID trid); int ma_control_file_write_and_force(LSN last_checkpoint_lsn_arg,
uint32 last_logno_arg, TrID max_trid_arg,
uint8 recovery_failures_arg);
int ma_control_file_end(void); int ma_control_file_end(void);
my_bool ma_control_file_inited(void); my_bool ma_control_file_inited(void);
C_MODE_END C_MODE_END
......
...@@ -86,7 +86,7 @@ void maria_end(void) ...@@ -86,7 +86,7 @@ void maria_end(void)
from the log, as it cannot process REDOs). from the log, as it cannot process REDOs).
*/ */
(void)ma_control_file_write_and_force(last_checkpoint_lsn, last_logno, (void)ma_control_file_write_and_force(last_checkpoint_lsn, last_logno,
trid); trid, recovery_failures);
} }
trnman_destroy(); trnman_destroy();
if (translog_status == TRANSLOG_OK) if (translog_status == TRANSLOG_OK)
......
...@@ -381,7 +381,7 @@ int _ma_test_if_changed(register MARIA_HA *info) ...@@ -381,7 +381,7 @@ int _ma_test_if_changed(register MARIA_HA *info)
tells us if the MARIA file wasn't properly closed. (This is true if tells us if the MARIA file wasn't properly closed. (This is true if
my_disable_locking is set). my_disable_locking is set).
open_count is not maintained on disk for transactional or temporary tables. open_count is not maintained on disk for temporary tables.
*/ */
int _ma_mark_file_changed(MARIA_HA *info) int _ma_mark_file_changed(MARIA_HA *info)
...@@ -400,11 +400,16 @@ int _ma_mark_file_changed(MARIA_HA *info) ...@@ -400,11 +400,16 @@ int _ma_mark_file_changed(MARIA_HA *info)
share->state.open_count++; share->state.open_count++;
} }
/* /*
temp tables don't need an open_count as they are removed on crash; Temp tables don't need an open_count as they are removed on crash.
transactional tables are fixed by log-based recovery, so don't need an In theory transactional tables are fixed by log-based recovery, so don't
open_count either (and we thus avoid the disk write below). need an open_count either, but if recovery has failed and logs have been
removed (by maria-force-start-after-recovery-failures), we still need to
detect dubious tables.
If we didn't maintain open_count on disk for a table, after a crash
we wouldn't know if it was closed at crash time (thus does not need a
check) or not. So we would have to check all tables: overkill.
*/ */
if (!(share->temporary | share->base.born_transactional)) if (!share->temporary)
{ {
mi_int2store(buff,share->state.open_count); mi_int2store(buff,share->state.open_count);
buff[2]=1; /* Mark that it's changed */ buff[2]=1; /* Mark that it's changed */
...@@ -471,7 +476,7 @@ int _ma_decrement_open_count(MARIA_HA *info) ...@@ -471,7 +476,7 @@ int _ma_decrement_open_count(MARIA_HA *info)
{ {
share->state.open_count--; share->state.open_count--;
share->changed= 1; /* We have to update state */ share->changed= 1; /* We have to update state */
if (!(share->temporary | share->base.born_transactional)) if (!share->temporary)
{ {
mi_int2store(buff,share->state.open_count); mi_int2store(buff,share->state.open_count);
write_error= (int) my_pwrite(share->kfile.file, buff, sizeof(buff), write_error= (int) my_pwrite(share->kfile.file, buff, sizeof(buff),
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "trnman.h" #include "trnman.h"
#include "ma_blockrec.h" /* for some constants and in-write hooks */ #include "ma_blockrec.h" /* for some constants and in-write hooks */
#include "ma_key_recover.h" /* For some in-write hooks */ #include "ma_key_recover.h" /* For some in-write hooks */
#include "ma_checkpoint.h"
/* /*
On Windows, neither my_open() nor my_sync() work for directories. On Windows, neither my_open() nor my_sync() work for directories.
...@@ -1522,7 +1523,8 @@ static my_bool translog_create_new_file() ...@@ -1522,7 +1523,8 @@ static my_bool translog_create_new_file()
DBUG_RETURN(1); DBUG_RETURN(1);
if (ma_control_file_write_and_force(last_checkpoint_lsn, file_no, if (ma_control_file_write_and_force(last_checkpoint_lsn, file_no,
max_trid_in_control_file)) max_trid_in_control_file,
recovery_failures))
{ {
translog_stop_writing(); translog_stop_writing();
DBUG_RETURN(1); DBUG_RETURN(1);
...@@ -3211,21 +3213,29 @@ static my_bool translog_truncate_log(TRANSLOG_ADDRESS addr) ...@@ -3211,21 +3213,29 @@ static my_bool translog_truncate_log(TRANSLOG_ADDRESS addr)
/** /**
@brief Check log files presence Applies function 'callback' to all files (in a directory) which
name looks like a log's name (maria_log.[0-9]{7}).
If 'callback' returns TRUE this interrupts the walk and returns
TRUE. Otherwise FALSE is returned after processing all log files.
It cannot just use log_descriptor.directory because that may not yet have
been initialized.
@retval 0 no log files. @param directory directory to scan
@retval 1 there is at least 1 log file in the directory @param callback function to apply; is passed directory and base
name of found file
*/ */
my_bool translog_is_log_files() my_bool translog_walk_filenames(const char *directory,
my_bool (*callback)(const char *,
const char *))
{ {
MY_DIR *dirp; MY_DIR *dirp;
uint i; uint i;
my_bool rc= FALSE; my_bool rc= FALSE;
/* Finds and removes transaction log files */ /* Finds and removes transaction log files */
if (!(dirp = my_dir(log_descriptor.directory, MYF(MY_DONT_SORT)))) if (!(dirp = my_dir(directory, MYF(MY_DONT_SORT))))
return 1; return FALSE;
for (i= 0; i < dirp->number_off_files; i++) for (i= 0; i < dirp->number_off_files; i++)
{ {
...@@ -3239,14 +3249,14 @@ my_bool translog_is_log_files() ...@@ -3239,14 +3249,14 @@ my_bool translog_is_log_files()
file[15] >= '0' && file[15] <= '9' && file[15] >= '0' && file[15] <= '9' &&
file[16] >= '0' && file[16] <= '9' && file[16] >= '0' && file[16] <= '9' &&
file[17] >= '0' && file[17] <= '9' && file[17] >= '0' && file[17] <= '9' &&
file[18] == '\0') file[18] == '\0' && (*callback)(directory, file))
{ {
rc= TRUE; rc= TRUE;
break; break;
} }
} }
my_dirend(dirp); my_dirend(dirp);
return FALSE; return rc;
} }
...@@ -3269,6 +3279,19 @@ static void translog_fill_overhead_table() ...@@ -3269,6 +3279,19 @@ static void translog_fill_overhead_table()
} }
/**
Callback to find first log in directory.
*/
static my_bool translog_callback_search_first(const char *directory
__attribute__((unused)),
const char *filename
__attribute__((unused)))
{
return TRUE;
}
/** /**
@brief Checks that chunk is LSN one @brief Checks that chunk is LSN one
...@@ -3353,7 +3376,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3353,7 +3376,7 @@ my_bool translog_init_with_table(const char *directory,
my_init_dynamic_array(&log_descriptor.unfinished_files, my_init_dynamic_array(&log_descriptor.unfinished_files,
sizeof(struct st_file_counter), sizeof(struct st_file_counter),
10, 10)) 10, 10))
DBUG_RETURN(1); goto err;
log_descriptor.min_need_file= 0; log_descriptor.min_need_file= 0;
log_descriptor.min_file_number= 0; log_descriptor.min_file_number= 0;
log_descriptor.last_lsn_checked= LSN_IMPOSSIBLE; log_descriptor.last_lsn_checked= LSN_IMPOSSIBLE;
...@@ -3367,7 +3390,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3367,7 +3390,7 @@ my_bool translog_init_with_table(const char *directory,
my_errno= errno; my_errno= errno;
DBUG_PRINT("error", ("Error %d during opening directory '%s'", DBUG_PRINT("error", ("Error %d during opening directory '%s'",
errno, log_descriptor.directory)); errno, log_descriptor.directory));
DBUG_RETURN(1); goto err;
} }
#endif #endif
log_descriptor.in_buffers_only= LSN_IMPOSSIBLE; log_descriptor.in_buffers_only= LSN_IMPOSSIBLE;
...@@ -3417,7 +3440,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3417,7 +3440,7 @@ my_bool translog_init_with_table(const char *directory,
for (i= 0; i < TRANSLOG_BUFFERS_NO; i++) for (i= 0; i < TRANSLOG_BUFFERS_NO; i++)
{ {
if (translog_buffer_init(log_descriptor.buffers + i)) if (translog_buffer_init(log_descriptor.buffers + i))
DBUG_RETURN(1); goto err;
#ifndef DBUG_OFF #ifndef DBUG_OFF
log_descriptor.buffers[i].buffer_no= (uint8) i; log_descriptor.buffers[i].buffer_no= (uint8) i;
#endif #endif
...@@ -3461,7 +3484,8 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3461,7 +3484,8 @@ my_bool translog_init_with_table(const char *directory,
log_descriptor.horizon= last_page= MAKE_LSN(last_logno, 0); log_descriptor.horizon= last_page= MAKE_LSN(last_logno, 0);
if (translog_get_last_page_addr(&last_page, &pageok, no_errors)) if (translog_get_last_page_addr(&last_page, &pageok, no_errors))
{ {
if (!translog_is_log_files()) if (!translog_walk_filenames(log_descriptor.directory,
&translog_callback_search_first))
{ {
/* /*
Files was deleted, just start from the next log number, so that Files was deleted, just start from the next log number, so that
...@@ -3472,7 +3496,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3472,7 +3496,7 @@ my_bool translog_init_with_table(const char *directory,
logs_found= 0; logs_found= 0;
} }
else else
DBUG_RETURN(1); goto err;
} }
else if (LSN_OFFSET(last_page) == 0) else if (LSN_OFFSET(last_page) == 0)
{ {
...@@ -3485,7 +3509,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3485,7 +3509,7 @@ my_bool translog_init_with_table(const char *directory,
{ {
last_page-= LSN_ONE_FILE; last_page-= LSN_ONE_FILE;
if (translog_get_last_page_addr(&last_page, &pageok, 0)) if (translog_get_last_page_addr(&last_page, &pageok, 0))
DBUG_RETURN(1); goto err;
} }
} }
if (logs_found) if (logs_found)
...@@ -3497,7 +3521,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3497,7 +3521,7 @@ my_bool translog_init_with_table(const char *directory,
if (allocate_dynamic(&log_descriptor.open_files, if (allocate_dynamic(&log_descriptor.open_files,
log_descriptor.max_file - log_descriptor.max_file -
log_descriptor.min_file + 1)) log_descriptor.min_file + 1))
DBUG_RETURN(1); goto err;
for (i = log_descriptor.max_file; i >= log_descriptor.min_file; i--) for (i = log_descriptor.max_file; i >= log_descriptor.min_file; i--)
{ {
/* /*
...@@ -3526,10 +3550,10 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3526,10 +3550,10 @@ my_bool translog_init_with_table(const char *directory,
if (file) if (file)
{ {
free(file); free(file);
DBUG_RETURN(1); goto err;
} }
else else
DBUG_RETURN(1); goto err;
} }
translog_file_init(file, i, 1); translog_file_init(file, i, 1);
/* we allocated space so it can't fail */ /* we allocated space so it can't fail */
...@@ -3543,7 +3567,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3543,7 +3567,7 @@ my_bool translog_init_with_table(const char *directory,
{ {
/* There is no logs and there is read-only mode => nothing to read */ /* There is no logs and there is read-only mode => nothing to read */
DBUG_PRINT("error", ("No logs and read-only mode")); DBUG_PRINT("error", ("No logs and read-only mode"));
DBUG_RETURN(1); goto err;
} }
if (logs_found) if (logs_found)
...@@ -3568,7 +3592,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3568,7 +3592,7 @@ my_bool translog_init_with_table(const char *directory,
TRANSLOG_ADDRESS current_file_last_page; TRANSLOG_ADDRESS current_file_last_page;
current_file_last_page= current_page; current_file_last_page= current_page;
if (translog_get_last_page_addr(&current_file_last_page, &pageok, 0)) if (translog_get_last_page_addr(&current_file_last_page, &pageok, 0))
DBUG_RETURN(1); goto err;
if (!pageok) if (!pageok)
{ {
DBUG_PRINT("error", ("File %lu have no complete last page", DBUG_PRINT("error", ("File %lu have no complete last page",
...@@ -3585,7 +3609,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3585,7 +3609,7 @@ my_bool translog_init_with_table(const char *directory,
uchar *page; uchar *page;
data.addr= &current_page; data.addr= &current_page;
if ((page= translog_get_page(&data, psize_buff.buffer, NULL)) == NULL) if ((page= translog_get_page(&data, psize_buff.buffer, NULL)) == NULL)
DBUG_RETURN(1); goto err;
if (data.was_recovered) if (data.was_recovered)
{ {
DBUG_PRINT("error", ("file no: %lu (%d) " DBUG_PRINT("error", ("file no: %lu (%d) "
...@@ -3614,7 +3638,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3614,7 +3638,7 @@ my_bool translog_init_with_table(const char *directory,
{ {
/* Panic!!! Even page which should be valid is invalid */ /* Panic!!! Even page which should be valid is invalid */
/* TODO: issue error */ /* TODO: issue error */
DBUG_RETURN(1); goto err;
} }
DBUG_PRINT("info", ("Last valid page is in file: %lu " DBUG_PRINT("info", ("Last valid page is in file: %lu "
"offset: %lu (0x%lx) " "offset: %lu (0x%lx) "
...@@ -3639,7 +3663,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3639,7 +3663,7 @@ my_bool translog_init_with_table(const char *directory,
LSN_FILE_NO(log_descriptor.horizon)); LSN_FILE_NO(log_descriptor.horizon));
if ((page= translog_get_page(&data, psize_buff.buffer, NULL)) == NULL || if ((page= translog_get_page(&data, psize_buff.buffer, NULL)) == NULL ||
(chunk_offset= translog_get_first_chunk_offset(page)) == 0) (chunk_offset= translog_get_first_chunk_offset(page)) == 0)
DBUG_RETURN(1); goto err;
/* Puts filled part of old page in the buffer */ /* Puts filled part of old page in the buffer */
log_descriptor.horizon= last_valid_page; log_descriptor.horizon= last_valid_page;
...@@ -3654,7 +3678,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3654,7 +3678,7 @@ my_bool translog_init_with_table(const char *directory,
uint16 chunk_length; uint16 chunk_length;
if ((chunk_length= if ((chunk_length=
translog_get_total_chunk_length(page, chunk_offset)) == 0) translog_get_total_chunk_length(page, chunk_offset)) == 0)
DBUG_RETURN(1); goto err;
DBUG_PRINT("info", ("chunk: offset: %u length: %u", DBUG_PRINT("info", ("chunk: offset: %u length: %u",
(uint) chunk_offset, (uint) chunk_length)); (uint) chunk_offset, (uint) chunk_length));
chunk_offset+= chunk_length; chunk_offset+= chunk_length;
...@@ -3690,7 +3714,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3690,7 +3714,7 @@ my_bool translog_init_with_table(const char *directory,
open_files, open_files,
0, TRANSLOG_FILE **))-> 0, TRANSLOG_FILE **))->
handler.file)) handler.file))
DBUG_RETURN(1); goto err;
version_changed= (info.maria_version != TRANSLOG_VERSION_ID); version_changed= (info.maria_version != TRANSLOG_VERSION_ID);
} }
} }
...@@ -3702,25 +3726,26 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3702,25 +3726,26 @@ my_bool translog_init_with_table(const char *directory,
MYF(0)); MYF(0));
DBUG_PRINT("info", ("The log is not found => we will create new log")); DBUG_PRINT("info", ("The log is not found => we will create new log"));
if (file == NULL) if (file == NULL)
DBUG_RETURN(1); goto err;
/* Start new log system from scratch */ /* Start new log system from scratch */
log_descriptor.horizon= MAKE_LSN(start_file_num, log_descriptor.horizon= MAKE_LSN(start_file_num,
TRANSLOG_PAGE_SIZE); /* header page */ TRANSLOG_PAGE_SIZE); /* header page */
if ((file->handler.file= if ((file->handler.file=
create_logfile_by_number_no_cache(start_file_num)) == -1) create_logfile_by_number_no_cache(start_file_num)) == -1)
DBUG_RETURN(1); goto err;
translog_file_init(file, start_file_num, 0); translog_file_init(file, start_file_num, 0);
if (insert_dynamic(&log_descriptor.open_files, (uchar*)&file)) if (insert_dynamic(&log_descriptor.open_files, (uchar*)&file))
DBUG_RETURN(1); goto err;
log_descriptor.min_file= log_descriptor.max_file= start_file_num; log_descriptor.min_file= log_descriptor.max_file= start_file_num;
if (translog_write_file_header()) if (translog_write_file_header())
DBUG_RETURN(1); goto err;
DBUG_ASSERT(log_descriptor.max_file - log_descriptor.min_file + 1 == DBUG_ASSERT(log_descriptor.max_file - log_descriptor.min_file + 1 ==
log_descriptor.open_files.elements); log_descriptor.open_files.elements);
if (ma_control_file_write_and_force(checkpoint_lsn, start_file_num, if (ma_control_file_write_and_force(checkpoint_lsn, start_file_num,
max_trid_in_control_file)) max_trid_in_control_file,
DBUG_RETURN(1); recovery_failures))
goto err;
/* assign buffer 0 */ /* assign buffer 0 */
translog_start_buffer(log_descriptor.buffers, &log_descriptor.bc, 0); translog_start_buffer(log_descriptor.buffers, &log_descriptor.bc, 0);
translog_new_page_header(&log_descriptor.horizon, &log_descriptor.bc); translog_new_page_header(&log_descriptor.horizon, &log_descriptor.bc);
...@@ -3734,7 +3759,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3734,7 +3759,7 @@ my_bool translog_init_with_table(const char *directory,
log_descriptor.horizon= LSN_REPLACE_OFFSET(log_descriptor.horizon, log_descriptor.horizon= LSN_REPLACE_OFFSET(log_descriptor.horizon,
TRANSLOG_PAGE_SIZE); TRANSLOG_PAGE_SIZE);
if (translog_create_new_file()) if (translog_create_new_file())
DBUG_RETURN(1); goto err;
/* /*
Buffer system left untouched after recovery => we should init it Buffer system left untouched after recovery => we should init it
(starting from buffer 0) (starting from buffer 0)
...@@ -3767,7 +3792,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3767,7 +3792,7 @@ my_bool translog_init_with_table(const char *directory,
id_to_share= (MARIA_SHARE **) my_malloc(SHARE_ID_MAX * sizeof(MARIA_SHARE*), id_to_share= (MARIA_SHARE **) my_malloc(SHARE_ID_MAX * sizeof(MARIA_SHARE*),
MYF(MY_WME | MY_ZEROFILL)); MYF(MY_WME | MY_ZEROFILL));
if (unlikely(!id_to_share)) if (unlikely(!id_to_share))
DBUG_RETURN(1); goto err;
id_to_share--; /* min id is 1 */ id_to_share--; /* min id is 1 */
/* Check the last LSN record integrity */ /* Check the last LSN record integrity */
...@@ -3783,7 +3808,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3783,7 +3808,7 @@ my_bool translog_init_with_table(const char *directory,
page_addr= (log_descriptor.horizon - page_addr= (log_descriptor.horizon -
((log_descriptor.horizon - 1) % TRANSLOG_PAGE_SIZE + 1)); ((log_descriptor.horizon - 1) % TRANSLOG_PAGE_SIZE + 1));
if (translog_scanner_init(page_addr, 1, &scanner, 1)) if (translog_scanner_init(page_addr, 1, &scanner, 1))
DBUG_RETURN(1); goto err;
scanner.page_offset= page_overhead[scanner.page[TRANSLOG_PAGE_FLAGS]]; scanner.page_offset= page_overhead[scanner.page[TRANSLOG_PAGE_FLAGS]];
for (;;) for (;;)
{ {
...@@ -3797,7 +3822,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3797,7 +3822,7 @@ my_bool translog_init_with_table(const char *directory,
if (translog_get_next_chunk(&scanner)) if (translog_get_next_chunk(&scanner))
{ {
translog_destroy_scanner(&scanner); translog_destroy_scanner(&scanner);
DBUG_RETURN(1); goto err;
} }
if (scanner.page != END_OF_LOG) if (scanner.page != END_OF_LOG)
chunk_1byte= scanner.page[scanner.page_offset]; chunk_1byte= scanner.page[scanner.page_offset];
...@@ -3808,7 +3833,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3808,7 +3833,7 @@ my_bool translog_init_with_table(const char *directory,
if (translog_get_next_chunk(&scanner)) if (translog_get_next_chunk(&scanner))
{ {
translog_destroy_scanner(&scanner); translog_destroy_scanner(&scanner);
DBUG_RETURN(1); goto err;
} }
if (scanner.page == END_OF_LOG) if (scanner.page == END_OF_LOG)
break; /* it was the last record */ break; /* it was the last record */
...@@ -3845,7 +3870,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3845,7 +3870,7 @@ my_bool translog_init_with_table(const char *directory,
} }
translog_destroy_scanner(&scanner); translog_destroy_scanner(&scanner);
if (translog_scanner_init(page_addr, 1, &scanner, 1)) if (translog_scanner_init(page_addr, 1, &scanner, 1))
DBUG_RETURN(1); goto err;
scanner.page_offset= page_overhead[scanner.page[TRANSLOG_PAGE_FLAGS]]; scanner.page_offset= page_overhead[scanner.page[TRANSLOG_PAGE_FLAGS]];
} }
translog_destroy_scanner(&scanner); translog_destroy_scanner(&scanner);
...@@ -3872,7 +3897,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3872,7 +3897,7 @@ my_bool translog_init_with_table(const char *directory,
else if (translog_truncate_log(last_lsn)) else if (translog_truncate_log(last_lsn))
{ {
translog_free_record_header(&rec); translog_free_record_header(&rec);
DBUG_RETURN(1); goto err;
} }
} }
else else
...@@ -3898,7 +3923,7 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3898,7 +3923,7 @@ my_bool translog_init_with_table(const char *directory,
else if (translog_truncate_log(last_lsn)) else if (translog_truncate_log(last_lsn))
{ {
translog_free_record_header(&rec); translog_free_record_header(&rec);
DBUG_RETURN(1); goto err;
} }
} }
} }
...@@ -3907,6 +3932,9 @@ my_bool translog_init_with_table(const char *directory, ...@@ -3907,6 +3932,9 @@ my_bool translog_init_with_table(const char *directory,
} }
} }
DBUG_RETURN(0); DBUG_RETURN(0);
err:
ma_message_no_user(0, "log initialization failed");
DBUG_RETURN(1);
} }
......
...@@ -317,6 +317,10 @@ extern void translog_deassign_id_from_share(struct st_maria_share *share); ...@@ -317,6 +317,10 @@ extern void translog_deassign_id_from_share(struct st_maria_share *share);
extern void extern void
translog_assign_id_to_share_from_recovery(struct st_maria_share *share, translog_assign_id_to_share_from_recovery(struct st_maria_share *share,
uint16 id); uint16 id);
extern my_bool translog_walk_filenames(const char *directory,
my_bool (*callback)(const char *,
const char *));
enum enum_translog_status enum enum_translog_status
{ {
TRANSLOG_UNINITED, /* no initialization done or error during initialization */ TRANSLOG_UNINITED, /* no initialization done or error during initialization */
......
...@@ -191,12 +191,12 @@ static void print_preamble() ...@@ -191,12 +191,12 @@ static void print_preamble()
@retval !=0 Error @retval !=0 Error
*/ */
int maria_recover(void) int maria_recovery_from_log(void)
{ {
int res= 1; int res= 1;
FILE *trace_file; FILE *trace_file;
uint warnings_count; uint warnings_count;
DBUG_ENTER("maria_recover"); DBUG_ENTER("maria_recovery_from_log");
DBUG_ASSERT(!maria_in_recovery); DBUG_ASSERT(!maria_in_recovery);
maria_in_recovery= TRUE; maria_in_recovery= TRUE;
...@@ -462,7 +462,12 @@ int maria_apply_log(LSN from_lsn, enum maria_apply_log_way apply, ...@@ -462,7 +462,12 @@ int maria_apply_log(LSN from_lsn, enum maria_apply_log_way apply,
"Maria recovery failed. Please run maria_chk -r on all maria " "Maria recovery failed. Please run maria_chk -r on all maria "
"tables and delete all maria_log.######## files", MYF(0)); "tables and delete all maria_log.######## files", MYF(0));
procent_printed= 0; procent_printed= 0;
/* we don't cleanly close tables if we hit some error (may corrupt them) */ /*
We don't cleanly close tables if we hit some error (may corrupt them by
flushing some wrong blocks made from wrong REDOs). It also leaves their
open_count>0, which ensures that --maria-recover, if used, will try to
repair them.
*/
DBUG_RETURN(error); DBUG_RETURN(error);
} }
...@@ -1224,6 +1229,12 @@ static int new_table(uint16 sid, const char *name, LSN lsn_of_file_id) ...@@ -1224,6 +1229,12 @@ static int new_table(uint16 sid, const char *name, LSN lsn_of_file_id)
" maria_chk -r", share->open_file_name); " maria_chk -r", share->open_file_name);
error= -1; /* not fatal, try with other tables */ error= -1; /* not fatal, try with other tables */
goto end; goto end;
/*
Note that if a first recovery fails to apply a REDO, it marks the table
corrupted and stops the entire recovery. A second recovery will find the
table is marked corrupted and skip it (and thus possibly handle other
tables).
*/
} }
/* don't log any records for this work */ /* don't log any records for this work */
_ma_tmp_disable_logging_for_table(info, FALSE); _ma_tmp_disable_logging_for_table(info, FALSE);
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
C_MODE_START C_MODE_START
enum maria_apply_log_way enum maria_apply_log_way
{ MARIA_LOG_APPLY, MARIA_LOG_DISPLAY_HEADER, MARIA_LOG_CHECK }; { MARIA_LOG_APPLY, MARIA_LOG_DISPLAY_HEADER, MARIA_LOG_CHECK };
int maria_recover(void); int maria_recovery_from_log(void);
int maria_apply_log(LSN lsn, enum maria_apply_log_way apply, int maria_apply_log(LSN lsn, enum maria_apply_log_way apply,
FILE *trace_file, FILE *trace_file,
my_bool execute_undo_phase, my_bool skip_DDLs, my_bool execute_undo_phase, my_bool skip_DDLs,
......
#!/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: 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
my $base_server_cmd= "perl mysql-test-run.pl --mem --mysqld=--maria-force-start-after-recovery-failures=$force_after maria-recover";
my $server_cmd;
my $client_cmd= "../client/mysql -u root -S var/tmp/master.sock test < $sql_name";
my $server_pid_name="./var/run/master.pid";
my $server_pid;
my $i; # count of server restarts
sub kill_server;
print "starting mysqld\n";
$server_cmd= $base_server_cmd . " --start-and-exit 2>&1";
@cmd_output=`$server_cmd`;
die if $?;
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: 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: Maria engine: removed all logs after [\d]+ consecutive failures of recovery from logs/, @cmd_output);
die unless grep(/\[ERROR\] mysqld: 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;
open(FILE, "<", $server_pid_name) or die;
@cmd_output= <FILE>;
close FILE;
$server_pid= $cmd_output[0];
die unless $server_pid > 0;
kill($sig, $server_pid) or die;
while (kill (0, $server_pid))
{
print "waiting for mysqld to die\n" if ($wait_count > 30);
$wait_count= $wait_count + 1;
select(undef, undef, undef, 0.1);
}
}
...@@ -45,6 +45,7 @@ char file_name[FN_REFLEN]; ...@@ -45,6 +45,7 @@ char file_name[FN_REFLEN];
LSN expect_checkpoint_lsn; LSN expect_checkpoint_lsn;
uint32 expect_logno; uint32 expect_logno;
TrID expect_max_trid; TrID expect_max_trid;
uint8 expect_recovery_failures;
static int delete_file(myf my_flags); static int delete_file(myf my_flags);
/* /*
...@@ -55,10 +56,11 @@ static int close_file(void); /* wraps ma_control_file_end */ ...@@ -55,10 +56,11 @@ static int close_file(void); /* wraps ma_control_file_end */
/* wraps ma_control_file_open_or_create */ /* wraps ma_control_file_open_or_create */
static int open_file(void); static int open_file(void);
/* wraps ma_control_file_write_and_force */ /* wraps ma_control_file_write_and_force */
static int write_file(LSN checkpoint_lsn, uint32 logno, TrID trid); static int write_file(LSN checkpoint_lsn, uint32 logno, TrID trid,
uint8 rec_failures);
/* Tests */ /* Tests */
static int test_one_log(void); static int test_one_log_and_recovery_failures(void);
static int test_five_logs_and_max_trid(void); static int test_five_logs_and_max_trid(void);
static int test_3_checkpoints_and_2_logs(void); static int test_3_checkpoints_and_2_logs(void);
static int test_binary_content(void); static int test_binary_content(void);
...@@ -135,7 +137,8 @@ int main(int argc,char *argv[]) ...@@ -135,7 +137,8 @@ int main(int argc,char *argv[])
RET_ERR_UNLESS(0 == delete_file(0)); /* if fails, can't continue */ RET_ERR_UNLESS(0 == delete_file(0)); /* if fails, can't continue */
diag("Tests of normal conditions"); diag("Tests of normal conditions");
ok(0 == test_one_log(), "test of creating one log"); ok(0 == test_one_log_and_recovery_failures(),
"test of creating one log and recording recovery failures");
ok(0 == test_five_logs_and_max_trid(), ok(0 == test_five_logs_and_max_trid(),
"test of creating five logs and many transactions"); "test of creating five logs and many transactions");
ok(0 == test_3_checkpoints_and_2_logs(), ok(0 == test_3_checkpoints_and_2_logs(),
...@@ -167,7 +170,7 @@ static int delete_file(myf my_flags) ...@@ -167,7 +170,7 @@ static int delete_file(myf my_flags)
my_delete(file_name, my_flags); my_delete(file_name, my_flags);
expect_checkpoint_lsn= LSN_IMPOSSIBLE; expect_checkpoint_lsn= LSN_IMPOSSIBLE;
expect_logno= FILENO_IMPOSSIBLE; expect_logno= FILENO_IMPOSSIBLE;
expect_max_trid= 0; expect_max_trid= expect_recovery_failures= 0;
return 0; return 0;
} }
...@@ -181,6 +184,7 @@ static int verify_module_values_match_expected(void) ...@@ -181,6 +184,7 @@ static int verify_module_values_match_expected(void)
RET_ERR_UNLESS(last_logno == expect_logno); RET_ERR_UNLESS(last_logno == expect_logno);
RET_ERR_UNLESS(last_checkpoint_lsn == expect_checkpoint_lsn); RET_ERR_UNLESS(last_checkpoint_lsn == expect_checkpoint_lsn);
RET_ERR_UNLESS(max_trid_in_control_file == expect_max_trid); RET_ERR_UNLESS(max_trid_in_control_file == expect_max_trid);
RET_ERR_UNLESS(recovery_failures == expect_recovery_failures);
return 0; return 0;
} }
...@@ -215,21 +219,28 @@ static int open_file(void) ...@@ -215,21 +219,28 @@ static int open_file(void)
return 0; return 0;
} }
static int write_file(LSN checkpoint_lsn, uint32 logno, TrID trid) static int write_file(LSN checkpoint_lsn, uint32 logno, TrID trid,
uint8 rec_failures)
{ {
RET_ERR_UNLESS(ma_control_file_write_and_force(checkpoint_lsn, logno, trid) RET_ERR_UNLESS(ma_control_file_write_and_force(checkpoint_lsn, logno, trid,
rec_failures)
== 0); == 0);
/* Check that the module reports expected information */ /* Check that the module reports expected information */
RET_ERR_UNLESS(verify_module_values_match_expected() == 0); RET_ERR_UNLESS(verify_module_values_match_expected() == 0);
return 0; return 0;
} }
static int test_one_log(void) static int test_one_log_and_recovery_failures(void)
{ {
RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
expect_logno= 123; expect_logno= 123;
RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno, RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno,
max_trid_in_control_file) == 0); max_trid_in_control_file,
recovery_failures) == 0);
expect_recovery_failures= 158;
RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno,
max_trid_in_control_file,
expect_recovery_failures) == 0);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
return 0; return 0;
} }
...@@ -245,7 +256,8 @@ static int test_five_logs_and_max_trid(void) ...@@ -245,7 +256,8 @@ static int test_five_logs_and_max_trid(void)
{ {
expect_logno*= 3; expect_logno*= 3;
RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno, RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno,
expect_max_trid) == 0); expect_max_trid,
recovery_failures) == 0);
} }
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
return 0; return 0;
...@@ -260,23 +272,28 @@ static int test_3_checkpoints_and_2_logs(void) ...@@ -260,23 +272,28 @@ static int test_3_checkpoints_and_2_logs(void)
RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK);
expect_checkpoint_lsn= MAKE_LSN(5, 10000); expect_checkpoint_lsn= MAKE_LSN(5, 10000);
RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno, RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
max_trid_in_control_file) == 0); max_trid_in_control_file,
recovery_failures) == 0);
expect_logno= 17; expect_logno= 17;
RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno, RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
max_trid_in_control_file) == 0); max_trid_in_control_file,
recovery_failures) == 0);
expect_checkpoint_lsn= MAKE_LSN(17, 20000); expect_checkpoint_lsn= MAKE_LSN(17, 20000);
RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno, RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
max_trid_in_control_file) == 0); max_trid_in_control_file,
recovery_failures) == 0);
expect_checkpoint_lsn= MAKE_LSN(17, 45000); expect_checkpoint_lsn= MAKE_LSN(17, 45000);
RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno, RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
max_trid_in_control_file) == 0); max_trid_in_control_file,
recovery_failures) == 0);
expect_logno= 19; expect_logno= 19;
RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno, RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno,
max_trid_in_control_file) == 0); max_trid_in_control_file,
recovery_failures) == 0);
RET_ERR_UNLESS(close_file() == 0); RET_ERR_UNLESS(close_file() == 0);
return 0; return 0;
} }
......
...@@ -129,10 +129,10 @@ static my_bool read_and_check_content(TRANSLOG_HEADER_BUFFER *rec, ...@@ -129,10 +129,10 @@ static my_bool read_and_check_content(TRANSLOG_HEADER_BUFFER *rec,
} }
static const char *load_default_groups[]= {"ma_unit_loghandler", 0}; static const char *load_default_groups[]= {"ma_unit_loghandler", 0};
#if defined(__WIN__) #ifndef DBUG_OFF
static const char *default_dbug_option= "d:t:i:O,\\ma_test_loghandler.trace"; static const char *default_dbug_option=
#else IF_WIN("d:t:i:O,\\ma_test_loghandler.trace",
static const char *default_dbug_option= "d:t:i:o,/tmp/ma_test_loghandler.trace"; "d:t:i:o,/tmp/ma_test_loghandler.trace");
#endif #endif
static const char *opt_wfile= NULL; static const char *opt_wfile= NULL;
static const char *opt_rfile= NULL; static const char *opt_rfile= 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