Skip to content

Commit a66f7d7

Browse files
committed
Bug#32416819 ASSERTION "UT_LIST_GET_LEN.TRX_SYS->MYSQL_TRX_LIST. == 0"
ON SHUTDOWN Note: This commit in 8.0 is different from 8.4 and trunk due to testcase Background: ----------- The trx_sys->mysql_trx_list maintains a list of trx_t objects created for user transactions. When the `USE <database>` statement is run, a trx_t object is created for the THD running this query and added to this list. When the connection is closed, the trx_t object of the THD is freed and removed from this list. During shutdown, all the connections are closed before trx_sys is closed. When closing trx_sys, it is expected that all trx_t objects in trx_sys->mysql_trx_list are freed and the list is empty. In replication, when the applier sees an `XA START`, it detaches the THD from the handlertons by storing a backup in the engine's ha_data. In case of InnoDB, the trx_t created for the THD is stored in ha_data and is made nullptr; but not removed from the mysql_trx_list. In replication, the detached engine ha_data is reattached after the XA transaction is processed. During the reattach, the trx_t is restored from the ha_data back to the THD. By design, the detach happens when `XA START` is encountered by the applier. When the applier sees an `XA PREPARE`, the reattach is done via `applier_reset_xa_trans` calling `attach_native_trx`. When the applier sees an `XA COMMIT .. ONE PHASE`, the reattach is done in `ha_commit_low`. If the server is shutdown, the applier performs the reattach in `ha_rollback_low`. The binlog applier can be simulated by enabling `pseudo_replica_mode` and running a `BINLOG '0'`. Issue: ------ When the XA transaction is empty, the ha_list is nullptr. The reattach logic is performed only when ha_list is non-empty. Hence, if the server is shutdown after the applier detaches the engine (i.e, after running an `XA START`), the applier would never reattach the ha_data which leads to the assert when InnoDB closes trx_sys. Due to the design, the problem is seen only with the reattach in `ha_commit_low` and `ha_rollback_low`. Fix: ---- Ensured that reattach is done even if ha_list is empty. Change-Id: Ida617d067d112914c9d55ef4cde9f91956d19a08
1 parent 6d5b1f8 commit a66f7d7

File tree

4 files changed

+684
-38
lines changed

4 files changed

