Skip to content

Commit 23f9e34

Browse files
committed
MDEV-32444 Data from orphaned XA transaction is lost after online alter
XA support for online alter was totally missing. Tying on binlog_hton made this hardly visible: simply having binlog_commit called from xa_commit made an impression that it will automagically work for online alter, which turns out wrong: all binlog does is writes "XA END" into trx cache and flushes it to a real binlog. In comparison, online alter can't do the same, since online replication happens in a single transaction. Solution: make a dedicated XA support. * Extend struct xid_t with a pointer to Online_alter_cache_list * On prepare: move online alter cache from THD::ha_data to XID passed * On XA commit/rollback: use the online alter cache stored in this XID. This makes us pass xid_cache_element->xid to xa_commit/xa_rollback instead of lex->xid * Use manual memory management for online alter cache list, instead of mem_root allocation, since we don't have mem_root connected to the XA transaction.
1 parent a569515 commit 23f9e34

File tree

8 files changed

+337
-22
lines changed

8 files changed

+337
-22
lines changed

mysql-test/main/alter_table_online_debug.result

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,6 +1563,116 @@ connection default;
15631563
drop table t1, t2;
15641564
set @@binlog_format=default;
15651565
set debug_sync= reset;
1566+
# MDEV-32444 Data from orphaned XA transaction is lost after online alter
1567+
create table t (a int primary key) engine=innodb;
1568+
insert into t values (1);
1569+
# XA commit
1570+
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
1571+
alter table t force, algorithm=copy, lock=none;
1572+
connection con1;
1573+
set debug_sync= 'now wait_for downgraded';
1574+
xa begin 'x1';
1575+
update t set a = 2 where a = 1;
1576+
xa end 'x1';
1577+
xa prepare 'x1';
1578+
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
1579+
disconnect con1;
1580+
connection con2;
1581+
set debug_sync= 'now wait_for xa_detach';
1582+
xa commit 'x1';
1583+
set debug_sync= 'now signal go';
1584+
connection default;
1585+
select * from t;
1586+
a
1587+
2
1588+
# XA rollback
1589+
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
1590+
alter table t force, algorithm=copy, lock=none;
1591+
connect con1, localhost, root,,;
1592+
set debug_sync= 'now wait_for downgraded';
1593+
xa begin 'x2';
1594+
insert into t values (53);
1595+
xa end 'x2';
1596+
xa prepare 'x2';
1597+
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
1598+
disconnect con1;
1599+
connection con2;
1600+
set debug_sync= 'now wait_for xa_detach';
1601+
xa rollback 'x2';
1602+
set debug_sync= 'now signal go';
1603+
connection default;
1604+
select * from t;
1605+
a
1606+
2
1607+
# XA transaction is left uncommitted
1608+
# end then is rollbacked after alter fails
1609+
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
1610+
set statement innodb_lock_wait_timeout=0, lock_wait_timeout= 0
1611+
for alter table t force, algorithm=copy, lock=none;
1612+
connect con1, localhost, root,,;
1613+
set debug_sync= 'now wait_for downgraded';
1614+
xa begin 'xuncommitted';
1615+
insert into t values (3);
1616+
xa end 'xuncommitted';
1617+
xa prepare 'xuncommitted';
1618+
set debug_sync= 'now signal go';
1619+
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
1620+
disconnect con1;
1621+
connection default;
1622+
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
1623+
set debug_sync= 'now wait_for xa_detach';
1624+
xa rollback 'xuncommitted';
1625+
select * from t;
1626+
a
1627+
2
1628+
# Same, but commit
1629+
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
1630+
set statement innodb_lock_wait_timeout=0, lock_wait_timeout= 0
1631+
for alter table t force, algorithm=copy, lock=none;
1632+
connect con1, localhost, root,,;
1633+
set debug_sync= 'now wait_for downgraded';
1634+
xa begin 'committed_later';
1635+
insert into t values (3);
1636+
xa end 'committed_later';
1637+
xa prepare 'committed_later';
1638+
set debug_sync= 'now signal go';
1639+
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
1640+
disconnect con1;
1641+
connection default;
1642+
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
1643+
set debug_sync= 'now wait_for xa_detach';
1644+
xa commit 'committed_later';
1645+
select * from t;
1646+
a
1647+
2
1648+
3
1649+
# Commit, but error in statement, and there is some stmt data to rollback
1650+
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
1651+
alter table t force, algorithm=copy, lock=none;
1652+
connect con1, localhost, root,,;
1653+
set debug_sync= 'now wait_for downgraded';
1654+
xa begin 'x1';
1655+
insert into t values (4), (3);
1656+
ERROR 23000: Duplicate entry '3' for key 'PRIMARY'
1657+
insert into t values (5);
1658+
xa end 'x1';
1659+
xa prepare 'x1';
1660+
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
1661+
disconnect con1;
1662+
connection con2;
1663+
set debug_sync= 'now wait_for xa_detach';
1664+
xa commit 'x1';
1665+
set debug_sync= 'now signal go';
1666+
connection default;
1667+
select * from t;
1668+
a
1669+
2
1670+
3
1671+
5
1672+
connect con1, localhost, root,,;
1673+
connection default;
1674+
drop table t;
1675+
set debug_sync= reset;
15661676
disconnect con1;
15671677
disconnect con2;
15681678
#

