mysql-server/mysql-test/common/binlog/gtid_next_xa_error_simul.test
2025-03-05 14:31:37 +07:00

299 lines
8.9 KiB
Text

# ==== Purpose ====
#
# Auxiliary test file included by 'common/binlog/gtid_next_xa.inc'.
# It differs from 'gtid_next_xa.test' to require to specify mandatory
# prepare or commit simulated error actions.
# Also it provides an optional server restart so the error action
# will unfold after the restart.
#
# This executes an XA PREPARE transaction, followed by an XA COMMIT or
# XA ROLLBACK transaction. It sets specified values of GTID_NEXT
# before each of the two transactions. After each transaction it
# verifies that GTID ownership is as expected.
#
# ==== Usage ====
#
# --let $gtid1= [<GTID> | ANONYMOUS | AUTOMATIC]
# --let $gtid2= [<GTID> | ANONYMOUS | AUTOMATIC | none]
# --let $commit= [COMMIT | ROLLBACK]
# --let $error_simul = ["at_prepare" | "at_commit"]
# --let $do_shutdown_after_prepare = [0 | 1]
# --let $one_phase = [one phase]
# --source extra/binlog/gtid_next_xa_error_simul.test
#
# Parameters:
#
# $commit
# If this is COMMIT, the second transaction will be an XA COMMIT
# transaction. If this is ROLLBACK, the second transaction will be
# an XA ROLLBACK transaction.
#
# $gtid1
# The value to use for GTID_NEXT for the first transaction
# (XA PREPARE).
#
# $gtid2
# The value to use for GTID_NEXT for the second transaction
# (XA COMMIT/ROLLBACK). If this is 'none', no SET GTID_NEXT
# statement is used and the previous value is reused.
#
# $one_phase
# Option to commit XA in one phase
#
# $error_simul
# This is an error in applying the prepare or the commit.
# The script accepts one of 'at_prepare' or 'at_commit' values.
# The caller must reset $error_simul=0 after the current invokation.
#
# Local vars:
#
# Error code's precise value is computed when $error_simul is set 'at_prepare'
--let $errno_at_prepare = 0
--let $errno_at_commit = 0
# Status of --log-bin of the server
--let $log_bin=`SELECT @@GLOBAL.log_bin`
# A copy of the main session var value, the var is used by $error_simul
--let $save_innodb_lock_wait_timeout = @@SESSION.innodb_lock_wait_timeout
SET @@SESSION.innodb_lock_wait_timeout = 1;
--echo ---- XID $xid, $commit: $gtid1 + $gtid2 ----
--source include/rpl/connection_source.inc
if ($error_simul != "at_prepare")
{
if ($error_simul != "at_commit")
{
--echo Incorrect 'error_simul' parameter value of $error_simul.
--die
}
}
if ($gtid1 != none)
{
if (!$one_phase)
{
eval SET GTID_NEXT= '$gtid1';
}
if ($one_phase)
{
# In the One-phase commit case the commit gtid2 is only used in the test
eval SET GTID_NEXT= '$gtid2';
}
}
# Prepare error pre-simulation block is place here, before even XA START
# to satisfy both the log-bin OFF and ON case. In the latter one SET @@debug
# cannot be done after XA END.
# ONE PHASE option skips the pre- and the post- prepare error simulation
# handling.
if (!$one_phase)
{
if ($error_simul == "at_prepare")
{
if (!$log_bin)
{
--connection conn_err_simul
BEGIN;
--eval INSERT INTO mysql.gtid_executed VALUES ('$uuida',$xid,$xid,'')
--source include/rpl/connection_source.inc
--disable_query_log
SET @@SESSION.debug = "+d,disable_se_persists_gtid";
--enable_query_log
# The "head" reported error won't be ER_XA_RBROLLBACK
--let $errno_at_prepare=ER_LOCK_WAIT_TIMEOUT
}
if ($log_bin)
{
--let $errno_at_prepare=ER_XA_RBROLLBACK
SET @@SESSION.debug = "+d,simulate_xa_failure_prepare";
}
}
}
eval XA START '$xid';
eval INSERT INTO t1 VALUES($xid);
eval XA END '$xid';
if (!$one_phase)
{
--error $errno_at_prepare
eval XA PREPARE '$xid';
# Prepare error post-simulation block
if ($error_simul == "at_prepare")
{
if (!$log_bin)
{
--connection conn_err_simul
ROLLBACK;
--echo *** The failed at XA PREPARE trx is rolled back, so XA RECOVER ***
--echo *** results in empty list: ***
XA RECOVER;
}
--source include/rpl/connection_source.inc
# Display XA specific error among other reported ones incl the "head" one
SHOW WARNINGS;
# Prepare simulation error indudes the following error at commit
--let $errno_at_commit= ER_XAER_NOTA
if ($log_bin)
{
SET @@SESSION.debug = "-d,simulate_xa_failure_prepare";
}
if (!$log_bin)
{
--disable_query_log
SET @@SESSION.debug = "-d,disable_se_persists_gtid";
--enable_query_log
}
#
# Prove that the simulated error in saving GTID does not update
# neither 'mysql.gtid_executed' table nor @@global.gtid_executed.
#
if (`SELECT GTID_SUBSET('$uuida:$xid', @@GLOBAL.gtid_executed)`)
{
--echo Unexpected committed gtid item $uuida:$xid in @@global.gtid_executed
--die
}
if (`SELECT count(*) > 0 FROM mysql.gtid_executed WHERE source_uuid = '$uuida' AND interval_start = $xid AND interval_end = $xid`)
{
--echo Unexpected committed gtid record $uuida:$xid in 'mysql.gtid_executed'
--die
}
}
--let $assert_text= Thread should not own any GTID.
--let $assert_cond= @@SESSION.GTID_OWNED = ""
--source include/assert.inc
--source include/rpl/connection_source1.inc
--let $assert_text= No thread should own any GTID.
--let $assert_cond= @@GLOBAL.GTID_OWNED = ""
--source include/assert.inc
--let $assert_text= No thread should hold anonymous ownership.
--let $assert_cond= [SHOW STATUS LIKE "ONGOING_ANONYMOUS_TRANSACTION_COUNT", Value, 1] = 0
--source include/assert.inc
if ($do_shutdown_after_prepare)
{
if (!$log_bin)
{
--disconnect conn_err_simul
}
--let $rpl_server_number= 1
--source include/rpl/restart_server.inc
--source include/rpl/set_gtid_mode.inc
if (!$log_bin)
{
--connect(conn_err_simul,localhost,root,,)
}
--source include/rpl/connection_source.inc
SET @@SESSION.innodb_lock_wait_timeout = 1;
# Prove that the prepared XA did not enroll 'mysql.gtid_executed', so
# its gtid record has been already committed:
--eval SELECT count(*) as one FROM mysql.gtid_executed WHERE source_uuid = '$uuida' AND interval_end = $xid
# No rows of the table is being locked by any other (XA) transaction
SELECT sum(interval_end+1)-sum(interval_start) as "count" FROM mysql.gtid_executed FOR UPDATE;
}
--source include/rpl/connection_source.inc
--echo error=$error_commit
if ($gtid2 != none)
{
eval SET GTID_NEXT= '$gtid2';
}
}
if ($commit == ROLLBACK)
{
# Reset $one_phase to empty in this case to satisfy syntax
--let $one_phase=
}
# Commit error pre-simulation block
if ($error_simul == "at_commit")
{
if (!$log_bin)
{
# simulate prepare error
--connection conn_err_simul
BEGIN;
--eval INSERT INTO mysql.gtid_executed VALUES ('$uuidb',$xid,$xid,'')
--source include/rpl/connection_source.inc
--disable_query_log
SET @@SESSION.debug = "+d,disable_se_persists_gtid";
--enable_query_log
# The "head" reported error won't be ER_XA_RBROLLBACK
--let $errno_at_commit=ER_LOCK_WAIT_TIMEOUT
}
if ($log_bin)
{
SET @@SESSION.debug = "+d,simulate_xa_rm_error";
--let $errno_at_commit=ER_XA_RBROLLBACK
}
}
--error $errno_at_commit
eval XA $commit '$xid' $one_phase;
# Prepare error post-simulation block
if ($error_simul == "at_commit")
{
if (!$log_bin)
{
--connection conn_err_simul
ROLLBACK;
--echo *** The failed at XA COMMIT trx is rolled back, so XA RECOVER ***
--echo *** results in empty list: ***
XA RECOVER;
--source include/rpl/connection_source.inc
# Display XA specific error among other reported ones incl the "head" one
SHOW WARNINGS;
--disable_query_log
SET @@SESSION.debug = "-d,disable_se_persists_gtid";
--enable_query_log
#
# Prove that the simulated error in saving GTID does not update
# neither 'mysql.gtid_executed' table nor @@global.gtid_executed.
#
if (`SELECT GTID_SUBSET('$uuidb:$xid', @@GLOBAL.gtid_executed)`)
{
--echo Unexpected committed gtid item $uuidb:$xid in @@global.gtid_executed
--die
}
if (`SELECT count(*) > 0 FROM mysql.gtid_executed WHERE source_uuid = '$uuidb' AND interval_start = $xid AND interval_end = $xid`)
{
--echo Unexpected committed gtid record $uuidb:$xid in 'mysql.gtid_executed'
--die
}
}
if ($log_bin)
{
SET @@SESSION.debug = "-d,simulate_xa_rm_error";
}
}
--let $assert_text= Thread should not own any GTID.
--let $assert_cond= @@SESSION.GTID_OWNED = ""
--source include/assert.inc
--let $assert_text= No thread should own any GTID.
--let $assert_cond= @@GLOBAL.GTID_OWNED = ""
--source include/assert.inc
--let $assert_text= No thread should hold anonymous ownership.
--let $assert_cond= [SHOW STATUS LIKE "ONGOING_ANONYMOUS_TRANSACTION_COUNT", Value, 1] = 0
--source include/assert.inc
ROLLBACK;
# Restore orig wait timeout
--eval SET @@SESSION.innodb_lock_wait_timeout = $save_innodb_lock_wait_timeout
--inc $xid
SET GTID_NEXT = 'AUTOMATIC';