Skip to content

Commit 9fa718b

Browse files
committed
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: Kristian Nielsen <knielsen@knielsen-hq.org>
1 parent f8f5ed2 commit 9fa718b

File tree

6 files changed

+104
-1
lines changed

6 files changed

+104
-1
lines changed

mysql-test/suite/innodb/include/crc32.pl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,26 @@ sub mycrc32 {
3131

3232
return $crc;
3333
}
34+
35+
36+
# Fix the checksum of an InnoDB tablespace page.
37+
# Inputs:
38+
# $page A bytestring with the page data.
39+
# $full_crc32 Checksum type, see get_full_crc32() in innodb-util.pl
40+
# Returns: the modified page as a bytestring.
41+
sub fix_page_crc {
42+
my ($page, $full_crc32)= @_;
43+
my $ps= length($page);
44+
my $polynomial = 0x82f63b78; # CRC-32C
45+
if ($full_crc32) {
46+
my $ck = mycrc32(substr($page, 0, $ps - 4), 0, $polynomial);
47+
substr($page, $ps - 4, 4) = pack("N", $ck);
48+
} else {
49+
my $ck= pack("N",
50+
mycrc32(substr($page, 4, 22), 0, $polynomial) ^
51+
mycrc32(substr($page, 38, $ps - 38 - 8), 0, $polynomial));
52+
substr($page, 0, 4)= $ck;
53+
substr($page, $ps-8, 4)= $ck;
54+
}
55+
return $page;
56+
}

mysql-test/suite/innodb/include/innodb-util.pl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,22 @@ sub ib_restore_ibd_files {
124124
ib_restore_ibd_file($tmpd, $datadir, $db, $table);
125125
}
126126
}
127+
128+
# Read the flag whether a tablespace is using full_crc32.
129+
# Input: filehandle opened on the tablespace.
130+
sub get_full_crc32 {
131+
my ($TBLSPC)= @_;
132+
my $old_pos= sysseek($TBLSPC, 0, 1);
133+
die "tell() failed on tablespace filehandle: $!\n"
134+
unless defined($old_pos);
135+
sysseek($TBLSPC, 0, 0)
136+
or die "sysseek() failed on tablespace filehandle: $!\n";
137+
my $tblspc_hdr;
138+
sysread($TBLSPC, $tblspc_hdr, 58)
139+
or die "Cannot read tablespace header: $!\n";
140+
sysseek($TBLSPC, $old_pos, 0)
141+
or die "sysseek() failed on tablespace filehandle: $!\n";
142+
my $full_crc32=
143+
unpack("N", substr($tblspc_hdr, 54, 4)) & 0x10; # FIL_SPACE_FLAGS
144+
return $full_crc32;
145+
}

mysql-test/suite/mariabackup/slave_provision_nolock.result

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# restart
12
RESET MASTER;
23
CREATE TABLE t1(a varchar(60) PRIMARY KEY, b VARCHAR(60)) ENGINE INNODB;
34
INSERT INTO t1 VALUES(1, NULL);

mysql-test/suite/mariabackup/slave_provision_nolock.test

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,58 @@
33