mysql-test/main/alter_table_online_debug.test

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1791,6 +1791,135 @@ set @@binlog_format=default;
17911791
set debug_sync= reset;
17921792

17931793

1794+
--echo # MDEV-32444 Data from orphaned XA transaction is lost after online alter
1795+
1796+
create table t (a int primary key) engine=innodb;
1797+
insert into t values (1);
1798+
1799+
--echo # XA commit
1800+
1801+
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
1802+
send alter table t force, algorithm=copy, lock=none;
1803+
1804+
--connection con1
1805+
set debug_sync= 'now wait_for downgraded';
1806+
xa begin 'x1';
1807+
update t set a = 2 where a = 1;
1808+
xa end 'x1';
1809+
xa prepare 'x1';
1810+
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
1811+
--disconnect con1
1812+
1813+
--connection con2
1814+
set debug_sync= 'now wait_for xa_detach';
1815+
xa commit 'x1';
1816+
set debug_sync= 'now signal go';
1817+
--connection default
1818+
--reap # alter table
1819+
1820+
select * from t;
1821+
1822+
--echo # XA rollback
1823+
1824+
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
1825+
send alter table t force, algorithm=copy, lock=none;
1826+
--connect(con1, localhost, root,,)
1827+
set debug_sync= 'now wait_for downgraded';
1828+
xa begin 'x2';
1829+
insert into t values (53);
1830+
xa end 'x2';
1831+
xa prepare 'x2';
1832+
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
1833+
--disconnect con1
1834+
1835+
--connection con2
1836+
set debug_sync= 'now wait_for xa_detach';
1837+
xa rollback 'x2';
1838+
set debug_sync= 'now signal go';
1839+
--connection default
1840+
--reap # alter table
1841+
1842+
select * from t;
1843+
1844+
--echo # XA transaction is left uncommitted
1845+
--echo # end then is rollbacked after alter fails
1846+
1847+
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
1848+
send set statement innodb_lock_wait_timeout=0, lock_wait_timeout= 0
1849+
for alter table t force, algorithm=copy, lock=none;
1850+
1851+
--connect(con1, localhost, root,,)
1852+
set debug_sync= 'now wait_for downgraded';
1853+
xa begin 'xuncommitted';
1854+
insert into t values (3);
1855+
xa end 'xuncommitted';
1856+
xa prepare 'xuncommitted';
1857+
set debug_sync= 'now signal go';
1858+
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
1859+
--disconnect con1
1860+
1861+
--connection default
1862+
--error ER_LOCK_WAIT_TIMEOUT
1863+
--reap # alter table
1864+
set debug_sync= 'now wait_for xa_detach';
1865+
xa rollback 'xuncommitted';
1866+
1867+
select * from t;
1868+
1869+
--echo # Same, but commit
1870+
1871+
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
1872+
send set statement innodb_lock_wait_timeout=0, lock_wait_timeout= 0
1873+
for alter table t force, algorithm=copy, lock=none;
1874+
1875+
--connect(con1, localhost, root,,)
1876+
set debug_sync= 'now wait_for downgraded';
1877+
xa begin 'committed_later';
1878+
insert into t values (3);
1879+
xa end 'committed_later';
1880+
xa prepare 'committed_later';
1881+
set debug_sync= 'now signal go';
1882+
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
1883+
--disconnect con1
1884+
1885+
--connection default
1886+
--error ER_LOCK_WAIT_TIMEOUT
1887+
--reap # alter table
1888+
set debug_sync= 'now wait_for xa_detach';
1889+
xa commit 'committed_later';
1890+
1891+
1892+
select * from t;
1893+
1894+
--echo # Commit, but error in statement, and there is some stmt data to rollback
1895+
1896+
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
1897+
send alter table t force, algorithm=copy, lock=none;
1898+
--connect(con1, localhost, root,,)
1899+
set debug_sync= 'now wait_for downgraded';
1900+
xa begin 'x1';
1901+
--error ER_DUP_ENTRY
1902+
insert into t values (4), (3);
1903+
insert into t values (5);
1904+
xa end 'x1';
1905+
xa prepare 'x1';
1906+
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
1907+
--disconnect con1
1908+
1909+
--connection con2
1910+
set debug_sync= 'now wait_for xa_detach';
1911+
xa commit 'x1';
1912+
set debug_sync= 'now signal go';
1913+
--connection default
1914+
--reap # alter table
1915+
1916+
select * from t;
1917+
1918+
--connect(con1, localhost, root,,)
1919+
--connection default
1920+
drop table t;
1921+
set debug_sync= reset;
1922+
17941923
--disconnect con1
17951924
--disconnect con2
17961925
--echo #

