Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions mysql-test/suite/mariabackup/slave_provision_prepared_trx.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[mysqld.1]
log-slave-updates
loose-innodb

[mysqld.2]
log-slave-updates
loose-innodb
skip-slave-start

[ENV]
SERVER_MYPORT_1= @mysqld.1.port
SERVER_MYSOCK_1= @mysqld.1.socket
SERVER_MYPORT_2= @mysqld.2.port
SERVER_MYSOCK_2= @mysqld.2.socket
70 changes: 70 additions & 0 deletions mysql-test/suite/mariabackup/slave_provision_prepared_trx.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
### MDEV-36025 backup taken from a slave with optimistic parallel replication fails to restore most of the time
### Test provisioning a slave from an existing server running optimistic
### parallel replication so that prepared transactions are left in the
### backup when the restored server is started.
RESET MASTER;
CREATE TABLE t1(a INT PRIMARY KEY, b VARCHAR(60)) ENGINE INNODB;
INSERT INTO t1 VALUES(1, NULL);
INSERT INTO t1 VALUES(2, NULL);
INSERT INTO t1 VALUES(3, NULL);
connect server2,127.0.0.1,root,,,$SERVER_MYPORT_2;
SET STATEMENT sql_log_bin=0 FOR ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
RESET MASTER;
SET GLOBAL gtid_slave_pos= '';
CHANGE MASTER TO
master_port=PORT, master_host='127.0.0.1', master_user='root',
master_use_gtid= slave_pos;
SET GLOBAL slave_parallel_threads=3;
SET GLOBAL slave_parallel_mode=optimistic;
START SLAVE;
connection default;
include/save_master_gtid.inc
connection server2;
include/sync_with_master_gtid.inc
connect server2a,127.0.0.1,root,,,$SERVER_MYPORT_2;
BEGIN;
SELECT * FROM t1 WHERE a=1 FOR UPDATE;
a b
1 NULL
connection default;
UPDATE t1 SET b='xyzzy' WHERE a=1;
INSERT INTO t1 VALUES (4, NULL);
UPDATE t1 SET b='foobar' WHERE a=2;
include/save_master_gtid.inc
*** Doing backup...
*** Doing prepare...
connection server2a;
ROLLBACK;
include/sync_with_master_gtid.inc
connection server2;
*** Provision a new slave from the backup
*** Stopping provisioned server
*** Removing old datadir for provisioned server
*** Provision new server from backup
disconnect server2a;
# restart
CHANGE MASTER TO
master_port=PORT, master_host='127.0.0.1', master_user='root',
master_use_gtid= slave_pos;
START SLAVE;
connection default;
include/save_master_gtid.inc
SELECT * FROM t1 ORDER BY a;
a b
1 xyzzy
2 foobar
3 NULL
4 NULL
connection server2;
include/sync_with_master_gtid.inc
SELECT * FROM t1 ORDER BY a;
a b
1 xyzzy
2 foobar
3 NULL
4 NULL
STOP SLAVE;
RESET SLAVE ALL;
DROP TABLE t1;
connection default;
DROP TABLE t1;
99 changes: 99 additions & 0 deletions mysql-test/suite/mariabackup/slave_provision_prepared_trx.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
--source include/have_innodb.inc
--source include/have_log_bin.inc

--echo ### MDEV-36025 backup taken from a slave with optimistic parallel replication fails to restore most of the time
--echo ### Test provisioning a slave from an existing server running optimistic
--echo ### parallel replication so that prepared transactions are left in the
--echo ### backup when the restored server is started.


RESET MASTER;
CREATE TABLE t1(a INT PRIMARY KEY, b VARCHAR(60)) ENGINE INNODB;
INSERT INTO t1 VALUES(1, NULL);
INSERT INTO t1 VALUES(2, NULL);
INSERT INTO t1 VALUES(3, NULL);

--connect (server2,127.0.0.1,root,,,$SERVER_MYPORT_2)
let MYSQLD_DATADIR=`select @@datadir`;
let $basedir=$MYSQLTEST_VARDIR/tmp/backup;
SET STATEMENT sql_log_bin=0 FOR ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
RESET MASTER;
SET GLOBAL gtid_slave_pos= '';

--replace_result $SERVER_MYPORT_1 PORT
eval CHANGE MASTER TO
master_port=$SERVER_MYPORT_1, master_host='127.0.0.1', master_user='root',
master_use_gtid= slave_pos;
SET GLOBAL slave_parallel_threads=3;
SET GLOBAL slave_parallel_mode=optimistic;
START SLAVE;

