Commit e6b3e38d authored by Vlad Lesin's avatar Vlad Lesin

MDEV-22929 MariaBackup option to report and/or continue when corruption is encountered

The new option --log-innodb-page-corruption is introduced.

When this option is set, backup is not interrupted if innodb corrupted
page is detected. Instead it logs all found corrupted pages in
innodb_corrupted_pages file in backup directory and finishes with error.

For incremental backup corrupted pages are also copied to .delta file,
because we can't do LSN check for such pages during backup,
innodb_corrupted_pages will also be created in incremental backup
directory.

During --prepare, corrupted pages list is read from the file just after
redo log is applied, and each page from the list is checked if it is allocated
in it's tablespace or not. If it is not allocated, then it is zeroed out,
flushed to the tablespace and removed from the list. If all pages are removed
from the list, then --prepare is finished successfully and
innodb_corrupted_pages file is removed from backup directory. Otherwise
--prepare is finished with error message and innodb_corrupted_pages contains
the list of the pages, which are detected as corrupted during backup, and are
allocated in their tablespaces, what means backup directory contains corrupted
innodb pages, and backup can not be considered as consistent.

For incremental --prepare corrupted pages from .delta files are applied
to the base backup, innodb_corrupted_pages is read from both base in
incremental directories, and the same action is proceded for corrupted
pages list as for full --prepare. innodb_corrupted_pages file is
modified or removed only in base directory.