sql/handler.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2596,7 +2596,7 @@ static bool xarecover_decide_to_commit(xid_recovery_member* member,
25962596
static void xarecover_do_commit_or_rollback(handlerton *hton,
25972597
xarecover_complete_arg *arg)
25982598
{
2599-
xid_t x;
2599+
XA_data x;
26002600
my_bool rc;
26012601
xid_recovery_member *member= arg->member;
26022602
Binlog_offset *ptr_commit_max= arg->binlog_coord;
@@ -2605,7 +2605,7 @@ static void xarecover_do_commit_or_rollback(handlerton *hton,
26052605
// Populate xid using the server_id from original transaction
26062606
x.set(member->xid, member->server_id);
26072607
else
2608-
x= *member->full_xid;
2608+
(XID)x= *member->full_xid;
26092609

26102610
rc= xarecover_decide_to_commit(member, ptr_commit_max) ?
26112611
hton->commit_by_xid(hton, &x) : hton->rollback_by_xid(hton, &x);

sql/handler.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,7 @@ typedef ulonglong my_xid; // this line is the same as in log_event.h
890890
#define COMPATIBLE_DATA_YES 0
891891
#define COMPATIBLE_DATA_NO 1
892892

893+
893894
/**
894895
struct xid_t is binary compatible with the XID structure as
895896
in the X/Open CAE Specification, Distributed Transaction Processing:
@@ -973,6 +974,12 @@ struct xid_t {
973974
};
974975
typedef struct xid_t XID;
975976

977+
struct Online_alter_cache_list;
978+
struct XA_data: XID
979+
{
980+
Online_alter_cache_list *online_alter_cache= NULL;
981+
};
982+
976983
/*
977984
Enumerates a sequence in the order of
978985
their creation that is in the top-down order of the index file.

0 commit comments

Comments
 (0)