--connection default
--source include/save_master_gtid.inc
--connection server2
--source include/sync_with_master_gtid.inc

# Block transaction T1 from committing on the slave, forcing a following
# T2 to remain in "prepared" state when the backup is taken.
--connect (server2a,127.0.0.1,root,,,$SERVER_MYPORT_2)
BEGIN;
SELECT * FROM t1 WHERE a=1 FOR UPDATE;

--connection default
UPDATE t1 SET b='xyzzy' WHERE a=1;
INSERT INTO t1 VALUES (4, NULL);
UPDATE t1 SET b='foobar' WHERE a=2;
--source include/save_master_gtid.inc

# Sleep a small amount to give a higher chance for the transactions to reach
# their prepared state when the backup runs.
--sleep 0.3

--echo *** Doing backup...
--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --socket=$SERVER_MYSOCK_2 --backup --target-dir=$basedir
--echo *** Doing prepare...
--exec $XTRABACKUP --prepare --binlog-info=1 --target-dir=$basedir

--connection server2a
ROLLBACK;
--source include/sync_with_master_gtid.inc

--connection server2
--echo *** Provision a new slave from the backup

--echo *** Stopping provisioned server
--source include/shutdown_mysqld.inc

--echo *** Removing old datadir for provisioned server
--rmdir $MYSQLD_DATADIR

--echo *** Provision new server from backup
--exec $XTRABACKUP --copy-back --datadir=$MYSQLD_DATADIR --target-dir=$basedir

--disconnect server2a
--source include/start_mysqld.inc

--replace_result $SERVER_MYPORT_1 PORT
eval CHANGE MASTER TO
master_port=$SERVER_MYPORT_1, master_host='127.0.0.1', master_user='root',
master_use_gtid= slave_pos;
# The slave GTID position was saved transactionally to mysql.gtid_slave_pos
# as part of the backup.
START SLAVE;

--connection default
--source include/save_master_gtid.inc
SELECT * FROM t1 ORDER BY a;

--connection server2
--source include/sync_with_master_gtid.inc
SELECT * FROM t1 ORDER BY a;

STOP SLAVE;
RESET SLAVE ALL;
DROP TABLE t1;

--connection default
DROP TABLE t1;

rmdir $basedir;
6 changes: 4 additions & 2 deletions sql/log.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10039,7 +10039,8 @@ int TC_LOG_MMAP::open(const char *opt_name)
inited= 1;
crashed= TRUE;
sql_print_information("Recovering after a crash using %s", opt_name);
if (tc_heuristic_recover)
if (tc_heuristic_recover &&
tc_heuristic_recover != TC_HEURISTIC_RECOVER_AUTO)
{
sql_print_error("Cannot perform automatic crash recovery when "
"--tc-heuristic-recover is used");
Expand Down Expand Up @@ -10562,7 +10563,8 @@ TC_LOG_MMAP tc_log_mmap;

int TC_LOG::using_heuristic_recover()
{
if (!tc_heuristic_recover)
if (!tc_heuristic_recover ||
tc_heuristic_recover == TC_HEURISTIC_RECOVER_AUTO)
return 0;

sql_print_information("Heuristic crash recovery mode");
Expand Down
4 changes: 2 additions & 2 deletions sql/mysqld.cc
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ const char *show_comp_option_name[]= {"YES", "NO", "DISABLED"};

static const char *tc_heuristic_recover_names[]=
{
"OFF", "COMMIT", "ROLLBACK", NullS
"OFF", "COMMIT", "ROLLBACK", "AUTO", NullS
};
static TYPELIB tc_heuristic_recover_typelib=
{
Expand Down Expand Up @@ -7027,7 +7027,7 @@ struct my_option my_long_options[]=
{"tc-heuristic-recover", 0,
"Decision to use in heuristic recover process",
&tc_heuristic_recover, &tc_heuristic_recover,
&tc_heuristic_recover_typelib, GET_ENUM, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
&tc_heuristic_recover_typelib, GET_ENUM, REQUIRED_ARG, 3, 0, 0, 0, 0, 0},
{"temp-pool", 0,
#if (ENABLE_TEMP_POOL)
"Using this option will cause most temporary files created to use a small "
Expand Down
1 change: 1 addition & 0 deletions sql/sql_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ class Recreate_info

#define TC_HEURISTIC_RECOVER_COMMIT 1
#define TC_HEURISTIC_RECOVER_ROLLBACK 2
#define TC_HEURISTIC_RECOVER_AUTO 3
extern ulong tc_heuristic_recover;

typedef struct st_user_var_events
Expand Down