44
# Test provisioning a slave from an existing server, using mariabackup --no-lock
55
# and the binlog position recovered from InnoDB redo log.
6+
7+
# Update the InnoDB system tablespace to simulate a pre-10.3.5
8+
# position in TRX_SYS. There was a bug that the wrong position could
9+
# be recovered if the old filename in TRX_SYS compares newer than the
10+
# newer filenames stored in rseg headers.
11+
let MYSQLD_DATADIR=`select @@datadir`;
12+
let INNODB_PAGE_SIZE=`select @@innodb_page_size`;
13+
14+
--source include/shutdown_mysqld.inc
15+
16+
--perl
17+
use strict;
18+
use warnings;
19+
use Fcntl qw(:DEFAULT :seek);
20+
do "$ENV{MTR_SUITE_DIR}/../innodb/include/crc32.pl";
21+
do "$ENV{MTR_SUITE_DIR}/../innodb/include/innodb-util.pl";
22+
23+
my $ps = $ENV{INNODB_PAGE_SIZE};
24+
25+
sysopen IBD_FILE, "$ENV{MYSQLD_DATADIR}/ibdata1", O_RDWR
26+
or die "Cannot open ibdata1: $!\n";
27+
28+
# Read the TRX_SYS page.
29+
my $page;
30+
sysseek(IBD_FILE, $ps * 5, SEEK_SET)
31+
or die "Cannot seek ibdata1: $!\n";
32+
sysread(IBD_FILE, $page, $ps)
33+
or die "Cannot read ibdata1: $!\n";
34+
35+
# Put in an old binlog position that will compare larger than master-bin.000001
36+
my $old_name= '~~~-bin.999999' . chr(0);
37+
my $old_off= 0xffff0000;
38+
my $old_magic= 873422344;
39+
my $binlog_offset= $ps - 1000 + 38;
40+
substr($page, $binlog_offset, 4)= pack('N', $old_magic);
41+
substr($page, $binlog_offset + 4, 4)= pack('N', ($old_off >> 32));
42+
substr($page, $binlog_offset + 8, 4)= pack('N', ($old_off & 0xffffffff));
43+
substr($page, $binlog_offset + 12, length($old_name))= $old_name;
44+
45+
# Write back the modified page.
46+
my $full_crc32= get_full_crc32(\*IBD_FILE);
47+
my $page= fix_page_crc($page, $full_crc32);
48+
sysseek(IBD_FILE, $ps * 5, SEEK_SET)
49+
or die "Cannot seek ibdata1: $!\n";
50+
syswrite(IBD_FILE, $page, $ps) == $ps
51+
or die "Cannot write ibdata1: $!\n";
52+
close IBD_FILE;
53+
EOF
54+
55+
--source include/start_mysqld.inc
56+
57+
658
let $basedir=$MYSQLTEST_VARDIR/tmp/backup;
759

860
RESET MASTER;

storage/innobase/include/trx0sys.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,8 @@ class trx_sys_t
867867
uint64_t recovered_binlog_offset;
868868
/** Latest recovered binlog file name */
869869
char recovered_binlog_filename[TRX_SYS_MYSQL_LOG_NAME_LEN];
870+
/** Set when latest position is from pre-version 10.3.5 TRX_SYS. */
871+
bool recovered_binlog_is_legacy_pos;
870872

871873

872874
/**

storage/innobase/trx/trx0rseg.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,10 @@ static dberr_t trx_rseg_mem_restore(trx_rseg_t *rseg, trx_id_t &max_trx_id,
468468
compile_time_assert(TRX_RSEG_BINLOG_NAME_LEN == sizeof
469469
trx_sys.recovered_binlog_filename);
470470

471-
int cmp = *trx_sys.recovered_binlog_filename
471+
/* Always prefer a position from rollback segment over
472+
a legacy position from before version 10.3.5. */
473+
int cmp = *trx_sys.recovered_binlog_filename &&
474+
!trx_sys.recovered_binlog_is_legacy_pos
472475
? strncmp(binlog_name,
473476
trx_sys.recovered_binlog_filename,
474477
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,
489492
trx_sys.recovered_binlog_offset
490493
= binlog_offset;
491494
}
495+
trx_sys.recovered_binlog_is_legacy_pos= false;
492496
}
493497

494498
#ifdef WITH_WSREP
@@ -564,6 +568,7 @@ static void trx_rseg_init_binlog_info(const page_t* page)
564568
trx_sys.recovered_binlog_offset = mach_read_from_8(
565569
TRX_SYS_MYSQL_LOG_INFO + TRX_SYS_MYSQL_LOG_OFFSET
566570
+ TRX_SYS + page);
571+
trx_sys.recovered_binlog_is_legacy_pos= true;
567572
}
568573

569574
#ifdef WITH_WSREP
@@ -578,6 +583,7 @@ dberr_t trx_rseg_array_init()
578583

579584
*trx_sys.recovered_binlog_filename = '\0';
580585
trx_sys.recovered_binlog_offset = 0;
586+
trx_sys.recovered_binlog_is_legacy_pos= false;
581587
#ifdef WITH_WSREP
582588
trx_sys.recovered_wsrep_xid.null();
583589
XID wsrep_sys_xid;

0 commit comments

Comments
 (0)