Skip to content

Commit 9b5f1b8

Browse files
author
Andrei Elkin
committed
Bug #20920851 ASSERTION `IS_STARTED()' IN HA_TRX_INFO::NEXT() AT TRANSACTION_INFO.H:138
The assert is hit when XA transaction updated only a non-transactional table and went to prepare stage. At that time MYSQL_BIN_LOG::write_binlog_and_commit_engine() is invoked where the trx should not attempt any committing. But that's happened. The binlog "engine" managed to commit_low() in conditions of the reported case which led to the assert few instructions later #7 0x00007fad151dbe42 in __GI___assert_fail (assertion=0x1e37d16 "is_started()", file=0x1e37c68 #8 0x0000000000fafcf7 in Ha_trx_info::next (this=0x7fac8c002028) at #9 0x0000000000f9dfa9 in ha_prepare (thd=0x7fac8c000bb0) More analysis proved there's an "inverted" related issue in the rollback branch. When the pure non-transactional engine xa transaction is rolled back, this time, it misses to execute rollback_low() method to leave a screwed state which the following query discovers hiting against an assert. bool trans_commit_stmt(THD*): Assertion `thd->in_active_multi_stmt_transaction() || thd->m_transaction_psi == __null' failed. And finally, testing revealed a case of no test coverage so far in combination of XA ROLLBACK, no transactional tables involved and no XA PREPARE. In such case logging was just incorrect mixing XA START and ROLLBACK. The original issue is fixed with making MYSQL_BIN_LOG::write_binlog_and_commit_engine() to compute a local boolean flag `skip-commit' correctly based on the value of the XA state. Commit is disallowed when the state is Prepared. To satisfy to the ONE-phase XA, the committing XA is also made to receive XA_PREPARED status, as intermediate, right after the prepare phase is done. in a general commit handler of ha_commit_trans(). The second issue of the rollback part is fixed with relocating an existing explicit rollback_low() for xa-rollback to a safer point. And the final third issue is fixed with augmentment of ending_trans()'s the trans_cannot_safely_rollback(thd) branch to compose an appropriate Query-log-event. Logics of preventing second time do_binlog_xa_commit_rollback() invocation that is actual to the "externally" committing XA is simplified. The former idea was in that the first invocation of do_binlog_xa_commit_rollback() in the "external" XA commit branch would necessarily turn the cache from empty, asserted, to not empty. On the other hand, at running do_binlog_xa_commit_rollback() for the local xa the cache must be empty (because it should've been flushed at prepare), asserted in the rollback case too. Hence the state of the cache checking was correct: (the local xa go through, the external xa goes through once which is first time). Now instead of the above deduction the 1st invocation is just gets explicitly flagged. And because we would like to preserve signature of MYSQL_BIN_LOG class methods the flag is made to pass as a new member of `bool binlog_cache_mngr::has_logged_xid' Mixing transactional and non-transactional tables in rpl.rpl_xa_survive_disconnect_mixed_engines reveal one issue in MTS grouping. An XA transaction "prepare" group can be closed with XA-ROLLBACK query which was previously missed to capture. A use case for that is mixed transactional and non-transactional updates. It's been corrected now. As a side effect is_loggable_xa_prepare() had to be refined to satisfy @c simulate_commit_failure.
1 parent 4b8bf3b commit 9b5f1b8

File tree

7 files changed

+313
-110
lines changed

7 files changed

+313
-110
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#
2+
# The test file is invoked from rpl.rpl_xa_survive_disconnect_mixed_engines
3+
#
4+
# The test file is orginized as three sections: setup, run and cleanup.
5+
# The main logics is resided in the run section which generates
6+
# three types of XA transaction: two kinds of mixed and one on non-transactional
7+
# table.
8+
#
9+
# param $command one of three of: 'setup', 'run' or 'cleanup'
10+
# param $xa_terminate how to conclude: 'XA COMMIT' or 'XA ROLLBACK'
11+
# param $one_phase 'one_phase' can be opted with XA COMMIT above
12+
# param $xa_prepare_opt '1' or empty can be opted to test with and without XA PREPARE
13+
# param $xid arbitrary name for xa trx, defaults to 'xa_trx'
14+
# Note '' is merely to underline, not a part of the value.
15+
#
16+
17+
if ($command == setup)
18+
{
19+
# Test randomizes the following variable's value:
20+
SET @@session.binlog_direct_non_transactional_updates := if(floor(rand()*10)%2,'ON','OFF');
21+
CREATE TABLE t (a INT) ENGINE=innodb;
22+
CREATE TABLE tm (a INT) ENGINE=myisam;
23+
}
24+
if (!$xid)
25+
{
26+
--let $xid=xa_trx
27+
}
28+
if ($command == run)
29+
{
30+
# Non transactional table goes first
31+
32+
--eval XA START '$xid'
33+
INSERT INTO tm VALUES (1);
34+
INSERT INTO t VALUES (1);
35+
--eval XA END '$xid'
36+
if ($xa_prepare_opt)
37+
{
38+
--eval XA PREPARE '$xid'
39+
}
40+
--eval $xa_terminate '$xid' $one_phase
41+
42+
# Transactional table goes first
43+
44+
--eval XA START '$xid'
45+
INSERT INTO t VALUES (2);
46+
--disable_warnings
47+
INSERT INTO tm VALUES (2);
48+
--enable_warnings
49+
--eval XA END '$xid'
50+
if ($xa_prepare_opt)
51+
{
52+
--eval XA PREPARE '$xid'
53+
}
54+
--eval $xa_terminate '$xid' $one_phase
55+
56+
# The pure non-transactional table
57+
58+
--eval XA START '$xid'
59+
--disable_warnings
60+
INSERT INTO tm VALUES (3);
61+
--enable_warnings
62+
--eval XA END '$xid'
63+
if ($xa_prepare_opt)
64+
{
65+
--eval XA PREPARE '$xid'
66+
}
67+
--eval $xa_terminate '$xid' $one_phase
68+
}
69+
70+
if ($command == cleanup)
71+
{
72+
DROP TABLE t, tm;
73+
}

mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_mixed_engines.result

Lines changed: 79 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,101 @@ Warnings:
33
Note #### Sending passwords in plain text without SSL/TLS is extremely insecure.
44
Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
55
[connection master]
6+
[connection slave]
7+
CALL mtr.add_suppression("An unexpected event sequence was detected by the IO thread while queuing the event");
8+
CALL mtr.add_suppression("GTID_LOG_EVENT or ANONYMOUS_GTID_LOG_EVENT is not expected in an event stream in the middle of a DML");
9+
[connection master]
610
CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
711
SET @@session.binlog_direct_non_transactional_updates := if(floor(rand()*10)%2,'ON','OFF');
812
CREATE TABLE t (a INT) ENGINE=innodb;
913
CREATE TABLE tm (a INT) ENGINE=myisam;
10-
XA START '1';
14+
=== COMMIT ===
15+
XA START 'xa_trx';
16+
INSERT INTO tm VALUES (1);
17+
INSERT INTO t VALUES (1);
18+
XA END 'xa_trx';
19+
XA PREPARE 'xa_trx';
20+
XA COMMIT 'xa_trx' ;
21+
XA START 'xa_trx';
22+
INSERT INTO t VALUES (2);
23+
INSERT INTO tm VALUES (2);
24+
XA END 'xa_trx';
25+
XA PREPARE 'xa_trx';
26+
XA COMMIT 'xa_trx' ;
27+
XA START 'xa_trx';
28+
INSERT INTO tm VALUES (3);
29+
XA END 'xa_trx';
30+
XA PREPARE 'xa_trx';
31+
XA COMMIT 'xa_trx' ;
32+
include/sync_slave_sql_with_master.inc
33+
[connection master]
34+
=== COMMIT ONE PHASE ===
35+
XA START 'xa_trx';
1136
INSERT INTO tm VALUES (1);
1237
INSERT INTO t VALUES (1);
13-
XA END '1';
14-
XA PREPARE '1';
15-
XA COMMIT '1';
16-
XA START '2';
38+
XA END 'xa_trx';
39+
XA COMMIT 'xa_trx' ONE PHASE;
40+
XA START 'xa_trx';
1741
INSERT INTO t VALUES (2);
1842
INSERT INTO tm VALUES (2);
43+
XA END 'xa_trx';
44+
XA COMMIT 'xa_trx' ONE PHASE;
45+
XA START 'xa_trx';
46+
INSERT INTO tm VALUES (3);
47+
XA END 'xa_trx';
48+
XA COMMIT 'xa_trx' ONE PHASE;
49+
include/sync_slave_sql_with_master.inc
50+
[connection master]
51+
=== ROLLBACK with PREPARE ===
52+
XA START 'xa_trx';
53+
INSERT INTO tm VALUES (1);
54+
INSERT INTO t VALUES (1);
55+
XA END 'xa_trx';
56+
XA PREPARE 'xa_trx';
57+
xa rollback 'xa_trx' ;
58+
Warnings:
59+
Warning 1196 Some non-transactional changed tables couldn't be rolled back
60+
XA START 'xa_trx';
1961
INSERT INTO t VALUES (2);
20-
XA END '2';
21-
XA PREPARE '2';
22-
XA COMMIT '2';
23-
XA START '3';
62+
INSERT INTO tm VALUES (2);
63+
XA END 'xa_trx';
64+
XA PREPARE 'xa_trx';
65+
xa rollback 'xa_trx' ;
66+
Warnings:
67+
Warning 1196 Some non-transactional changed tables couldn't be rolled back
68+
XA START 'xa_trx';
2469
INSERT INTO tm VALUES (3);
25-
INSERT INTO t VALUES (3);
26-
XA END '3';
27-
XA PREPARE '3';
28-
XA ROLLBACK '3';
70+
XA END 'xa_trx';
71+
XA PREPARE 'xa_trx';
72+
xa rollback 'xa_trx' ;
2973
Warnings:
3074
Warning 1196 Some non-transactional changed tables couldn't be rolled back
31-
XA START '4';
32-
INSERT INTO t VALUES (4);
33-
INSERT INTO tm VALUES (4);
34-
INSERT INTO t VALUES (4);
35-
XA END '4';
36-
XA PREPARE '4';
37-
XA ROLLBACK '4';
75+
include/sync_slave_sql_with_master.inc
76+
[connection master]
77+
=== ROLLBACK with no PREPARE ===
78+
XA START 'xa_trx';
79+
INSERT INTO tm VALUES (1);
80+
INSERT INTO t VALUES (1);
81+
XA END 'xa_trx';
82+
xa rollback 'xa_trx' ;
83+
Warnings:
84+
Warning 1196 Some non-transactional changed tables couldn't be rolled back
85+
XA START 'xa_trx';
86+
INSERT INTO t VALUES (2);
87+
INSERT INTO tm VALUES (2);
88+
XA END 'xa_trx';
89+
xa rollback 'xa_trx' ;
90+
Warnings:
91+
Warning 1196 Some non-transactional changed tables couldn't be rolled back
92+
XA START 'xa_trx';
93+
INSERT INTO tm VALUES (3);
94+
XA END 'xa_trx';
95+
xa rollback 'xa_trx' ;
3896
Warnings:
3997
Warning 1196 Some non-transactional changed tables couldn't be rolled back
4098
include/sync_slave_sql_with_master.inc
4199
include/diff_tables.inc [master:tm, slave:tm]
42-
DELETE FROM t;
100+
[connection master]
43101
DROP TABLE t, tm;
44102
include/sync_slave_sql_with_master.inc
45103
include/rpl_end.inc

mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_mixed_engines.test

Lines changed: 52 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,78 +2,71 @@
22
#
33
# The test verifies correct XA transaction two phase logging and its applying
44
# in a case the transaction updates transactional and non-transactional tables.
5+
# Transactions are terminated according to specfied parameters to
6+
# a sourced inc-file.
57

68
--source include/not_gtid_enabled.inc
79
--source include/master-slave.inc
810

9-
--connection master
11+
--source include/rpl_connection_slave.inc
12+
#Bug #21273010 SLAVE GROUP EVENT PARSER DOES NOT RECOGNIZE XA ROLLBACK AS A BOUNDARY
13+
CALL mtr.add_suppression("An unexpected event sequence was detected by the IO thread while queuing the event");
14+
CALL mtr.add_suppression("GTID_LOG_EVENT or ANONYMOUS_GTID_LOG_EVENT is not expected in an event stream in the middle of a DML");
15+
16+
--source include/rpl_connection_master.inc
1017
CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
1118

12-
# Test randomizes the following variable's value:
13-
SET @@session.binlog_direct_non_transactional_updates := if(floor(rand()*10)%2,'ON','OFF');
14-
CREATE TABLE t (a INT) ENGINE=innodb;
15-
16-
# memorize for the following show
17-
--let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1)
18-
--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
19-
20-
CREATE TABLE tm (a INT) ENGINE=myisam;
21-
22-
# case A: COMMIT
23-
24-
# Non transactional table goes first
25-
XA START '1';
26-
INSERT INTO tm VALUES (1);
27-
INSERT INTO t VALUES (1);
28-
XA END '1';
29-
XA PREPARE '1';
30-
XA COMMIT '1';
31-
32-
# Transactional table goes first
33-
XA START '2';
34-
INSERT INTO t VALUES (2);
35-
--disable_warnings
36-
INSERT INTO tm VALUES (2);
37-
--enable_warnings
38-
INSERT INTO t VALUES (2);
39-
XA END '2';
40-
XA PREPARE '2';
41-
XA COMMIT '2';
42-
43-
# case B: ROLLBACK
44-
45-
# Non transactional table goes first
46-
XA START '3';
47-
--disable_warnings
48-
INSERT INTO tm VALUES (3);
49-
--enable_warnings
50-
INSERT INTO t VALUES (3);
51-
XA END '3';
52-
XA PREPARE '3';
53-
XA ROLLBACK '3';
54-
55-
# Transactional table goes first
56-
XA START '4';
57-
INSERT INTO t VALUES (4);
58-
--disable_warnings
59-
INSERT INTO tm VALUES (4);
60-
--enable_warnings
61-
INSERT INTO t VALUES (4);
62-
XA END '4';
63-
XA PREPARE '4';
64-
XA ROLLBACK '4';
19+
--let $command=setup
20+
--source extra/rpl_tests/rpl_xa_mixed_engines.inc
21+
22+
--echo === COMMIT ===
23+
--let $command=run
24+
--let $xa_terminate=XA COMMIT
25+
--let $xa_prepare_opt=1
26+
--source extra/rpl_tests/rpl_xa_mixed_engines.inc
27+
28+
--source include/sync_slave_sql_with_master.inc
29+
--source include/rpl_connection_master.inc
30+
31+
--echo === COMMIT ONE PHASE ===
32+
33+
--let $command=run
34+
--let $xa_terminate=XA COMMIT
35+
--let $one_phase=ONE PHASE
36+
--let $xa_prepare_opt=
37+
--source extra/rpl_tests/rpl_xa_mixed_engines.inc
38+
--let $one_phase=
39+
--source include/sync_slave_sql_with_master.inc
40+
--source include/rpl_connection_master.inc
41+
42+
--echo === ROLLBACK with PREPARE ===
43+
44+
--let $command=run
45+
--let $xa_terminate=xa rollback
46+
--let $xa_prepare_opt=1
47+
--source extra/rpl_tests/rpl_xa_mixed_engines.inc
48+
49+
--source include/sync_slave_sql_with_master.inc
50+
--source include/rpl_connection_master.inc
51+
52+
--echo === ROLLBACK with no PREPARE ===
53+
54+
--let $command=run
55+
--let $xa_terminate=xa rollback
56+
--let $xa_prepare_opt=
57+
--source extra/rpl_tests/rpl_xa_mixed_engines.inc
58+
--let $xa_rollback_only=
6559

6660
--source include/sync_slave_sql_with_master.inc
6761

6862
--let $diff_tables= master:tm, slave:tm
6963
--source include/diff_tables.inc
7064

71-
# cleanup
72-
73-
--connection master
65+
# Cleanup
7466

75-
DELETE FROM t;
76-
DROP TABLE t, tm;
67+
--source include/rpl_connection_master.inc
68+
--let $command=cleanup
69+
--source extra/rpl_tests/rpl_xa_mixed_engines.inc
7770

7871
--source include/sync_slave_sql_with_master.inc
7972

0 commit comments

Comments
 (0)