+684
-38
lines changed
+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# This test checks the behavior of applier thread when shutdown is issued at
2+
# various stages of the XA transaction. The session thread is marked as an
3+
# applier thread by setting the pseudo_replica_mode and executing BINLOG''
4+
# query.
5+
#
6+
# References:
7+
# Bug#32416819: ASSERTION "UT_LIST_GET_LEN.TRX_SYS->MYSQL_TRX_LIST. == 0" ON
8+
# SHUTDOWN
9+
#
10+
# The include file lists the scenarios to be checked before running shutdown.
11+
# Enable the following flags to check different scenarios:
12+
# $create_table: Create the table for the test and drop it at the end
13+
# $insert_table: Insert 3 rows into the table containing single int col
14+
# $need_xa_end: Includes the XA END statement
15+
# $need_xa_commit_one: Includes the XA COMMIT..ONE PHASE statement followed by
16+
# the previous set of statements
17+
# $need_xa_prepare: Includes the XA PREPARE statement
18+
# $need_xa_commit: Includes the XA COMMIT statement
19+
# $need_xa_rollback: Includes the XA ROLLBACK statement
20+
# $restart_connection: If set, create new connection and restart the server
21+
# using the new connection
22+
# If unset, restart the server on the connection that
23+
# executes the transaction
24+
#
25+
# Note:
26+
# During shutdown, Innodb closes trx_sys. At this stage, it is expected that all
27+
# the trx_t's created for mysql is freed (mysql_trx_list - list of trx_t created
28+
# for mysql is empty). Whenever a `USE <database>` command is issued, Innodb
29+
# will create a trx_t (associated with the corresponding session THD) and adds
30+
# it to mysql_trx_list.
31+
#
32+
# During replication, if the replication applier sees an 'XA START', it will
33+
# call detach_native_trx on all handlertons. This function calls
34+
# replace_native_transaction_in_thd by passing nullptr as the new value and
35+
# ha_ptr_backup as the buffer to store the handlerton's THD context. In case of
36+
# InnoDB, the THD context is the trx_t mentioned above.
37+
#
38+
# This context is restored at 3 different places depending on the XA transaction
39+
# 1. In 'XA PREPARE' using attach_native_trx
40+
# 2. In 'XA COMMIT .. ONE PHASE' using ha_commit_low
41+
# 3. In 'XA ROLLBACK' or shutdown using ha_rollback_low
42+
#
43+
# In the bug scenario, the restore was done only when the ha_list was non-empty.
44+
# Hence, if shutdown is issued in middle of processing an empty XA transaction
45+
# (right before XA PREPARE / XA COMMIT .. ONE PHASE), then the reattach step is
46+
# skipped. Thus the trx_t is never returned, leaving the mysql_trx_list
47+
# non-empty when closing trx_sys leading to the assert.
48+
#
49+
# In case of empty transaction, 'XA COMMIT .. ONE PHASE' does not call reattach.
50+
# Hence an 'XA START' after this will attempt to detach again, leading to an
51+
# assertion in the detach function.
52+
#
53+
# The current connection can simulate a replication applier by issuing:
54+
# SET @@SESSION.pseudo_replica_mode=1;
55+
# BINLOG '0';
56+
#
57+
# The BINLOG statement will initialize thd->rli_fake, even though the statement
58+
# fails. The thd->rli_fake will simulate the replication applier. '0' is used
59+
# for better readability.
60+
61+
if ($create_table) {
62+
CREATE TABLE t1 (c1 INT);
63+
INSERT INTO t1 VALUES (10);
64+
}
65+
66+
SET @@SESSION.pseudo_replica_mode=1;
67+
--error ER_BASE64_DECODE_ERROR
68+
BINLOG '0';
69+
70+
XA START 'test0';
71+
72+
if ($insert_table) {
73+
INSERT INTO t1 VALUES (1);
74+
INSERT INTO t1 VALUES (2);
75+
INSERT INTO t1 VALUES (3);
76+
}
77+
78+
if ($need_xa_end) {
79+
XA END 'test0';
80+
}
81+
82+
if ($need_xa_commit_one) {
83+
XA COMMIT 'test0' ONE PHASE;
84+
85+
XA START 'test0';
86+
if ($insert_table) {
87+
INSERT INTO t1 VALUES (4);
88+
INSERT INTO t1 VALUES (5);
89+
INSERT INTO t1 VALUES (6);
90+
}
91+
92+
if ($need_xa_end) {
93+
XA END 'test0';
94+
}
95+
}
96+
97+
if ($need_xa_prepare) {
98+
XA PREPARE 'test0';
99+
}
100+
101+
XA RECOVER;
102+
103+
if ($restart_connection) {
104+
# When restarting from a different connection, default connection must be
105+
# handled separately. In this case, the default connection simply waits until
106+
# it can reconnect after the server starts.
107+
108+
--connection default
109+
--disable_reconnect
110+
111+
--connect ($restart_connection, localhost, root,,)
112+
--connection $restart_connection
113+
--source include/restart_mysqld.inc
114+
--disconnect $restart_connection
115+
116+
--connection default
117+
--enable_reconnect
118+
--source include/wait_until_connected_again.inc
119+
}
120+
121+
if (!$restart_connection) {
122+
# The `connection default` is used to indicate that restart is issued in the
123+
# current connection
124+
125+
--connection default
126+
--source include/restart_mysqld.inc
127+
--connection default
128+
}
129+
130+
XA RECOVER;
131+
132+
if ($need_xa_rollback) {
133+
XA ROLLBACK 'test0';
134+
}
135+
136+
if ($need_xa_commit) {
137+
XA COMMIT 'test0';
138+
}
139+
140+
if ($create_table) {
141+
SELECT * FROM t1 ORDER BY c1;
142+
DROP TABLE t1;
143+
}

0 commit comments

Comments
 (0)