mirror of
https://github.com/MariaDB/server.git
synced 2026-01-06 05:22:24 +03:00
MDEV-742 XA PREPAREd transaction survive disconnect/server restart
Lifted long standing limitation to the XA of rolling it back at the
transaction's
connection close even if the XA is prepared.
Prepared XA-transaction is made to sustain connection close or server
restart.
The patch consists of
- binary logging extension to write prepared XA part of
transaction signified with
its XID in a new XA_prepare_log_event. The concusion part -
with Commit or Rollback decision - is logged separately as
Query_log_event.
That is in the binlog the XA consists of two separate group of
events.
That makes the whole XA possibly interweaving in binlog with
other XA:s or regular transaction but with no harm to
replication and data consistency.
Gtid_log_event receives two more flags to identify which of the
two XA phases of the transaction it represents. With either flag
set also XID info is added to the event.
When binlog is ON on the server XID::formatID is
constrained to 4 bytes.
- engines are made aware of the server policy to keep up user
prepared XA:s so they (Innodb, rocksdb) don't roll them back
anymore at their disconnect methods.
- slave applier is refined to cope with two phase logged XA:s
including parallel modes of execution.
This patch does not address crash-safe logging of the new events which
is being addressed by MDEV-21469.
CORNER CASES: read-only, pure myisam, binlog-*, @@skip_log_bin, etc
Are addressed along the following policies.
1. The read-only at reconnect marks XID to fail for future
completion with ER_XA_RBROLLBACK.
2. binlog-* filtered XA when it changes engine data is regarded as
loggable even when nothing got cached for binlog. An empty
XA-prepare group is recorded. Consequent Commit-or-Rollback
succeeds in the Engine(s) as well as recorded into binlog.
3. The same applies to the non-transactional engine XA.
4. @@skip_log_bin=OFF does not record anything at XA-prepare
(obviously), but the completion event is recorded into binlog to
admit inconsistency with slave.
The following actions are taken by the patch.
At XA-prepare:
when empty binlog cache - don't do anything to binlog if RO,
otherwise write empty XA_prepare (assert(binlog-filter case)).
At Disconnect:
when Prepared && RO (=> no binlogging was done)
set Xid_cache_element::error := ER_XA_RBROLLBACK
*keep* XID in the cache, and rollback the transaction.
At XA-"complete":
Discover the error, if any don't binlog the "complete",
return the error to the user.
Kudos
-----
Alexey Botchkov took to drive this work initially.
Sergei Golubchik, Sergei Petrunja, Marko Mäkelä provided a number of
good recommendations.
Sergei Voitovich made a magnificent review and improvements to the code.
They all deserve a bunch of thanks for making this work done!
This commit is contained in:
9
mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc
Normal file
9
mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc
Normal file
@@ -0,0 +1,9 @@
|
||||
# param $xid to name xa and take part in the connection name
|
||||
# param $query to execute as the xa body
|
||||
# param $db_ign the default database
|
||||
|
||||
--connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,)
|
||||
--eval xa start '$xid'
|
||||
--eval $query
|
||||
--eval xa end '$xid'
|
||||
--eval xa prepare '$xid';
|
||||
235
mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa.test
Normal file
235
mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa.test
Normal file
@@ -0,0 +1,235 @@
|
||||
# The tests verify concurrent execution of replicated (MDEV-742)
|
||||
# XA transactions in the parallel optimistic mode.
|
||||
|
||||
--source include/have_innodb.inc
|
||||
--source include/have_perfschema.inc
|
||||
--source include/master-slave.inc
|
||||
|
||||
# Tests' global declarations
|
||||
--let $trx = _trx_
|
||||
|
||||
call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction");
|
||||
call mtr.add_suppression("WSREP: handlerton rollback failed");
|
||||
#call mtr.add_suppression("Can't find record in 't1'");
|
||||
CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
|
||||
|
||||
--connection master
|
||||
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
|
||||
--save_master_pos
|
||||
|
||||
# Prepare to restart slave into optimistic parallel mode
|
||||
--connection slave
|
||||
--sync_with_master
|
||||
--source include/stop_slave.inc
|
||||
SET @old_parallel_threads = @@GLOBAL.slave_parallel_threads;
|
||||
SET @@global.slave_parallel_threads = 7;
|
||||
SET @old_parallel_mode = @@GLOBAL.slave_parallel_mode;
|
||||
SET @@global.slave_parallel_mode ='optimistic';
|
||||
# Run the first part of the test with high batch size and see that
|
||||
# old rows remain in the table.
|
||||
SET @old_gtid_cleanup_batch_size = @@GLOBAL.gtid_cleanup_batch_size;
|
||||
SET @@global.gtid_cleanup_batch_size = 1000000;
|
||||
|
||||
CHANGE MASTER TO master_use_gtid=slave_pos;
|
||||
|
||||
# LOAD GENERATOR creates XA:s interleaved in binlog when they are from
|
||||
# different connections. All the following block XA:s of the same connection
|
||||
# update the same data which challenges slave optimistic scheduler's correctness.
|
||||
# Slave must eventually apply such load, and correctly (checked).
|
||||
|
||||
--connection master
|
||||
CREATE TABLE t0 (a int, b INT) ENGINE=InnoDB;
|
||||
CREATE TABLE t1 (a int PRIMARY KEY, b INT) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES (1, 0);
|
||||
|
||||
|
||||
# I. Logging some sequence of XA:s by one connection.
|
||||
#
|
||||
# The slave applier's task is to successfully execute a series of
|
||||
# Prepare and Complete parts of a sequence of XA:s
|
||||
|
||||
--let $trx_num = 300
|
||||
--let $i = $trx_num
|
||||
--let $conn = master
|
||||
--disable_query_log
|
||||
while($i > 0)
|
||||
{
|
||||
# 'decision' to commit 0, or rollback 1
|
||||
--let $decision = `SELECT $i % 2`
|
||||
--eval XA START '$conn$trx$i'
|
||||
--eval UPDATE t1 SET b = 1 - 2 * $decision WHERE a = 1
|
||||
--eval XA END '$conn$trx$i'
|
||||
--let $one_phase = `SELECT IF(floor(rand()*10)%2, "ONE PHASE", 0)`
|
||||
if (!$one_phase)
|
||||
{
|
||||
--eval XA PREPARE '$conn$trx$i'
|
||||
--let $one_phase =
|
||||
}
|
||||
|
||||
--let $term = COMMIT
|
||||
if ($decision)
|
||||
{
|
||||
--let $term = ROLLBACK
|
||||
--let $one_phase =
|
||||
}
|
||||
--eval XA $term '$conn$trx$i' $one_phase
|
||||
|
||||
--dec $i
|
||||
}
|
||||
--enable_query_log
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--connection slave
|
||||
--source include/start_slave.inc
|
||||
--source include/sync_with_master_gtid.inc
|
||||
--source include/stop_slave.inc
|
||||
|
||||
|
||||
# II. Logging XS:s from multiple connections in random interweaving manner:
|
||||
#
|
||||
# in a loop ($i) per connection
|
||||
# arrange an inner ($k) loop where
|
||||
# start and prepare an XA;
|
||||
# decide whether to terminate it and then continue to loop innerly
|
||||
# OR disconnect to break the inner loop;
|
||||
# the disconnected one's XA is taken care by 'master' connection
|
||||
#
|
||||
# Effectively binlog must collect a well mixed XA- prepared and terminated
|
||||
# groups for slave to handle.
|
||||
|
||||
--connection master
|
||||
# Total # of connections
|
||||
--let $conn_num=53
|
||||
|
||||
--let $i = $conn_num
|
||||
--disable_query_log
|
||||
while($i > 0)
|
||||
{
|
||||
--connect (master_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,)
|
||||
--dec $i
|
||||
}
|
||||
--enable_query_log
|
||||
|
||||
--let $i = $conn_num
|
||||
while($i > 0)
|
||||
{
|
||||
--let $conn_i = conn$i
|
||||
# $i2 indexes the current connection's "own" row
|
||||
--let $i2 = `SELECT $i + 2`
|
||||
--disable_query_log
|
||||
--connection master_conn$i
|
||||
--enable_query_log
|
||||
--disable_query_log
|
||||
--let $i_conn_id = `SELECT connection_id()`
|
||||
|
||||
--let $decision = 0
|
||||
# the row id of the last connection that committed its XA
|
||||
--let $c_max = 1
|
||||
--let $k = 0
|
||||
while ($decision < 3)
|
||||
{
|
||||
--inc $k
|
||||
--eval XA START '$conn_i$trx$k'
|
||||
# UPDATE depends on previously *committed* transactions
|
||||
--eval UPDATE t1 SET b = b + $k + 1 WHERE a = $c_max
|
||||
if (`SELECT $k % 2 = 1`)
|
||||
{
|
||||
--eval REPLACE INTO t1 VALUES ($i2, $k)
|
||||
}
|
||||
if (`SELECT $k % 2 = 0`)
|
||||
{
|
||||
--eval DELETE FROM t1 WHERE a = $i2
|
||||
}
|
||||
CREATE TEMPORARY TABLE tmp LIKE t0;
|
||||
--eval INSERT INTO tmp SET a=$i, b= $k
|
||||
INSERT INTO t0 SELECT * FROM tmp;
|
||||
DROP TEMPORARY TABLE tmp;
|
||||
--eval XA END '$conn_i$trx$k'
|
||||
|
||||
--let $term = COMMIT
|
||||
--let $decision = `SELECT (floor(rand()*10 % 10) + ($i+$k)) % 4`
|
||||
if ($decision == 1)
|
||||
{
|
||||
--let $term = ROLLBACK
|
||||
}
|
||||
if ($decision < 2)
|
||||
{
|
||||
--eval XA PREPARE '$conn_i$trx$k'
|
||||
--eval XA $term '$conn_i$trx$k'
|
||||
# Iteration counter is taken care *now*
|
||||
}
|
||||
if ($decision == 2)
|
||||
{
|
||||
--eval XA COMMIT '$conn_i$trx$k' ONE PHASE
|
||||
}
|
||||
}
|
||||
|
||||
# $decision = 3
|
||||
--eval XA PREPARE '$conn_i$trx$k'
|
||||
# disconnect now
|
||||
--disconnect master_conn$i
|
||||
--connection master
|
||||
|
||||
--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $i_conn_id
|
||||
--source include/wait_condition.inc
|
||||
|
||||
--disable_query_log
|
||||
--let $decision = `SELECT ($i+$k) % 2`
|
||||
--let $term = COMMIT
|
||||
if ($decision == 1)
|
||||
{
|
||||
--let $term = ROLLBACK
|
||||
}
|
||||
--eval XA $term '$conn_i$trx$k'
|
||||
--let $c_max = $i2
|
||||
|
||||
--dec $i
|
||||
}
|
||||
--enable_query_log
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--connection slave
|
||||
--source include/start_slave.inc
|
||||
--source include/sync_with_master_gtid.inc
|
||||
|
||||
#
|
||||
# Overall consistency check
|
||||
#
|
||||
--let $diff_tables= master:t0, slave:t0
|
||||
--source include/diff_tables.inc
|
||||
--let $diff_tables= master:t1, slave:t1
|
||||
--source include/diff_tables.inc
|
||||
|
||||
|
||||
#
|
||||
# Clean up.
|
||||
#
|
||||
--connection slave
|
||||
--source include/stop_slave.inc
|
||||
set global log_warnings=default;
|
||||
SET GLOBAL slave_parallel_mode=@old_parallel_mode;
|
||||
SET GLOBAL slave_parallel_threads=@old_parallel_threads;
|
||||
--source include/start_slave.inc
|
||||
|
||||
--connection master
|
||||
DROP VIEW v_processlist;
|
||||
DROP TABLE t0, t1;
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--connection slave
|
||||
--source include/sync_with_master_gtid.inc
|
||||
# Check that old rows are deleted from mysql.gtid_slave_pos.
|
||||
# Deletion is asynchronous, so use wait_condition.inc.
|
||||
# Also, there is a small amount of non-determinism in the deletion of old
|
||||
# rows, so it is not guaranteed that there can never be more than
|
||||
# @@gtid_cleanup_batch_size rows in the table; so allow a bit of slack
|
||||
# here.
|
||||
let $wait_condition=
|
||||
SELECT COUNT(*) <= 5*@@GLOBAL.gtid_cleanup_batch_size
|
||||
FROM mysql.gtid_slave_pos;
|
||||
--source include/wait_condition.inc
|
||||
eval $wait_condition;
|
||||
SET GLOBAL gtid_cleanup_batch_size= @old_gtid_cleanup_batch_size;
|
||||
|
||||
--connection master
|
||||
--source include/rpl_end.inc
|
||||
@@ -0,0 +1 @@
|
||||
--log-slave-updates=OFF
|
||||
@@ -0,0 +1,2 @@
|
||||
# --log-slave-updates OFF version of rpl_parallel_optimistic_xa
|
||||
--source rpl_parallel_optimistic_xa.test
|
||||
138
mysql-test/suite/rpl/t/rpl_parallel_xa_same_xid.test
Normal file
138
mysql-test/suite/rpl/t/rpl_parallel_xa_same_xid.test
Normal file
@@ -0,0 +1,138 @@
|
||||
# The tests verify concurrent execution of replicated (MDEV-742)
|
||||
# XA transactions in the parallel optimistic mode.
|
||||
# Prove optimistic scheduler handles xid-namesake XA:s.
|
||||
# That is despite running in parallel there must be no conflicts
|
||||
# caused by multiple transactions' same xid.
|
||||
|
||||
--source include/have_binlog_format_mixed_or_row.inc
|
||||
--source include/have_innodb.inc
|
||||
--source include/have_perfschema.inc
|
||||
--source include/master-slave.inc
|
||||
|
||||
--let $xid_num = 19
|
||||
--let $repeat = 17
|
||||
--let $workers = 7
|
||||
--connection slave
|
||||
call mtr.add_suppression("WSREP: handlerton rollback failed");
|
||||
|
||||
--source include/stop_slave.inc
|
||||
# a measure against MDEV-20605
|
||||
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
|
||||
|
||||
SET @old_parallel_threads = @@GLOBAL.slave_parallel_threads;
|
||||
--disable_query_log
|
||||
--eval SET @@global.slave_parallel_threads = $workers
|
||||
--enable_query_log
|
||||
SET @old_parallel_mode = @@GLOBAL.slave_parallel_mode;
|
||||
SET @@global.slave_parallel_mode ='optimistic';
|
||||
--source include/start_slave.inc
|
||||
|
||||
--connection master
|
||||
CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB;
|
||||
|
||||
--let $i = $xid_num
|
||||
--let $t = t1
|
||||
--disable_query_log
|
||||
while ($i)
|
||||
{
|
||||
--let $k = $repeat
|
||||
while ($k)
|
||||
{
|
||||
--eval XA START 'xid_$i'
|
||||
--eval INSERT INTO $t SET a=$i, b=$k
|
||||
--eval XA END 'xid_$i'
|
||||
--let $one_phase = `SELECT IF(floor(rand()*10)%2, "ONE PHASE", 0)`
|
||||
if (!$one_phase)
|
||||
{
|
||||
--eval XA PREPARE 'xid_$i'
|
||||
--eval XA COMMIT 'xid_$i'
|
||||
}
|
||||
if ($one_phase)
|
||||
{
|
||||
--eval XA COMMIT 'xid_$i' ONE PHASE
|
||||
}
|
||||
|
||||
if (!$one_phase)
|
||||
{
|
||||
--eval XA START 'xid_$i'
|
||||
--eval INSERT INTO $t SET a=$i, b=$k
|
||||
--eval XA END 'xid_$i'
|
||||
--eval XA PREPARE 'xid_$i'
|
||||
--eval XA ROLLBACK 'xid_$i'
|
||||
}
|
||||
|
||||
--dec $k
|
||||
}
|
||||
|
||||
--dec $i
|
||||
}
|
||||
--enable_query_log
|
||||
|
||||
|
||||
|
||||
# Above-like test complicates execution env to create
|
||||
# data conflicts as well. They will be resolved by the optmistic
|
||||
# scheduler as usual.
|
||||
|
||||
CREATE TABLE t2 (a INT AUTO_INCREMENT PRIMARY KEY, b INT) ENGINE=InnoDB;
|
||||
|
||||
--let $i = $xid_num
|
||||
--let $t = t2
|
||||
--disable_query_log
|
||||
while ($i)
|
||||
{
|
||||
--let $k = $repeat
|
||||
while ($k)
|
||||
{
|
||||
--eval XA START 'xid_$i'
|
||||
--eval INSERT INTO $t SET a=NULL, b=$k
|
||||
--eval UPDATE $t SET b=$k + 1 WHERE a=last_insert_id() % $workers
|
||||
--eval XA END 'xid_$i'
|
||||
--let $one_phase = `SELECT IF(floor(rand()*10)%2, "ONE PHASE", 0)`
|
||||
if (!$one_phase)
|
||||
{
|
||||
--eval XA PREPARE 'xid_$i'
|
||||
--eval XA COMMIT 'xid_$i'
|
||||
}
|
||||
if ($one_phase)
|
||||
{
|
||||
--eval XA COMMIT 'xid_$i' ONE PHASE
|
||||
}
|
||||
|
||||
--eval XA START 'xid_$i'
|
||||
--eval UPDATE $t SET b=$k + 1 WHERE a=last_insert_id() % $workers
|
||||
--eval DELETE FROM $t WHERE a=last_insert_id()
|
||||
--eval XA END 'xid_$i'
|
||||
--eval XA PREPARE 'xid_$i'
|
||||
--eval XA ROLLBACK 'xid_$i'
|
||||
|
||||
--let $do_drop_create = `SELECT IF(floor(rand()*10)%100, 1, 0)`
|
||||
if ($do_drop_create)
|
||||
{
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB;
|
||||
}
|
||||
--dec $k
|
||||
}
|
||||
|
||||
--dec $i
|
||||
}
|
||||
--enable_query_log
|
||||
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
--let $diff_tables= master:t1, slave:t1
|
||||
--source include/diff_tables.inc
|
||||
|
||||
#
|
||||
# Clean up.
|
||||
#
|
||||
--connection slave
|
||||
--source include/stop_slave.inc
|
||||
SET GLOBAL slave_parallel_threads=@old_parallel_threads;
|
||||
SET GLOBAL slave_parallel_mode=@old_parallel_mode;
|
||||
--source include/start_slave.inc
|
||||
|
||||
--connection master
|
||||
DROP TABLE t1, t2;
|
||||
|
||||
--source include/rpl_end.inc
|
||||
@@ -6,7 +6,7 @@ call mtr.add_suppression("Deadlock found");
|
||||
call mtr.add_suppression("Can't find record in 't.'");
|
||||
|
||||
connection master;
|
||||
CREATE TABLE t1 (a INT PRIMARY KEY, b INT);
|
||||
CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=innodb;
|
||||
INSERT INTO t1 VALUES (1,1), (2,2), (3,3), (4,4);
|
||||
sync_slave_with_master;
|
||||
SHOW STATUS LIKE 'Slave_retried_transactions';
|
||||
@@ -14,20 +14,94 @@ SHOW STATUS LIKE 'Slave_retried_transactions';
|
||||
# the following UPDATE t1 to pass the mode is switched temprorarily
|
||||
set @@global.slave_exec_mode= 'IDEMPOTENT';
|
||||
UPDATE t1 SET a = 5, b = 47 WHERE a = 1;
|
||||
SELECT * FROM t1;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
connection master;
|
||||
UPDATE t1 SET a = 5, b = 5 WHERE a = 1;
|
||||
SELECT * FROM t1;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
#SHOW BINLOG EVENTS;
|
||||
sync_slave_with_master;
|
||||
set @@global.slave_exec_mode= default;
|
||||
SHOW STATUS LIKE 'Slave_retried_transactions';
|
||||
SELECT * FROM t1;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
source include/check_slave_is_running.inc;
|
||||
|
||||
connection slave;
|
||||
call mtr.add_suppression("Slave SQL.*Could not execute Update_rows event on table test.t1");
|
||||
call mtr.add_suppression("Slave SQL for channel '': worker thread retried transaction");
|
||||
call mtr.add_suppression("The slave coordinator and worker threads are stopped");
|
||||
#
|
||||
# Bug#24764800 REPLICATION FAILING ON SLAVE WITH XAER_RMFAIL ERROR
|
||||
#
|
||||
# Verify that a temporary failing replicated xa transaction completes
|
||||
# upon slave applier restart after previous
|
||||
# @@global.slave_transaction_retries number of retries in vain.
|
||||
#
|
||||
connection slave;
|
||||
|
||||
set @save_innodb_lock_wait_timeout=@@global.innodb_lock_wait_timeout;
|
||||
set @save_slave_transaction_retries=@@global.slave_transaction_retries;
|
||||
|
||||
# Slave applier parameters for the failed retry
|
||||
set @@global.innodb_lock_wait_timeout=1;
|
||||
set @@global.slave_transaction_retries=2;
|
||||
--source include/restart_slave_sql.inc
|
||||
|
||||
# Temporary error implement: a record is blocked by slave local trx
|
||||
connection slave1;
|
||||
BEGIN;
|
||||
INSERT INTO t1 SET a = 6, b = 7;
|
||||
|
||||
connection master;
|
||||
INSERT INTO t1 SET a = 99, b = 99; # slave applier warm up trx
|
||||
XA START 'xa1';
|
||||
INSERT INTO t1 SET a = 6, b = 6; # this record eventually must be found on slave
|
||||
XA END 'xa1';
|
||||
XA PREPARE 'xa1';
|
||||
|
||||
connection slave;
|
||||
# convert_error(ER_LOCK_WAIT_TIMEOUT)
|
||||
--let $err_timeout= 1205
|
||||
# convert_error(ER_LOCK_DEADLOCK)
|
||||
--let $err_deadlock= 1213
|
||||
--let $slave_sql_errno=$err_deadlock,$err_timeout
|
||||
--let $show_slave_sql_error=
|
||||
--source include/wait_for_slave_sql_error.inc
|
||||
|
||||
# b. Slave applier parameters for successful retry after restart
|
||||
set @@global.innodb_lock_wait_timeout=1;
|
||||
set @@global.slave_transaction_retries=100;
|
||||
|
||||
--source include/restart_slave_sql.inc
|
||||
|
||||
--let $last_retries= query_get_value(SHOW GLOBAL STATUS LIKE 'Slave_retried_transactions', Value, 1)
|
||||
--let $status_type=GLOBAL
|
||||
--let $status_var=Slave_retried_transactions
|
||||
--let $status_var_value=`SELECT 1 + $last_retries`
|
||||
--let $$status_var_comparsion= >
|
||||
--source include/wait_for_status_var.inc
|
||||
|
||||
# Release the record after just one retry
|
||||
connection slave1;
|
||||
ROLLBACK;
|
||||
|
||||
connection master;
|
||||
XA COMMIT 'xa1';
|
||||
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
|
||||
# Proof of correctness: the committed XA is on the slave
|
||||
connection slave;
|
||||
--let $assert_text=XA transaction record must be in the table
|
||||
--let $assert_cond=count(*)=1 FROM t1 WHERE a=6 AND b=6
|
||||
--source include/assert.inc
|
||||
|
||||
# Bug#24764800 cleanup:
|
||||
set @@global.innodb_lock_wait_timeout=@save_innodb_lock_wait_timeout;
|
||||
set @@global.slave_transaction_retries= @save_slave_transaction_retries;
|
||||
|
||||
#
|
||||
# Total cleanup:
|
||||
#
|
||||
connection master;
|
||||
DROP TABLE t1;
|
||||
--sync_slave_with_master
|
||||
|
||||
1
mysql-test/suite/rpl/t/rpl_xa-master.opt
Normal file
1
mysql-test/suite/rpl/t/rpl_xa-master.opt
Normal file
@@ -0,0 +1 @@
|
||||
--binlog-ignore-db=test_ign
|
||||
354
mysql-test/suite/rpl/t/rpl_xa.inc
Normal file
354
mysql-test/suite/rpl/t/rpl_xa.inc
Normal file
@@ -0,0 +1,354 @@
|
||||
#
|
||||
# This "body" file checks general properties of XA transaction replication
|
||||
# as of MDEV-7974.
|
||||
# Parameters:
|
||||
# --let rpl_xa_check= SELECT ...
|
||||
#
|
||||
connection master;
|
||||
create table t1 (a int, b int) engine=InnoDB;
|
||||
insert into t1 values(0, 0);
|
||||
xa start 't';
|
||||
insert into t1 values(1, 2);
|
||||
xa end 't';
|
||||
xa prepare 't';
|
||||
xa commit 't';
|
||||
|
||||
sync_slave_with_master;
|
||||
let $diff_tables= master:t1, slave:t1;
|
||||
source include/diff_tables.inc;
|
||||
|
||||
connection master;
|
||||
|
||||
xa start 't';
|
||||
insert into t1 values(3, 4);
|
||||
xa end 't';
|
||||
xa prepare 't';
|
||||
xa rollback 't';
|
||||
|
||||
sync_slave_with_master;
|
||||
let $diff_tables= master:t1, slave:t1;
|
||||
source include/diff_tables.inc;
|
||||
|
||||
connection master;
|
||||
--disable_warnings
|
||||
SET pseudo_slave_mode=1;
|
||||
--enable_warnings
|
||||
create table t2 (a int) engine=InnoDB;
|
||||
xa start 't';
|
||||
insert into t1 values (5, 6);
|
||||
xa end 't';
|
||||
xa prepare 't';
|
||||
xa start 's';
|
||||
insert into t2 values (0);
|
||||
xa end 's';
|
||||
xa prepare 's';
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
connection slave;
|
||||
source include/sync_with_master_gtid.inc;
|
||||
if ($rpl_xa_check)
|
||||
{
|
||||
--eval $rpl_xa_check
|
||||
if ($rpl_xa_verbose)
|
||||
{
|
||||
--eval SELECT $rpl_xa_check_lhs
|
||||
--eval SELECT $rpl_xa_check_rhs
|
||||
}
|
||||
}
|
||||
xa recover;
|
||||
|
||||
connection master;
|
||||
xa commit 't';
|
||||
xa commit 's';
|
||||
--disable_warnings
|
||||
SET pseudo_slave_mode=0;
|
||||
--enable_warnings
|
||||
sync_slave_with_master;
|
||||
let $diff_tables= master:t1, slave:t1;
|
||||
source include/diff_tables.inc;
|
||||
let $diff_tables= master:t2, slave:t2;
|
||||
source include/diff_tables.inc;
|
||||
|
||||
#
|
||||
# Read-only XA remains prepared after disconnect and must rollback at XA-complete
|
||||
# after recoonect. To the read-only also belongs non-transactional engine XA.
|
||||
#
|
||||
--connection master
|
||||
|
||||
--echo *** At the start of read-only section gtid list is:
|
||||
flush logs;
|
||||
--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
|
||||
--source include/show_gtid_list.inc
|
||||
|
||||
|
||||
set @query1="select 1";
|
||||
set @query2="select count(*) into @s2 from t1";
|
||||
--let $ro_cases=2
|
||||
--let $db=test
|
||||
|
||||
# No disconnect
|
||||
--let $p_trx=$ro_cases
|
||||
while ($p_trx)
|
||||
{
|
||||
--connection master
|
||||
--let $xid=ro_$p_trx
|
||||
--let $query=`SELECT @query$p_trx`
|
||||
--source rpl_create_xa_prepared.inc
|
||||
--let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')`
|
||||
--error 0
|
||||
--disable_query_log
|
||||
--disable_result_log
|
||||
--eval xa $complete '$xid'
|
||||
--enable_result_log
|
||||
--enable_query_log
|
||||
|
||||
--disconnect master_$xid
|
||||
--source include/wait_until_disconnected.inc
|
||||
|
||||
--dec $p_trx
|
||||
}
|
||||
|
||||
|
||||
--let $p_trx=$ro_cases
|
||||
# With diconnect
|
||||
while ($p_trx)
|
||||
{
|
||||
--connection master
|
||||
--let $xid=ro_$p_trx
|
||||
--let $query=`SELECT @query$p_trx`
|
||||
--source rpl_create_xa_prepared.inc
|
||||
|
||||
--disconnect master_$xid
|
||||
--source include/wait_until_disconnected.inc
|
||||
|
||||
--dec $p_trx
|
||||
}
|
||||
|
||||
--echo *** $ro_cases prepared xa:s must be in the list:
|
||||
--connection master
|
||||
xa recover;
|
||||
|
||||
--let $p_trx=$ro_cases
|
||||
while ($p_trx)
|
||||
{
|
||||
--let $xid=ro_$p_trx
|
||||
--let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')`
|
||||
--disable_query_log
|
||||
--disable_result_log
|
||||
--error ER_XA_RBROLLBACK
|
||||
--eval xa $complete '$xid'
|
||||
--enable_result_log
|
||||
--enable_query_log
|
||||
|
||||
--dec $p_trx
|
||||
}
|
||||
--echo *** Zero prepared xa:s must be in the list:
|
||||
xa recover;
|
||||
|
||||
--echo *** At the end of read-only section gtid list has 0 more compare with previous check:
|
||||
flush logs;
|
||||
--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
|
||||
--source include/show_gtid_list.inc
|
||||
|
||||
|
||||
#
|
||||
# XA logging cases while some of XA resources are read-only
|
||||
#
|
||||
# A1. Binlog filter
|
||||
|
||||
|
||||
--let $db=test_ign
|
||||
--eval create database $db
|
||||
set @@sql_log_bin = 0;
|
||||
--eval create table $db.t (a int) engine=InnoDB
|
||||
set @@sql_log_bin = 1;
|
||||
|
||||
--let $xid=rw_no_binlog
|
||||
--let $query=insert into $db.t set a=1
|
||||
--source rpl_create_xa_prepared.inc
|
||||
--disconnect master_$xid
|
||||
--source include/wait_until_disconnected.inc
|
||||
|
||||
--echo *** $xid must be in the list:
|
||||
--connection master
|
||||
xa recover;
|
||||
|
||||
--let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')`
|
||||
--error 0
|
||||
--disable_query_log
|
||||
--disable_result_log
|
||||
--eval xa $complete '$xid'
|
||||
--enable_result_log
|
||||
--enable_query_log
|
||||
|
||||
--echo *** Zero must be in the list:
|
||||
--connection master
|
||||
xa recover;
|
||||
# restore for the following tests
|
||||
--let $db=test
|
||||
|
||||
--echo *** At the end of --binlog-ignore-db section gtid list has 2 more:
|
||||
flush logs;
|
||||
--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
|
||||
--source include/show_gtid_list.inc
|
||||
|
||||
#
|
||||
# A2. Opposite to A1, ineffective execution in Engine may create a
|
||||
# binlog transaction
|
||||
#
|
||||
connection master;
|
||||
create table t3 (a int) engine=innodb;
|
||||
|
||||
--echo *** the disconnected prepare case
|
||||
--let $xid=rw_binlog_only
|
||||
--let $query=delete from t3
|
||||
--connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,)
|
||||
set @@binlog_format=statement;
|
||||
# --source rpl_create_xa_prepared.inc
|
||||
--eval xa start '$xid'
|
||||
--eval $query
|
||||
--eval xa end '$xid'
|
||||
--eval xa prepare '$xid'
|
||||
|
||||
--disconnect master_$xid
|
||||
--source include/wait_until_disconnected.inc
|
||||
|
||||
connection master;
|
||||
--echo *** $xid must be in the list:
|
||||
xa recover;
|
||||
|
||||
--let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')`
|
||||
--disable_query_log
|
||||
--disable_result_log
|
||||
--eval xa $complete '$xid'
|
||||
--enable_result_log
|
||||
--enable_query_log
|
||||
|
||||
--echo *** Zero must be in the list:
|
||||
xa recover;
|
||||
|
||||
--echo *** the same connection complete case.
|
||||
connection master;
|
||||
--let $xid=rw_binlog_only
|
||||
--let $query=delete from t3
|
||||
--connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,)
|
||||
set @@binlog_format=statement;
|
||||
# --source rpl_create_xa_prepared.inc
|
||||
--eval xa start '$xid'
|
||||
--eval $query
|
||||
--eval xa end '$xid'
|
||||
--eval xa prepare '$xid'
|
||||
|
||||
--echo *** $xid must be in the list:
|
||||
xa recover;
|
||||
|
||||
--disable_query_log
|
||||
--disable_result_log
|
||||
--eval xa $complete '$xid'
|
||||
--enable_result_log
|
||||
--enable_query_log
|
||||
--disconnect master_$xid
|
||||
--source include/wait_until_disconnected.inc
|
||||
|
||||
--echo *** Zero must be in the list:
|
||||
--connection master
|
||||
xa recover;
|
||||
|
||||
--echo *** At the end of ineffective in engine section gtid list has 5 more:
|
||||
flush logs;
|
||||
--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
|
||||
--source include/show_gtid_list.inc
|
||||
|
||||
#
|
||||
# A3 MyISAM "xa" logs empty XA-prepare group, followed by
|
||||
# an XA-complete event
|
||||
create table tm (a int) engine=myisam;
|
||||
|
||||
# No disconnect
|
||||
--connection master
|
||||
--let $xid=rw_myisam
|
||||
--let $query=insert into tm set a=1
|
||||
--source rpl_create_xa_prepared.inc
|
||||
--let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')`
|
||||
--error 0
|
||||
--disable_query_log
|
||||
--disable_result_log
|
||||
--eval xa $complete '$xid'
|
||||
--enable_result_log
|
||||
--enable_query_log
|
||||
|
||||
--disconnect master_$xid
|
||||
--source include/wait_until_disconnected.inc
|
||||
|
||||
# With diconnect
|
||||
--connection master
|
||||
--source rpl_create_xa_prepared.inc
|
||||
--disconnect master_$xid
|
||||
--source include/wait_until_disconnected.inc
|
||||
|
||||
--echo *** $xid prepared must be in the list:
|
||||
--connection master
|
||||
xa recover;
|
||||
|
||||
--let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')`
|
||||
--disable_query_log
|
||||
--disable_result_log
|
||||
--eval xa $complete '$xid'
|
||||
--enable_result_log
|
||||
--enable_query_log
|
||||
|
||||
--echo *** Zero prepared xa:s must be in the list:
|
||||
xa recover;
|
||||
|
||||
--echo *** At the end of MyISAM "xa" section gtid list has 7 more compare with previous check:
|
||||
flush logs;
|
||||
--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
|
||||
--source include/show_gtid_list.inc
|
||||
|
||||
|
||||
# B. Session binlog disable does not log even empty XA-prepare but XA-complete will be
|
||||
# logged despite of that.
|
||||
|
||||
--let $db=test
|
||||
--let $xid=skip_binlog
|
||||
--let $query=insert into t2 values(1)
|
||||
--connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,)
|
||||
set @@session.sql_log_bin = OFF;
|
||||
--eval xa start '$xid'
|
||||
--eval $query
|
||||
--eval xa end '$xid'
|
||||
--eval xa prepare '$xid'
|
||||
|
||||
--disconnect master_$xid
|
||||
--source include/wait_until_disconnected.inc
|
||||
|
||||
--echo *** $xid must be in the list:
|
||||
--connection master
|
||||
xa recover;
|
||||
|
||||
--connection master
|
||||
call mtr.add_suppression("Slave: XAER_NOTA: Unknown XID");
|
||||
--eval xa rollback '$xid'
|
||||
|
||||
--echo *** Zero must be in the list:
|
||||
--connection master
|
||||
xa recover;
|
||||
|
||||
--echo *** At the end of --binlog-ignore-db section gtid list has 2 more:
|
||||
flush logs;
|
||||
--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
|
||||
--source include/show_gtid_list.inc
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
#
|
||||
# Expected error on slave and manual correction
|
||||
#
|
||||
--connection slave
|
||||
let $slave_sql_errno= 1397; # ER_XAER_NOTA
|
||||
source include/wait_for_slave_sql_error.inc;
|
||||
set @@global.sql_slave_skip_counter= 1;
|
||||
--source include/start_slave.inc
|
||||
|
||||
connection master;
|
||||
--eval drop database test_ign
|
||||
drop table t1, t2, t3, tm;
|
||||
5
mysql-test/suite/rpl/t/rpl_xa.test
Normal file
5
mysql-test/suite/rpl/t/rpl_xa.test
Normal file
@@ -0,0 +1,5 @@
|
||||
source include/have_innodb.inc;
|
||||
source include/master-slave.inc;
|
||||
|
||||
source rpl_xa.inc;
|
||||
source include/rpl_end.inc;
|
||||
1
mysql-test/suite/rpl/t/rpl_xa_gap_lock-slave.opt
Normal file
1
mysql-test/suite/rpl/t/rpl_xa_gap_lock-slave.opt
Normal file
@@ -0,0 +1 @@
|
||||
--transaction-isolation=READ-COMMITTED
|
||||
137
mysql-test/suite/rpl/t/rpl_xa_gap_lock.test
Normal file
137
mysql-test/suite/rpl/t/rpl_xa_gap_lock.test
Normal file
@@ -0,0 +1,137 @@
|
||||
# ==== Purpose ====
|
||||
#
|
||||
# This test will generate two XA transactions on the master in a way that
|
||||
# they will block each other on the slave if the transaction isolation level
|
||||
# used by the slave applier is more restrictive than the READ COMMITTED one.
|
||||
#
|
||||
# Consider:
|
||||
# E=execute, P=prepare, C=commit;
|
||||
# 1=first transaction, 2=second transaction;
|
||||
#
|
||||
# Master does: E1, E2, P2, P1, C1, C2
|
||||
# Slave does: E2, P2, E1, P1, C1, C2
|
||||
#
|
||||
# The transactions are designed so that, if the applier transaction isolation
|
||||
# level is more restrictive than the READ COMMITTED, E1 will be blocked on
|
||||
# the slave waiting for gap locks to be released.
|
||||
#
|
||||
# Step 1
|
||||
#
|
||||
# The test will verify that the transactions don't block each other because
|
||||
# the applier thread automatically changed the isolation level.
|
||||
#
|
||||
# Step 2
|
||||
#
|
||||
# The test will verify that applying master's binary log dump in slave doesn't
|
||||
# block because mysqlbinlog is informing the isolation level to be used.
|
||||
#
|
||||
# ==== Related Bugs and Worklogs ====
|
||||
#
|
||||
# BUG#25040331: INTERLEAVED XA TRANSACTIONS MAY DEADLOCK SLAVE APPLIER WITH
|
||||
# REPEATABLE READ
|
||||
#
|
||||
--source include/have_debug.inc
|
||||
--source include/have_innodb.inc
|
||||
# The test case only make sense for RBR
|
||||
--source include/have_binlog_format_row.inc
|
||||
--source include/master-slave.inc
|
||||
|
||||
--connection slave
|
||||
# To hit the issue, we need to split the data in two pages.
|
||||
# This global variable will help us.
|
||||
SET @saved_innodb_limit_optimistic_insert_debug = @@GLOBAL.innodb_limit_optimistic_insert_debug;
|
||||
SET @@GLOBAL.innodb_limit_optimistic_insert_debug = 2;
|
||||
|
||||
#
|
||||
# Step 1 - Using async replication
|
||||
#
|
||||
|
||||
# Let's generate the workload on the master
|
||||
--connection master
|
||||
CREATE TABLE t1 (
|
||||
c1 INT NOT NULL,
|
||||
KEY(c1)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE t2 (
|
||||
c1 INT NOT NULL,
|
||||
FOREIGN KEY(c1) REFERENCES t1(c1)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO t1 VALUES (1), (3), (4);
|
||||
|
||||
--connection master1
|
||||
XA START 'XA1';
|
||||
INSERT INTO t1 values(2);
|
||||
XA END 'XA1';
|
||||
|
||||
# This transaction will reference the gap where XA1
|
||||
# was inserted, and will be prepared and committed
|
||||
# before XA1, so the slave will prepare it (but will
|
||||
# not commit it) before preparing XA1.
|
||||
--connection master
|
||||
XA START 'XA2';
|
||||
INSERT INTO t2 values(3);
|
||||
XA END 'XA2';
|
||||
|
||||
# The XA2 prepare should be binary logged first
|
||||
XA PREPARE 'XA2';
|
||||
|
||||
# The XA1 prepare should be binary logged
|
||||
# after XA2 prepare and before XA2 commit.
|
||||
--connection master1
|
||||
XA PREPARE 'XA1';
|
||||
|
||||
# The commit order doesn't matter much for the issue being tested.
|
||||
XA COMMIT 'XA1';
|
||||
--connection master
|
||||
XA COMMIT 'XA2';
|
||||
|
||||
# Everything is fine if the slave can sync with the master.
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
|
||||
#
|
||||
# Step 2 - Using mysqlbinlog dump to restore the salve
|
||||
#
|
||||
--source include/stop_slave.inc
|
||||
DROP TABLE t2, t1;
|
||||
RESET SLAVE;
|
||||
RESET MASTER;
|
||||
|
||||
--connection master
|
||||
--let $master_data_dir= `SELECT @@datadir`
|
||||
--let $master_log_file= query_get_value(SHOW MASTER STATUS, File, 1)
|
||||
--let $mysql_server= $MYSQL --defaults-group-suffix=.2
|
||||
--echo Restore binary log from the master into the slave
|
||||
--exec $MYSQL_BINLOG --force-if-open $master_data_dir/$master_log_file | $mysql_server
|
||||
|
||||
--let $diff_tables= master:test.t1, slave:test.t1
|
||||
--source include/diff_tables.inc
|
||||
--let $diff_tables= master:test.t2, slave:test.t2
|
||||
--source include/diff_tables.inc
|
||||
|
||||
#
|
||||
# Cleanup
|
||||
#
|
||||
--let $master_file= query_get_value(SHOW MASTER STATUS, File, 1)
|
||||
--let $master_pos= query_get_value(SHOW MASTER STATUS, Position, 1)
|
||||
DROP TABLE t2, t1;
|
||||
|
||||
## When GTID_MODE=OFF, we need to skip already applied transactions
|
||||
--connection slave
|
||||
#--let $gtid_mode= `SELECT @@GTID_MODE`
|
||||
#if ($gtid_mode == OFF)
|
||||
#{
|
||||
# --disable_query_log
|
||||
# --disable_result_log
|
||||
# --eval CHANGE MASTER TO MASTER_LOG_FILE='$master_file', MASTER_LOG_POS=$master_pos
|
||||
# --enable_result_log
|
||||
# --enable_query_log
|
||||
#}
|
||||
--replace_result $master_file LOG_FILE $master_pos LOG_POS
|
||||
--eval CHANGE MASTER TO MASTER_LOG_FILE='$master_file', MASTER_LOG_POS=$master_pos
|
||||
|
||||
SET @@GLOBAL.innodb_limit_optimistic_insert_debug = @saved_innodb_limit_optimistic_insert_debug;
|
||||
--source include/start_slave.inc
|
||||
|
||||
--source include/rpl_end.inc
|
||||
@@ -0,0 +1 @@
|
||||
--binlog-ignore-db=test_ign
|
||||
29
mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine.test
Normal file
29
mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine.test
Normal file
@@ -0,0 +1,29 @@
|
||||
--source include/have_innodb.inc
|
||||
--source include/master-slave.inc
|
||||
|
||||
--connection slave
|
||||
call mtr.add_suppression("The automatically created table.*name may not be entirely in lowercase");
|
||||
|
||||
--source include/stop_slave.inc
|
||||
CHANGE MASTER TO master_use_gtid=slave_pos;
|
||||
|
||||
SET @@global.gtid_pos_auto_engines="innodb";
|
||||
--source include/start_slave.inc
|
||||
--let $rpl_xa_check_lhs= @@global.gtid_slave_pos
|
||||
--let $rpl_xa_check_rhs= CONCAT(domain_id,"-",server_id,"-",seq_no) FROM mysql.gtid_slave_pos WHERE seq_no = (SELECT DISTINCT max(seq_no) FROM mysql.gtid_slave_pos)
|
||||
--let $rpl_xa_check=SELECT $rpl_xa_check_lhs = $rpl_xa_check_rhs
|
||||
--source rpl_xa.inc
|
||||
|
||||
--connection slave
|
||||
--source include/stop_slave.inc
|
||||
SET @@global.gtid_pos_auto_engines="";
|
||||
SET @@session.sql_log_bin=0;
|
||||
DROP TABLE mysql.gtid_slave_pos_InnoDB;
|
||||
if (`SHOW COUNT(*) WARNINGS`)
|
||||
{
|
||||
show tables in mysql like 'gtid_slave_pos%';
|
||||
}
|
||||
SET @@session.sql_log_bin=1;
|
||||
--source include/start_slave.inc
|
||||
|
||||
--source include/rpl_end.inc
|
||||
297
mysql-test/suite/rpl/t/rpl_xa_survive_disconnect.test
Normal file
297
mysql-test/suite/rpl/t/rpl_xa_survive_disconnect.test
Normal file
@@ -0,0 +1,297 @@
|
||||
# BUG #12161 Xa recovery and client disconnection
|
||||
# the test verifies that
|
||||
# a. disconnection does not lose a prepared transaction
|
||||
# so it can be committed from another connection
|
||||
# c. the prepared transaction is logged
|
||||
# d. interleaved prepared transactions are correctly applied on the slave.
|
||||
|
||||
#
|
||||
# Both replication format are checked through explict
|
||||
# set @@binlog_format in the test.
|
||||
#
|
||||
--source include/have_innodb.inc
|
||||
--source include/have_binlog_format_mixed.inc
|
||||
#
|
||||
# Prepared XA can't get available to an external connection
|
||||
# until a connection, that either leaves actively or is killed,
|
||||
# has completed a necessary part of its cleanup.
|
||||
# Selecting from P_S.threads provides a method to learn that.
|
||||
#
|
||||
--source include/have_perfschema.inc
|
||||
--source include/master-slave.inc
|
||||
|
||||
--connection master
|
||||
call mtr.add_suppression("Found 2 prepared XA transactions");
|
||||
CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
|
||||
|
||||
CREATE DATABASE d1;
|
||||
CREATE DATABASE d2;
|
||||
|
||||
CREATE TABLE d1.t (a INT) ENGINE=innodb;
|
||||
CREATE TABLE d2.t (a INT) ENGINE=innodb;
|
||||
|
||||
connect (master_conn1, 127.0.0.1,root,,test,$MASTER_MYPORT,);
|
||||
--let $conn_id=`SELECT connection_id()`
|
||||
SET @@session.binlog_format= statement;
|
||||
XA START '1-stmt';
|
||||
INSERT INTO d1.t VALUES (1);
|
||||
XA END '1-stmt';
|
||||
XA PREPARE '1-stmt';
|
||||
|
||||
--disconnect master_conn1
|
||||
|
||||
--connection master
|
||||
|
||||
--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
|
||||
--source include/wait_condition.inc
|
||||
|
||||
connect (master_conn2, 127.0.0.1,root,,test,$MASTER_MYPORT,);
|
||||
--let $conn_id=`SELECT connection_id()`
|
||||
SET @@session.binlog_format= row;
|
||||
XA START '1-row';
|
||||
INSERT INTO d2.t VALUES (1);
|
||||
XA END '1-row';
|
||||
XA PREPARE '1-row';
|
||||
|
||||
--disconnect master_conn2
|
||||
|
||||
--connection master
|
||||
--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
|
||||
--source include/wait_condition.inc
|
||||
|
||||
XA START '2';
|
||||
INSERT INTO d1.t VALUES (2);
|
||||
XA END '2';
|
||||
XA PREPARE '2';
|
||||
XA COMMIT '2';
|
||||
|
||||
XA COMMIT '1-row';
|
||||
XA COMMIT '1-stmt';
|
||||
source include/show_binlog_events.inc;
|
||||
|
||||
# the proof: slave is in sync with the table updated by the prepared transactions.
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
|
||||
--source include/stop_slave.inc
|
||||
|
||||
#
|
||||
# Recover with Master server restart
|
||||
#
|
||||
--connection master
|
||||
|
||||
connect (master2, 127.0.0.1,root,,test,$MASTER_MYPORT,);
|
||||
--connection master2
|
||||
SET @@session.binlog_format= statement;
|
||||
XA START '3-stmt';
|
||||
INSERT INTO d1.t VALUES (3);
|
||||
XA END '3-stmt';
|
||||
XA PREPARE '3-stmt';
|
||||
--disconnect master2
|
||||
|
||||
connect (master2, 127.0.0.1,root,,test,$MASTER_MYPORT,);
|
||||
--connection master2
|
||||
SET @@session.binlog_format= row;
|
||||
XA START '3-row';
|
||||
INSERT INTO d2.t VALUES (4);
|
||||
XA END '3-row';
|
||||
XA PREPARE '3-row';
|
||||
--disconnect master2
|
||||
|
||||
--connection master
|
||||
|
||||
#
|
||||
# Testing read-only
|
||||
#
|
||||
connect (master2, 127.0.0.1,root,,test,$MASTER_MYPORT,);
|
||||
--connection master2
|
||||
XA START '4';
|
||||
SELECT * FROM d1.t;
|
||||
XA END '4';
|
||||
XA PREPARE '4';
|
||||
--disconnect master2
|
||||
|
||||
#
|
||||
# Logging few disconnected XA:s for replication.
|
||||
#
|
||||
--let $bulk_trx_num=10
|
||||
--let $i = $bulk_trx_num
|
||||
|
||||
while($i > 0)
|
||||
{
|
||||
--connect (master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,)
|
||||
--let $conn_id=`SELECT connection_id()`
|
||||
|
||||
--eval XA START 'bulk_trx_$i'
|
||||
--eval INSERT INTO d1.t VALUES ($i)
|
||||
--eval INSERT INTO d2.t VALUES ($i)
|
||||
--eval XA END 'bulk_trx_$i'
|
||||
--eval XA PREPARE 'bulk_trx_$i'
|
||||
|
||||
--disconnect master_bulk_conn$i
|
||||
|
||||
--connection master
|
||||
--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
|
||||
--source include/wait_condition.inc
|
||||
|
||||
--dec $i
|
||||
}
|
||||
|
||||
#
|
||||
# Prove the slave applier is capable to resume the prepared XA:s
|
||||
# upon its restart.
|
||||
#
|
||||
--connection slave
|
||||
--source include/start_slave.inc
|
||||
--connection master
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
--source include/stop_slave.inc
|
||||
|
||||
--connection master
|
||||
--let $i = $bulk_trx_num
|
||||
while($i > 0)
|
||||
{
|
||||
--let $command=COMMIT
|
||||
if (`SELECT $i % 2`)
|
||||
{
|
||||
--let $command=ROLLBACK
|
||||
}
|
||||
--eval XA $command 'bulk_trx_$i'
|
||||
--dec $i
|
||||
}
|
||||
|
||||
--let $rpl_server_number= 1
|
||||
--source include/rpl_restart_server.inc
|
||||
|
||||
--connection slave
|
||||
--source include/start_slave.inc
|
||||
|
||||
--connection master
|
||||
--echo *** '3-stmt','3-row' xa-transactions must be in the list ***
|
||||
XA RECOVER;
|
||||
XA COMMIT '3-stmt';
|
||||
XA ROLLBACK '3-row';
|
||||
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
|
||||
#
|
||||
# Testing replication with marginal XID values and in two formats.
|
||||
#
|
||||
|
||||
--connection master
|
||||
--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
|
||||
--source include/wait_condition.inc
|
||||
|
||||
# Max size XID incl max value of formatID
|
||||
--let $formatid_range=`SELECT (1<<31)`
|
||||
--let $max_formatid=`SELECT (1<<31) - 1`
|
||||
|
||||
connect (master_conn2, 127.0.0.1,root,,test,$MASTER_MYPORT,);
|
||||
--let $conn_id=`SELECT connection_id()`
|
||||
|
||||
--let $gtrid=0123456789012345678901234567890123456789012345678901234567890124
|
||||
--let $bqual=0123456789012345678901234567890123456789012345678901234567890124
|
||||
--eval XA START '$gtrid','$bqual',$max_formatid
|
||||
INSERT INTO d1.t VALUES (64);
|
||||
--eval XA END '$gtrid','$bqual',$max_formatid
|
||||
--eval XA PREPARE '$gtrid','$bqual',$max_formatid
|
||||
|
||||
--disconnect master_conn2
|
||||
|
||||
--connection master
|
||||
--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
|
||||
--source include/wait_condition.inc
|
||||
|
||||
# Max size XID with non-ascii chars
|
||||
connect (master_conn3, 127.0.0.1,root,,test,$MASTER_MYPORT,);
|
||||
--let $conn_id=`SELECT connection_id()`
|
||||
|
||||
--let $gtrid_hex=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
--let $bqual_hex=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
--eval XA START X'$gtrid_hex',X'$bqual_hex',0
|
||||
INSERT INTO d1.t VALUES (0);
|
||||
--eval XA END X'$gtrid_hex',X'$bqual_hex',0
|
||||
--eval XA PREPARE X'$gtrid_hex',X'$bqual_hex',0
|
||||
|
||||
--disconnect master_conn3
|
||||
|
||||
--connection master
|
||||
--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
|
||||
--source include/wait_condition.inc
|
||||
|
||||
# Random XID
|
||||
--disable_query_log
|
||||
|
||||
connect (master_conn4, 127.0.0.1,root,,test,$MASTER_MYPORT,);
|
||||
--let $conn_id=`SELECT connection_id()`
|
||||
|
||||
--let $gtridlen=`SELECT 2*(1 + round(rand()*100) % 31)`
|
||||
--let $bquallen=`SELECT 2*(1 + round(rand()*100) % 31)`
|
||||
--let $gtrid_rand=`SELECT substring(concat(MD5(rand()), MD5(rand())), 1, $gtridlen)`
|
||||
--let $bqual_rand=`SELECT substring(concat(MD5(rand()), MD5(rand())), 1, $bquallen)`
|
||||
--let $formt_rand=`SELECT floor((rand()*10000000000) % $formatid_range)`
|
||||
--eval XA START X'$gtrid_rand',X'$bqual_rand',$formt_rand
|
||||
INSERT INTO d1.t VALUES (0);
|
||||
--eval XA END X'$gtrid_rand',X'$bqual_rand',$formt_rand
|
||||
--eval XA PREPARE X'$gtrid_rand',X'$bqual_rand',$formt_rand
|
||||
|
||||
--enable_query_log
|
||||
|
||||
--disconnect master_conn4
|
||||
|
||||
--connection master
|
||||
--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
|
||||
--source include/wait_condition.inc
|
||||
|
||||
--eval XA COMMIT '$gtrid','$bqual',$max_formatid
|
||||
--eval XA COMMIT X'$gtrid_hex',X'$bqual_hex',0
|
||||
--disable_query_log
|
||||
--echo XA COMMIT 'RANDOM XID'
|
||||
--eval XA COMMIT X'$gtrid_rand',X'$bqual_rand',$formt_rand
|
||||
--enable_query_log
|
||||
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
|
||||
#
|
||||
# Testing ONE PHASE
|
||||
#
|
||||
--let $onephase_trx_num=10
|
||||
--let $i = $onephase_trx_num
|
||||
while($i > 0)
|
||||
{
|
||||
--connect (master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,)
|
||||
|
||||
--connection master_bulk_conn$i
|
||||
--eval XA START 'one_phase_$i'
|
||||
--eval INSERT INTO d1.t VALUES ($i)
|
||||
--eval INSERT INTO d2.t VALUES ($i)
|
||||
--eval XA END 'one_phase_$i'
|
||||
--eval XA COMMIT 'one_phase_$i' ONE PHASE
|
||||
|
||||
--disconnect master_bulk_conn$i
|
||||
--dec $i
|
||||
}
|
||||
--connection master
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
|
||||
#
|
||||
# Overall consistency check
|
||||
#
|
||||
--let $diff_tables= master:d1.t, slave:d1.t
|
||||
--source include/diff_tables.inc
|
||||
--let $diff_tables= master:d2.t, slave:d2.t
|
||||
--source include/diff_tables.inc
|
||||
#
|
||||
# cleanup
|
||||
#
|
||||
--connection master
|
||||
|
||||
DELETE FROM d1.t;
|
||||
DELETE FROM d2.t;
|
||||
DROP TABLE d1.t, d2.t;
|
||||
DROP DATABASE d1;
|
||||
DROP DATABASE d2;
|
||||
DROP VIEW v_processlist;
|
||||
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
|
||||
--source include/rpl_end.inc
|
||||
@@ -0,0 +1,2 @@
|
||||
--log-slave-updates=off
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# ==== Purpose ====
|
||||
# 'rpl_xa_survive_disconnect_lsu_off' verifies the same properties as the sourced file
|
||||
# in conditions of the slave does not log own updates
|
||||
# (lsu in the name stands for log_slave_updates).
|
||||
# Specifically this mode aims at proving correct operations on the slave
|
||||
# mysql.gtid_executed.
|
||||
|
||||
--source ./rpl_xa_survive_disconnect.test
|
||||
@@ -0,0 +1,68 @@
|
||||
# BUG#12161 Xa recovery and client disconnection
|
||||
#
|
||||
# The test verifies correct XA transaction two phase logging and its applying
|
||||
# in a case the transaction updates transactional and non-transactional tables.
|
||||
# Transactions are terminated according to specfied parameters to
|
||||
# a sourced inc-file.
|
||||
|
||||
--source include/have_innodb.inc
|
||||
--source include/master-slave.inc
|
||||
|
||||
--connection master
|
||||
CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
|
||||
|
||||
--let $command=setup
|
||||
--source include/rpl_xa_mixed_engines.inc
|
||||
|
||||
--echo === COMMIT ===
|
||||
--let $command=run
|
||||
--let $xa_terminate=XA COMMIT
|
||||
--let $xa_prepare_opt=1
|
||||
--source include/rpl_xa_mixed_engines.inc
|
||||
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
--connection master
|
||||
|
||||
--echo === COMMIT ONE PHASE ===
|
||||
|
||||
--let $command=run
|
||||
--let $xa_terminate=XA COMMIT
|
||||
--let $one_phase=ONE PHASE
|
||||
--let $xa_prepare_opt=
|
||||
--source include/rpl_xa_mixed_engines.inc
|
||||
--let $one_phase=
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
--connection master
|
||||
|
||||
--echo === ROLLBACK with PREPARE ===
|
||||
|
||||
--let $command=run
|
||||
--let $xa_terminate=xa rollback
|
||||
--let $xa_prepare_opt=1
|
||||
--source include/rpl_xa_mixed_engines.inc
|
||||
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
--connection master
|
||||
|
||||
--echo === ROLLBACK with no PREPARE ===
|
||||
|
||||
--let $command=run
|
||||
--let $xa_terminate=xa rollback
|
||||
--let $xa_prepare_opt=
|
||||
--source include/rpl_xa_mixed_engines.inc
|
||||
--let $xa_rollback_only=
|
||||
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
|
||||
--let $diff_tables= master:tm, slave:tm
|
||||
--source include/diff_tables.inc
|
||||
|
||||
# Cleanup
|
||||
|
||||
--connection master
|
||||
--let $command=cleanup
|
||||
--source include/rpl_xa_mixed_engines.inc
|
||||
|
||||
--source include/sync_slave_sql_with_master.inc
|
||||
|
||||
--source include/rpl_end.inc
|
||||
Reference in New Issue
Block a user