If DDL happens during backup, it is also processed at the end of backup
to have correct tablespace names in innodb_corrupted_pages.
parent 828471cb
......@@ -867,21 +867,14 @@ datafile_rsync_backup(const char *filepath, bool save_to_list, FILE *f)
return(true);
}
static
bool
backup_file_vprintf(const char *filename, const char *fmt, va_list ap)
bool backup_file_print_buf(const char *filename, const char *buf, int buf_len)
{
ds_file_t *dstfile = NULL;
MY_STAT stat; /* unused for now */
char *buf = 0;
int buf_len;
const char *action;
memset(&stat, 0, sizeof(stat));
buf_len = vasprintf(&buf, fmt, ap);
stat.st_size = buf_len;
stat.st_mtime = my_time(0);
......@@ -905,7 +898,6 @@ backup_file_vprintf(const char *filename, const char *fmt, va_list ap)
/* close */
msg(" ...done");
free(buf);
if (ds_close(dstfile)) {
goto error_close;
......@@ -914,7 +906,6 @@ backup_file_vprintf(const char *filename, const char *fmt, va_list ap)
return(true);
error:
free(buf);
if (dstfile != NULL) {
ds_close(dstfile);
}
......@@ -922,8 +913,21 @@ backup_file_vprintf(const char *filename, const char *fmt, va_list ap)
error_close:
msg("Error: backup file failed.");
return(false); /*ERROR*/
}
return true;
};
static
bool
backup_file_vprintf(const char *filename, const char *fmt, va_list ap)
{
char *buf = 0;
int buf_len;
buf_len = vasprintf(&buf, fmt, ap);
bool result = backup_file_print_buf(filename, buf, buf_len);
free(buf);
return result;
}
bool
backup_file_printf(const char *filename, const char *fmt, ...)
......@@ -1446,7 +1450,7 @@ backup_files(const char *from, bool prep_mode)
return(ret);
}
void backup_fix_ddl(void);
void backup_fix_ddl(CorruptedPages &);
lsn_t get_current_lsn(MYSQL *connection)
{
......@@ -1471,7 +1475,7 @@ lsn_t get_current_lsn(MYSQL *connection)
lsn_t server_lsn_after_lock;
extern void backup_wait_for_lsn(lsn_t lsn);
/** Start --backup */
bool backup_start()
bool backup_start(CorruptedPages &corrupted_pages)
{
if (!opt_no_lock) {
if (opt_safe_slave_backup) {
......@@ -1506,7 +1510,7 @@ bool backup_start()
msg("Waiting for log copy thread to read lsn %llu", (ulonglong)server_lsn_after_lock);
backup_wait_for_lsn(server_lsn_after_lock);
backup_fix_ddl();
backup_fix_ddl(corrupted_pages);
// There is no need to stop slave thread before coping non-Innodb data when
// --no-lock option is used because --no-lock option requires that no DDL or
......
......@@ -33,7 +33,7 @@ copy_file(ds_ctxt_t *datasink,
uint thread_n);
/** Start --backup */
bool backup_start();
bool backup_start(CorruptedPages &corrupted_pages);
/** Release resources after backup_start() */
void backup_release();
/** Finish after backup_start() and backup_release() */
......@@ -51,5 +51,6 @@ directory_exists(const char *dir, bool create);
lsn_t
get_current_lsn(MYSQL *connection);
bool backup_file_print_buf(const char *filename, const char *buf, int buf_len);
#endif
#pragma once
#include "my_dbug.h"
#ifndef DBUG_OFF
extern char *dbug_mariabackup_get_val(const char *event, const char *key);
/*
In debug mode, execute SQL statement that was passed via environment.
To use this facility, you need to
1. Add code DBUG_EXECUTE_MARIABACKUP_EVENT("my_event_name", key););
to the code. key is usually a table name
2. Set environment variable my_event_name_$key SQL statement you want to execute
when event occurs, in DBUG_EXECUTE_IF from above.
In mtr , you can set environment via 'let' statement (do not use $ as the first char
for the variable)
3. start mariabackup with --dbug=+d,debug_mariabackup_events
*/
extern void dbug_mariabackup_event(
const char *event,const char *key);
#define DBUG_MARIABACKUP_EVENT(A, B) \
DBUG_EXECUTE_IF("mariabackup_events", \
dbug_mariabackup_event(A,B););
#define DBUG_EXECUTE_FOR_KEY(EVENT, KEY, CODE) \
DBUG_EXECUTE_IF("mariabackup_inject_code", {\
char *dbug_val = dbug_mariabackup_get_val(EVENT, KEY); \
if (dbug_val && *dbug_val) CODE \
})
#else
#define DBUG_MARIABACKUP_EVENT(A,B)
#define DBUG_MARIABACKUP_EVENT_LOCK(A,B)
#define DBUG_EXECUTE_FOR_KEY(EVENT, KEY, CODE)
#endif
......@@ -2,7 +2,6 @@
#include <mysql.h>
#include <xtrabackup.h>
#include <encryption_plugin.h>
#include <backup_copy.h>
#include <sql_plugin.h>
#include <sstream>
#include <vector>
......
......@@ -36,6 +36,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
#include "read_filt.h"
#include "xtrabackup.h"
#include "xb0xb.h"
#include "backup_debug.h"
/* Size of read buffer in pages (640 pages = 10M for 16K sized pages) */
#define XB_FIL_CUR_PAGES 640
......@@ -372,16 +373,15 @@ static bool page_is_corrupted(const byte *page, ulint page_no,
return buf_page_is_corrupted(true, page, cursor->page_size, space);
}
/************************************************************************
Reads and verifies the next block of pages from the source
/** Reads and verifies the next block of pages from the source
file. Positions the cursor after the last read non-corrupted page.
@param[in,out] cursor source file cursor
@param[out] corrupted_pages adds corrupted pages if
opt_log_innodb_page_corruption is set
@return XB_FIL_CUR_SUCCESS if some have been read successfully, XB_FIL_CUR_EOF
if there are no more pages to read and XB_FIL_CUR_ERROR on error. */
xb_fil_cur_result_t
xb_fil_cur_read(
/*============*/
xb_fil_cur_t* cursor) /*!< in/out: source file cursor */
xb_fil_cur_result_t xb_fil_cur_read(xb_fil_cur_t* cursor,
CorruptedPages &corrupted_pages)
{
byte* page;
ulint i;
......@@ -455,20 +455,40 @@ xb_fil_cur_read(
retry_count--;
if (retry_count == 0) {
const char *ignore_corruption_warn = opt_log_innodb_page_corruption ?
" WARNING!!! The corruption is ignored due to"
" log-innodb-page-corruption option, the backup can contain"
" corrupted data." : "";
msg(cursor->thread_n,
"Error: failed to read page after "
"10 retries. File %s seems to be "
"corrupted.", cursor->abs_path);
ret = XB_FIL_CUR_ERROR;
"corrupted.%s", cursor->abs_path, ignore_corruption_warn);
buf_page_print(page, cursor->page_size);
break;
if (opt_log_innodb_page_corruption) {
corrupted_pages.add_page(cursor->node->name, cursor->node->space->id,
page_no);
retry_count = 1;
}
else {
ret = XB_FIL_CUR_ERROR;
break;
}
}
else {
msg(cursor->thread_n, "Database page corruption detected at page "
ULINTPF ", retrying...",
page_no);
os_thread_sleep(100000);
goto read_retry;
}
msg(cursor->thread_n, "Database page corruption detected at page "
ULINTPF ", retrying...",
page_no);
os_thread_sleep(100000);
goto read_retry;
}
DBUG_EXECUTE_FOR_KEY("add_corrupted_page_for", cursor->node->space->name,
{
ulint corrupted_page_no = strtoul(dbug_val, NULL, 10);
if (page_no == corrupted_page_no)
corrupted_pages.add_page(cursor->node->name, cursor->node->space->id,
corrupted_page_no);
});
cursor->buf_read += page_size;
cursor->buf_npages++;
}
......
......@@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
#include <my_dir.h>
#include "read_filt.h"
#include "srv0start.h"
#include "xtrabackup.h"
struct xb_fil_cur_t {
pfs_os_file_t file; /*!< source file handle */
......@@ -89,17 +90,15 @@ xb_fil_cur_open(
uint thread_n, /*!< thread number for diagnostics */
ulonglong max_file_size = ULLONG_MAX);
/************************************************************************
Reads and verifies the next block of pages from the source
/** Reads and verifies the next block of pages from the source
file. Positions the cursor after the last read non-corrupted page.
@param[in,out] cursor source file cursor
@param[out] corrupted_pages adds corrupted pages if
opt_log_innodb_page_corruption is set
@return XB_FIL_CUR_SUCCESS if some have been read successfully, XB_FIL_CUR_EOF
if there are no more pages to read and XB_FIL_CUR_ERROR on error. */
xb_fil_cur_result_t
xb_fil_cur_read(
/*============*/
xb_fil_cur_t* cursor); /*!< in/out: source file cursor */
xb_fil_cur_result_t xb_fil_cur_read(xb_fil_cur_t *cursor,
CorruptedPages &corrupted_pages);
/************************************************************************
Close the source file cursor opened with xb_fil_cur_open() and its
associated read filter. */
......
......@@ -26,13 +26,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
#include "common.h"
#include "write_filt.h"
#include "fil_cur.h"
#include "xtrabackup.h"
#include <os0proc.h>
/************************************************************************
Write-through page write filter. */
static my_bool wf_wt_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
xb_fil_cur_t *cursor);
xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages);
static my_bool wf_wt_process(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile);
xb_write_filt_t wf_write_through = {
......@@ -45,7 +44,7 @@ xb_write_filt_t wf_write_through = {
/************************************************************************
Incremental page write filter. */
static my_bool wf_incremental_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
xb_fil_cur_t *cursor);
xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages);
static my_bool wf_incremental_process(xb_write_filt_ctxt_t *ctxt,
ds_file_t *dstfile);
static my_bool wf_incremental_finalize(xb_write_filt_ctxt_t *ctxt,
......@@ -65,11 +64,11 @@ Initialize incremental page write filter.
@return TRUE on success, FALSE on error. */
static my_bool
wf_incremental_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
xb_fil_cur_t *cursor)
xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages)
{
char meta_name[FN_REFLEN];
xb_wf_incremental_ctxt_t *cp =
&(ctxt->u.wf_incremental_ctxt);
&(ctxt->wf_incremental_ctxt);
ctxt->cursor = cursor;
......@@ -100,7 +99,9 @@ wf_incremental_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
strcat(dst_name, ".delta");
mach_write_to_4(cp->delta_buf, 0x78747261UL); /*"xtra"*/
cp->npages = 1;
cp->corrupted_pages = corrupted_pages;
return(TRUE);
}
......@@ -117,15 +118,16 @@ wf_incremental_process(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile)
byte *page;
const ulint page_size
= cursor->page_size.physical();
xb_wf_incremental_ctxt_t *cp = &(ctxt->u.wf_incremental_ctxt);
xb_wf_incremental_ctxt_t *cp = &(ctxt->wf_incremental_ctxt);
for (i = 0, page = cursor->buf; i < cursor->buf_npages;
i++, page += page_size) {
if (incremental_lsn >= mach_read_from_8(page + FIL_PAGE_LSN)) {
if ((!cp->corrupted_pages ||
!cp->corrupted_pages->contains(cursor->node->space->id,
cursor->buf_page_no + i)) &&
incremental_lsn >= mach_read_from_8(page + FIL_PAGE_LSN))
continue;
}
/* updated page */
if (cp->npages == page_size / 4) {
......@@ -163,7 +165,7 @@ wf_incremental_finalize(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile)
xb_fil_cur_t *cursor = ctxt->cursor;
const ulint page_size
= cursor->page_size.physical();
xb_wf_incremental_ctxt_t *cp = &(ctxt->u.wf_incremental_ctxt);
xb_wf_incremental_ctxt_t *cp = &(ctxt->wf_incremental_ctxt);
if (cp->npages != page_size / 4) {
mach_write_to_4(cp->delta_buf + cp->npages * 4, 0xFFFFFFFFUL);
......@@ -185,7 +187,7 @@ Free the incremental page write filter's buffer. */
static void
wf_incremental_deinit(xb_write_filt_ctxt_t *ctxt)
{
xb_wf_incremental_ctxt_t *cp = &(ctxt->u.wf_incremental_ctxt);
xb_wf_incremental_ctxt_t *cp = &(ctxt->wf_incremental_ctxt);
os_mem_free_large(cp->delta_buf, cp->delta_buf_size);
}
......@@ -195,7 +197,7 @@ Initialize the write-through page write filter.
@return TRUE on success, FALSE on error. */
static my_bool
wf_wt_init(xb_write_filt_ctxt_t *ctxt, char *dst_name __attribute__((unused)),
xb_fil_cur_t *cursor)
xb_fil_cur_t *cursor, CorruptedPages *)
{
ctxt->cursor = cursor;
......
......@@ -27,26 +27,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
#include "fil_cur.h"
#include "datasink.h"
#include "xtrabackup.h"
/* Incremental page filter context */
typedef struct {
ulint delta_buf_size;
byte *delta_buf;
ulint npages;
CorruptedPages *corrupted_pages;
} xb_wf_incremental_ctxt_t;
/* Page filter context used as an opaque structure by callers */
typedef struct {
xb_fil_cur_t *cursor;
union {
xb_wf_incremental_ctxt_t wf_incremental_ctxt;
} u;
xb_wf_incremental_ctxt_t wf_incremental_ctxt;
} xb_write_filt_ctxt_t;
typedef struct {
my_bool (*init)(xb_write_filt_ctxt_t *ctxt, char *dst_name,
xb_fil_cur_t *cursor);
xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages);
my_bool (*process)(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile);
my_bool (*finalize)(xb_write_filt_ctxt_t *, ds_file_t *dstfile);
void (*deinit)(xb_write_filt_ctxt_t *);
......
This diff is collapsed.
......@@ -35,6 +35,32 @@ struct xb_delta_info_t
ulint space_id;
};
class CorruptedPages
{
public:
CorruptedPages();
~CorruptedPages();
void add_page(const char *file_name, ulint space_id, ulint page_no);
bool contains(ulint space_id, ulint page_no) const;
void drop_space(ulint space_id);
void rename_space(ulint space_id, const std::string &new_name);
bool print_to_file(const char *file_name) const;
void read_from_file(const char *file_name);
bool empty() const;
void zero_out_free_pages();
private:
void add_page_no_lock(const char *space_name, ulint space_id, ulint page_no,
bool convert_space_name);
struct space_info_t {
std::string space_name;
std::set<ulint> pages;
};
typedef std::map<ulint, space_info_t> container_t;
mutable pthread_mutex_t m_mutex;
container_t m_spaces;
};
/* value of the --incremental option */
extern lsn_t incremental_lsn;
......@@ -110,6 +136,7 @@ extern my_bool opt_remove_original;
extern my_bool opt_extended_validation;
extern my_bool opt_encrypted_backup;
extern my_bool opt_lock_ddl_per_table;
extern my_bool opt_log_innodb_page_corruption;
extern char *opt_incremental_history_name;
extern char *opt_incremental_history_uuid;
......
use strict;
use warnings;
use Fcntl qw(:DEFAULT :seek);
do "$ENV{MTR_SUITE_DIR}/../innodb/include/crc32.pl";
sub corrupt_space_page_id {
my $file_name = shift;
my @pages_to_corrupt = @_;
my $page_size = $ENV{INNODB_PAGE_SIZE};
sysopen my $ibd_file, $file_name, O_RDWR || die "Cannot open $file_name\n";
sysread($ibd_file, $_, 38) || die "Cannot read $file_name\n";
my $space = unpack("x[34]N", $_);
foreach my $page_no (@pages_to_corrupt) {
$space += 10; # generate wrong space id
sysseek($ibd_file, $page_size * $page_no, SEEK_SET)
|| die "Cannot seek $file_name\n";
my $head = pack("Nx[18]", $page_no + 10); # generate wrong page number
my $body = chr(0) x ($page_size - 38 - 8);
# Calculate innodb_checksum_algorithm=crc32 for the unencrypted page.
# The following bytes are excluded:
# bytes 0..3 (the checksum is stored there)
# bytes 26..37 (encryption key version, post-encryption checksum, tablespace id)
# bytes $page_size-8..$page_size-1 (checksum, LSB of FIL_PAGE_LSN)
my $polynomial = 0x82f63b78; # CRC-32C
my $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
my $page= pack("N",$ck).$head.pack("NNN",1,$ck,$space).$body.pack("Nx[4]",$ck);
die unless syswrite($ibd_file, $page, $page_size) == $page_size;
}
close $ibd_file;
}
sub extend_space {
my $file_name = shift;
my $n_pages = shift;
my $page_size = $ENV{INNODB_PAGE_SIZE};
my $page;
sysopen my $ibd_file, $file_name, O_RDWR || die "Cannot open $file_name\n";
sysread($ibd_file, $page, $page_size)
|| die "Cannot read $file_name\n";
my $size = unpack("N", substr($page, 46, 4));
my $packed_new_size = pack("N", $size + $n_pages);
substr($page, 46, 4, $packed_new_size);
my $head = substr($page, 4, 22);
my $body = substr($page, 38, $page_size - 38 - 8);
my $polynomial = 0x82f63b78; # CRC-32C
my $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
my $packed_ck = pack("N", $ck);
substr($page, 0, 4, $packed_ck);
substr($page, $page_size - 8, 4, $packed_ck);
sysseek($ibd_file, 0, SEEK_SET)
|| die "Cannot seek $file_name\n";
die unless syswrite($ibd_file, $page, $page_size) == $page_size;
sysseek($ibd_file, 0, SEEK_END)
|| die "Cannot seek $file_name\n";
my $pages_size = $page_size*$n_pages;
my $pages = chr(0) x $pages_size;
die unless syswrite($ibd_file, $pages, $pages_size) == $pages_size;
close $ibd_file;
return $size;
}
sub die_if_page_is_not_zero {
my $file_name = shift;
my @pages_to_check = @_;
no locale;
my $page_size = $ENV{INNODB_PAGE_SIZE};
my $zero_page = chr(0) x $page_size;
sysopen my $ibd_file, $file_name, O_RDWR || die "Cannot open $file_name\n";
foreach my $page_no_to_check (@pages_to_check) {
sysseek($ibd_file, $page_size*$page_no_to_check, SEEK_SET) ||
die "Cannot seek $file_name\n";
sysread($ibd_file, my $read_page, $page_size) ||
die "Cannot read $file_name\n";
die "The page $page_no_to_check is not zero-filed in $file_name"
if ($read_page cmp $zero_page);
}
close $ibd_file;
}
sub print_corrupted_pages_file {
my $file_in = shift;
my $file_out = shift;
open my $fh, '<', $file_in || die $!;
my $line_number = 0;
my $space = {};
my @spaces;
while (my $line = <$fh>) {
++$line_number;
if ($line_number & 1) {
my ($name, $id) = split(/ /, $line);
$space->{name} = $name;
}
else {
$space->{pages} = $line;
push (@spaces, $space);
$space = {};
}
}
close $fh;
my @sorted_spaces = sort { $a->{name} cmp $b->{name} } @spaces;
open $fh, '>', $file_out || die $!;
foreach my $space (@sorted_spaces) {
print $fh $space->{name};
print $fh "\n";
print $fh $space->{pages};
}
close $fh;
}
sub append_corrupted_pages {
my $file_name = shift;
my $space_name = shift;
my $pages = shift;
open my $fh, '<', $file_name || die $!;
my $line_number = 0;
my $space_line;
while (my $line = <$fh>) {
++$line_number;
if ($line_number & 1) {
my ($name, $id) = split(/ /, $line);
if ($name eq $space_name) {
$space_line = $line;
last;
}
}
}
close $fh;
if (not defined $space_line) {
die "Can't find requested space $space_name in file $file_name";
}
open $fh, '>>', $file_name || die $!;
print $fh $space_line;
print $fh "$pages\n";
close $fh;
}
......@@ -22,7 +22,7 @@ INSERT into t1 values(1);
--let after_copy_test_t2=DROP TABLE test.t2
--let after_copy_test_t3=CREATE INDEX a_i ON test.t3(i);
--let before_copy_test_t10=DROP TABLE test.t10
--let wait_innodb_redo_before_copy=test/t10
--let wait_innodb_redo_before_copy_test_t10 = 1
# mariabackup should crash with assertion if MDEV-24026 is not fixed
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$incremental_dir --incremental-basedir=$basedir --dbug=+d,mariabackup_events,mariabackup_inject_code;
......
########
# Test for generating "innodb_corrupted_pages" file during full and
# incremental backup, including DDL processing
###
CREATE TABLE t1_corrupted(c INT) ENGINE INNODB;
CREATE TABLE t2_corrupted(c INT) ENGINE INNODB;
CREATE TABLE t3(c INT) ENGINE INNODB;
CREATE TABLE t5_corrupted_to_rename(c INT) ENGINE INNODB;
CREATE TABLE t6_corrupted_to_drop(c INT) ENGINE INNODB;
CREATE TABLE t7_corrupted_to_alter(c INT) ENGINE INNODB;
CREATE TABLE t1_inc_corrupted(c INT) ENGINE INNODB;
CREATE TABLE t2_inc_corrupted(c INT) ENGINE INNODB;
CREATE TABLE t3_inc(c INT) ENGINE INNODB;
CREATE TABLE t5_inc_corrupted_to_rename(c INT) ENGINE INNODB;
CREATE TABLE t6_inc_corrupted_to_drop(c INT) ENGINE INNODB;
CREATE TABLE t7_inc_corrupted_to_alter(c INT) ENGINE INNODB;
INSERT INTO t1_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
INSERT INTO t2_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
INSERT INTO t3 VALUES (3), (4), (5), (6), (7), (8), (9);
INSERT INTO t5_corrupted_to_rename VALUES (3), (4), (5), (6), (7), (8), (9);
INSERT INTO t6_corrupted_to_drop VALUES (3), (4), (5), (6), (7), (8), (9);
INSERT INTO t7_corrupted_to_alter VALUES (3), (4), (5), (6), (7), (8), (9);
# Corrupt tables
# Backup must fail due to page corruption
FOUND 1 /Database page corruption detected.*/ in backup.log
# "innodb_corrupted_pages" file must not exist
# Backup must fail, but "innodb_corrupted_pages" file must be created due to --log-innodb-page-corruption option
FOUND 1 /Database page corruption detected.*/ in backup.log
--- "innodb_corrupted_pages" file content: ---
test/t1_corrupted
6 8 9
test/t2_corrupted
7 8 10
test/t4_corrupted_new
1
test/t5_corrupted_to_rename_renamed
6
test/t7_corrupted_to_alter
3
------
INSERT INTO t1_inc_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
INSERT INTO t2_inc_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
INSERT INTO t3_inc VALUES (3), (4), (5), (6), (7), (8), (9);
# Backup must fail, but "innodb_corrupted_pages" file must be created due to --log-innodb-page-corruption option
--- "innodb_corrupted_pages" file content: ---
test/t1_corrupted
6 8 9
test/t1_inc_corrupted
6 8 9
test/t2_corrupted
7 8 10
test/t2_inc_corrupted
7 8 10
test/t4_inc_corrupted_new
1
test/t5_corrupted_to_rename_renamed
6
test/t5_inc_corrupted_to_rename_renamed
6
test/t7_inc_corrupted_to_alter
3
------
# Check if corrupted pages were copied to delta files, and non-corrupted pages are not copied.
DROP TABLE t1_corrupted;
DROP TABLE t2_corrupted;
DROP TABLE t4_corrupted_new;
DROP TABLE t5_corrupted_to_rename_renamed;
DROP TABLE t7_corrupted_to_alter;
DROP TABLE t1_inc_corrupted;
DROP TABLE t2_inc_corrupted;
DROP TABLE t4_inc_corrupted_new;
DROP TABLE t5_inc_corrupted_to_rename_renamed;
DROP TABLE t7_inc_corrupted_to_alter;
########
# Test for --prepare with "innodb_corrupted_pages" file
###
# Extend some tablespace and corrupt extended pages for full backup
# Full backup with --log-innodb-page-corruption
--- "innodb_corrupted_pages" file content: ---
test/t3
6 8
------
# Extend some tablespace and corrupt extended pages for incremental backup
# Incremental backup --log-innodb-page-corruption
--- "innodb_corrupted_pages" file content: ---
test/t3
6 8
test/t3_inc
6 8
------
# Full backup prepare
# "innodb_corrupted_pages" file must not exist after successful prepare
FOUND 1 /was successfuly fixed.*/ in backup.log
# Check that fixed pages are zero-filled
# Incremental backup prepare
# "innodb_corrupted_pages" file must not exist after successful prepare
# do not remove "innodb_corrupted_pages" in incremental dir
FOUND 1 /was successfuly fixed.*/ in backup.log
# Check that fixed pages are zero-filled
# shutdown server
# remove datadir
# xtrabackup move back
# restart server
SELECT * FROM t3;
c
3
4
5
6
7
8
9
SELECT * FROM t3_inc;
c
3
4
5
6
7
8
9
# Test the case when not all corrupted pages are fixed
# Add some fake corrupted pages
# Full backup prepare
FOUND 1 /Error: corrupted page.*/ in backup.log
--- "innodb_corrupted_pages" file content: ---
test/t3
3
------
# Incremental backup prepare
FOUND 1 /Error: corrupted page.*/ in backup.log
--- "innodb_corrupted_pages" file content: ---
test/t3
3
------
DROP TABLE t3;
DROP TABLE t3_inc;
This diff is collapsed.
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