Commit 9fa718b1 authored by Kristian Nielsen's avatar Kristian Nielsen

Fix mariabackup InnoDB recovered binlog position on server upgrade

Before MariaDB 10.3.5, the binlog position was stored in the TRX_SYS page,
while after it is stored in rollback segments. There is code to read the
legacy position from TRX_SYS to handle upgrades. The problem was if the
legacy position happens to compare larger than the position found in
rollback segments; in this case, the old TRX_SYS position would incorrectly
be preferred over the newer position from rollback segments.

Fixed by always preferring a position from rollback segments over a legacy
position.
Signed-off-by: default avatarKristian Nielsen <knielsen@knielsen-hq.org>
parent f8f5ed22
......@@ -31,3 +31,26 @@ sub mycrc32 {
return $crc;
}
# Fix the checksum of an InnoDB tablespace page.
# Inputs:
# $page A bytestring with the page data.
# $full_crc32 Checksum type, see get_full_crc32() in innodb-util.pl
# Returns: the modified page as a bytestring.
sub fix_page_crc {
my ($page, $full_crc32)= @_;
my $ps= length($page);
my $polynomial = 0x82f63b78; # CRC-32C
if ($full_crc32) {
my $ck = mycrc32(substr($page, 0, $ps - 4), 0, $polynomial);
substr($page, $ps - 4, 4) = pack("N", $ck);
} else {
my $ck= pack("N",
mycrc32(substr($page, 4, 22), 0, $polynomial) ^
mycrc32(substr($page, 38, $ps - 38 - 8), 0, $polynomial));
substr($page, 0, 4)= $ck;
substr($page, $ps-8, 4)= $ck;
}
return $page;
}
......@@ -124,3 +124,22 @@ sub ib_restore_ibd_files {
ib_restore_ibd_file($tmpd, $datadir, $db, $table);
}
}
# Read the flag whether a tablespace is using full_crc32.
# Input: filehandle opened on the tablespace.
sub get_full_crc32 {
my ($TBLSPC)= @_;
my $old_pos= sysseek($TBLSPC, 0, 1);
die "tell() failed on tablespace filehandle: $!\n"
unless defined($old_pos);
sysseek($TBLSPC, 0, 0)
or die "sysseek() failed on tablespace filehandle: $!\n";
my $tblspc_hdr;
sysread($TBLSPC, $tblspc_hdr, 58)
or die "Cannot read tablespace header: $!\n";
sysseek($TBLSPC, $old_pos, 0)
or die "sysseek() failed on tablespace filehandle: $!\n";
my $full_crc32=
unpack("N", substr($tblspc_hdr, 54, 4)) & 0x10; # FIL_SPACE_FLAGS
return $full_crc32;
}
# restart
RESET MASTER;
CREATE TABLE t1(a varchar(60) PRIMARY KEY, b VARCHAR(60)) ENGINE INNODB;
INSERT INTO t1 VALUES(1, NULL);
......
......@@ -3,6 +3,58 @@
# Test provisioning a slave from an existing server, using mariabackup --no-lock
# and the binlog position recovered from InnoDB redo log.
# Update the InnoDB system tablespace to simulate a pre-10.3.5
# position in TRX_SYS. There was a bug that the wrong position could
# be recovered if the old filename in TRX_SYS compares newer than the
# newer filenames stored in rseg headers.
let MYSQLD_DATADIR=`select @@datadir`;
let INNODB_PAGE_SIZE=`select @@innodb_page_size`;
--source include/shutdown_mysqld.inc
--perl
use strict;
use warnings;
use Fcntl qw(:DEFAULT :seek);
do "$ENV{MTR_SUITE_DIR}/../innodb/include/crc32.pl";
do "$ENV{MTR_SUITE_DIR}/../innodb/include/innodb-util.pl";
my $ps = $ENV{INNODB_PAGE_SIZE};
sysopen IBD_FILE, "$ENV{MYSQLD_DATADIR}/ibdata1", O_RDWR
or die "Cannot open ibdata1: $!\n";
# Read the TRX_SYS page.
my $page;
sysseek(IBD_FILE, $ps * 5, SEEK_SET)
or die "Cannot seek ibdata1: $!\n";
sysread(IBD_FILE, $page, $ps)
or die "Cannot read ibdata1: $!\n";
# Put in an old binlog position that will compare larger than master-bin.000001
my $old_name= '~~~-bin.999999' . chr(0);
my $old_off= 0xffff0000;
my $old_magic= 873422344;
my $binlog_offset= $ps - 1000 + 38;
substr($page, $binlog_offset, 4)= pack('N', $old_magic);
substr($page, $binlog_offset + 4, 4)= pack('N', ($old_off >> 32));
substr($page, $binlog_offset + 8, 4)= pack('N', ($old_off & 0xffffffff));
substr($page, $binlog_offset + 12, length($old_name))= $old_name;
# Write back the modified page.
my $full_crc32= get_full_crc32(\*IBD_FILE);
my $page= fix_page_crc($page, $full_crc32);
sysseek(IBD_FILE, $ps * 5, SEEK_SET)
or die "Cannot seek ibdata1: $!\n";
syswrite(IBD_FILE, $page, $ps) == $ps
or die "Cannot write ibdata1: $!\n";
close IBD_FILE;
EOF
--source include/start_mysqld.inc
let $basedir=$MYSQLTEST_VARDIR/tmp/backup;
RESET MASTER;
......
......@@ -867,6 +867,8 @@ class trx_sys_t
uint64_t recovered_binlog_offset;
/** Latest recovered binlog file name */
char recovered_binlog_filename[TRX_SYS_MYSQL_LOG_NAME_LEN];
/** Set when latest position is from pre-version 10.3.5 TRX_SYS. */
bool recovered_binlog_is_legacy_pos;
/**
......
......@@ -468,7 +468,10 @@ static dberr_t trx_rseg_mem_restore(trx_rseg_t *rseg, trx_id_t &max_trx_id,
compile_time_assert(TRX_RSEG_BINLOG_NAME_LEN == sizeof
trx_sys.recovered_binlog_filename);
int cmp = *trx_sys.recovered_binlog_filename
/* Always prefer a position from rollback segment over
a legacy position from before version 10.3.5. */
int cmp = *trx_sys.recovered_binlog_filename &&
!trx_sys.recovered_binlog_is_legacy_pos
? strncmp(binlog_name,
trx_sys.recovered_binlog_filename,
TRX_RSEG_BINLOG_NAME_LEN)
......@@ -489,6 +492,7 @@ static dberr_t trx_rseg_mem_restore(trx_rseg_t *rseg, trx_id_t &max_trx_id,
trx_sys.recovered_binlog_offset
= binlog_offset;
}
trx_sys.recovered_binlog_is_legacy_pos= false;
}
#ifdef WITH_WSREP
......@@ -564,6 +568,7 @@ static void trx_rseg_init_binlog_info(const page_t* page)
trx_sys.recovered_binlog_offset = mach_read_from_8(
TRX_SYS_MYSQL_LOG_INFO + TRX_SYS_MYSQL_LOG_OFFSET
+ TRX_SYS + page);
trx_sys.recovered_binlog_is_legacy_pos= true;
}
#ifdef WITH_WSREP
......@@ -578,6 +583,7 @@ dberr_t trx_rseg_array_init()
*trx_sys.recovered_binlog_filename = '\0';
trx_sys.recovered_binlog_offset = 0;
trx_sys.recovered_binlog_is_legacy_pos= false;
#ifdef WITH_WSREP
trx_sys.recovered_wsrep_xid.null();
XID wsrep_sys_xid;
......
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