Skip to content

Commit 830bdfc

Browse files
committed
MDEV-32126 Assertion fails upon online ALTER and binary log enabled
Assertion `!writer.checksum_len || writer.remains == 0' fails upon concurrent online ALTER and transactions with failing statements and binary log enabled. Also another assertion, `pos != (~(my_off_t) 0)', fails in my_seek, upon reinit_io_cache, on a simplified test. This means that IO_CACHE wasn't properly initialized, or had an error before. The overall problem is a deep interference with the effect of an installed binlog_hton: the assumption about that thd->binlog_get_cache_mngr() is, sufficiently, NULL, when we shouldn't run the binlog part of binlog_commit/binlog_rollback, is wrong: as turns out, sometimes the binlog handlerton can be not installed in current thd, but binlog_commit can be called on behalf of binlog, as in the bug reported. One separate condition found is XA recovery of the orphaned transaction, when binlog_commit is also called, but it has nothing to do with online alter. Solution: Extract online alter operations into a separate handlerton.
1 parent a1019e9 commit 830bdfc

File tree

6 files changed

+172
-17
lines changed

6 files changed

+172
-17
lines changed

mysql-test/main/alter_table_online_debug.result

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,6 +1518,51 @@ connection default;
15181518
drop table t1;
15191519
set debug_sync= reset;
15201520
drop table iso_levels;
1521+
# MDEV-32126 Assertion fails upon online ALTER and binary log enabled
1522+
create temporary table tmp (id int, primary key(id)) engine=innodb;
1523+
create table t1 (a int, b text) engine=innodb;
1524+
create table t2 (a int, b int, c char(8), d text, unique(a)) engine=innodb;
1525+
insert into t2 values (1,1,'f','e'),(1000,1000,'c','b');
1526+
connection default;
1527+
set debug_sync= 'alter_table_online_before_lock signal go_trx wait_for go_alter';
1528+
alter table t2 force, algorithm=copy, lock=none;
1529+
connection con2;
1530+
set debug_sync= 'now wait_for go_trx';
1531+
start transaction;
1532+
insert into t1 values (3,'a');
1533+
insert into t2 values (3,3,'a','x'), (3,3,'a','x');
1534+
ERROR 23000: Duplicate entry '3' for key 'a'
1535+
insert into t2 values (3,3,'a','x');
1536+
commit;
1537+
set debug_sync= 'now signal go_alter';
1538+
connection default;
1539+
truncate t2;
1540+
set @@binlog_format=mixed;
1541+
connection con2;
1542+
start transaction;
1543+
create temporary table tmp (id int, primary key(id)) engine=innodb;
1544+
insert into t1 values (1, repeat('x',8000)),(2, repeat('x',8000));
1545+
update t2 set b = null order by b limit 2;
1546+
insert into t1 values (3, repeat('x',8000));
1547+
delete from t1;
1548+
insert into t2 values (1,1,'f','e'),(1000,1000,'c','b');
1549+
commit;
1550+
connection default;
1551+
set debug_sync= 'alter_table_online_before_lock signal go_trx wait_for go_alter';
1552+
alter table t2 force, algorithm=copy, lock=none;
1553+
connection con2;
1554+
set debug_sync= 'now wait_for go_trx';
1555+
start transaction;
1556+
drop temporary table if exists tmp;
1557+
insert into t2 values (3,3,'a','x'), (3,3,'a','x');
1558+
ERROR 23000: Duplicate entry '3' for key 'a'
1559+
insert into t2 values (3,3,'a','x');
1560+
commit;
1561+
set debug_sync= 'now signal go_alter';
1562+
connection default;
1563+
drop table t1, t2;
1564+
set @@binlog_format=default;
1565+
set debug_sync= reset;
15211566
disconnect con1;
15221567
disconnect con2;
15231568
#

mysql-test/main/alter_table_online_debug.test

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,6 +1736,60 @@ set debug_sync= reset;
17361736
drop table iso_levels;
17371737

17381738

1739+
--echo # MDEV-32126 Assertion fails upon online ALTER and binary log enabled
1740+
create temporary table tmp (id int, primary key(id)) engine=innodb;
1741+
create table t1 (a int, b text) engine=innodb;
1742+
create table t2 (a int, b int, c char(8), d text, unique(a)) engine=innodb;
1743+
insert into t2 values (1,1,'f','e'),(1000,1000,'c','b');
1744+
--connection default
1745+
set debug_sync= 'alter_table_online_before_lock signal go_trx wait_for go_alter';
1746+
send alter table t2 force, algorithm=copy, lock=none;
1747+
1748+
--connection con2
1749+
set debug_sync= 'now wait_for go_trx';
1750+
start transaction;
1751+
insert into t1 values (3,'a');
1752+
--error ER_DUP_ENTRY
1753+
insert into t2 values (3,3,'a','x'), (3,3,'a','x');
1754+
insert into t2 values (3,3,'a','x');
1755+
commit;
1756+
set debug_sync= 'now signal go_alter';
1757+
--connection default
1758+
--reap
1759+
truncate t2;
1760+
set @@binlog_format=mixed;
1761+
--connection con2
1762+
start transaction;
1763+
create temporary table tmp (id int, primary key(id)) engine=innodb;
1764+
insert into t1 values (1, repeat('x',8000)),(2, repeat('x',8000));
1765+
update t2 set b = null order by b limit 2;
1766+
insert into t1 values (3, repeat('x',8000));
1767+
delete from t1;
1768+
insert into t2 values (1,1,'f','e'),(1000,1000,'c','b');
1769+
commit;
1770+
1771+
1772+
--connection default
1773+
set debug_sync= 'alter_table_online_before_lock signal go_trx wait_for go_alter';
1774+
send alter table t2 force, algorithm=copy, lock=none;
1775+
1776+
--connection con2
1777+
set debug_sync= 'now wait_for go_trx';
1778+
start transaction;
1779+
drop temporary table if exists tmp;
1780+
--error ER_DUP_ENTRY
1781+
insert into t2 values (3,3,'a','x'), (3,3,'a','x');
1782+
insert into t2 values (3,3,'a','x');
1783+
commit;
1784+
set debug_sync= 'now signal go_alter';
1785+
1786+
--connection default
1787+
--reap
1788+
1789+
drop table t1, t2;
1790+
set @@binlog_format=default;
1791+
set debug_sync= reset;
1792+
17391793

17401794
--disconnect con1
17411795
--disconnect con2

sql/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY
218218
RECOMPILE_FOR_EMBEDDED)
219219
MYSQL_ADD_PLUGIN(sql_sequence ha_sequence.cc STORAGE_ENGINE MANDATORY STATIC_ONLY
220220
RECOMPILE_FOR_EMBEDDED)
221+
MYSQL_ADD_PLUGIN(online_alter_log log.cc STORAGE_ENGINE MANDATORY STATIC_ONLY
222+
NOT_EMBEDDED)
221223

222224
ADD_LIBRARY(sql STATIC ${SQL_SOURCE})
223225
MAYBE_DISABLE_IPO(sql)

sql/handler.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,7 @@ enum legacy_db_type
557557
DB_TYPE_BLACKHOLE_DB=19,
558558
DB_TYPE_PARTITION_DB=20,
559559
DB_TYPE_BINLOG=21,
560+
DB_TYPE_ONLINE_ALTER=22,
560561
DB_TYPE_PBXT=23,
561562
DB_TYPE_PERFORMANCE_SCHEMA=28,
562563
DB_TYPE_S3=41,
@@ -1096,6 +1097,9 @@ enum ha_stat_type { HA_ENGINE_STATUS, HA_ENGINE_LOGS, HA_ENGINE_MUTEX };
10961097
extern MYSQL_PLUGIN_IMPORT st_plugin_int *hton2plugin[MAX_HA];
10971098

10981099
struct handlerton;
1100+
1101+
extern handlerton *online_alter_hton;
1102+
10991103
#define view_pseudo_hton ((handlerton *)1)
11001104

11011105
/*
@@ -1916,7 +1920,6 @@ struct THD_TRANS
19161920

19171921
};
19181922

1919-
19201923
/**
19211924
Either statement transaction or normal transaction - related
19221925
thread-specific storage engine data.
@@ -1969,7 +1972,9 @@ class Ha_trx_info
19691972
bool is_trx_read_write() const
19701973
{
19711974
DBUG_ASSERT(is_started());
1972-
return m_flags & (int) TRX_READ_WRITE;
1975+
bool result= m_flags & (int) TRX_READ_WRITE;
1976+
DBUG_ASSERT(!result || m_ht != online_alter_hton);
1977+
return result;
19731978
}
19741979
bool is_started() const { return m_ht != NULL; }
19751980
/** Mark this transaction read-write if the argument is read-write. */

sql/log.cc

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,7 +2185,6 @@ int binlog_rollback_by_xid(handlerton *hton, XID *xid)
21852185

21862186
DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_ROLLBACK ||
21872187
(thd->transaction->xid_state.get_state_code() == XA_ROLLBACK_ONLY));
2188-
21892188
rc= binlog_rollback(hton, thd, TRUE);
21902189
thd->ha_data[hton->slot].ha_info[1].reset();
21912190

@@ -2276,9 +2275,9 @@ int binlog_log_row_online_alter(TABLE* table, const uchar *before_record,
22762275
if (!table->online_alter_cache)
22772276
{
22782277
table->online_alter_cache= online_alter_binlog_get_cache_data(thd, table);
2279-
trans_register_ha(thd, false, binlog_hton, 0);
2278+
trans_register_ha(thd, false, online_alter_hton, 0);
22802279
if (thd->in_multi_stmt_transaction_mode())
2281-
trans_register_ha(thd, true, binlog_hton, 0);
2280+
trans_register_ha(thd, true, online_alter_hton, 0);
22822281
}
22832282

22842283
// We need to log all columns for the case if alter table changes primary key
@@ -2339,20 +2338,15 @@ int binlog_commit(THD *thd, bool all, bool ro_1pc)
23392338
PSI_stage_info org_stage;
23402339
DBUG_ENTER("binlog_commit");
23412340

2342-
IF_DBUG(bool commit_online= !thd->online_alter_cache_list.empty(),);
2343-
23442341
bool is_ending_transaction= ending_trans(thd, all);
2345-
error= binlog_online_alter_end_trans(thd, all, true);
2346-
if (error)
2347-
DBUG_RETURN(error);
23482342

23492343
binlog_cache_mngr *const cache_mngr= thd->binlog_get_cache_mngr();
23502344
/*
23512345
cache_mngr can be NULL in case if binlog logging is disabled.
23522346
*/
23532347
if (!cache_mngr)
23542348
{
2355-
DBUG_ASSERT(WSREP(thd) || commit_online ||
2349+
DBUG_ASSERT(WSREP(thd) ||
23562350
(thd->lex->sql_command != SQLCOM_XA_PREPARE &&
23572351
!(thd->lex->sql_command == SQLCOM_XA_COMMIT &&
23582352
thd->lex->xa_opt == XA_ONE_PHASE)));
@@ -2450,17 +2444,13 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
24502444
DBUG_ENTER("binlog_rollback");
24512445

24522446
bool is_ending_trans= ending_trans(thd, all);
2453-
2454-
bool rollback_online= !thd->online_alter_cache_list.empty();
2455-
if (rollback_online)
2456-
binlog_online_alter_end_trans(thd, all, 0);
24572447
int error= 0;
24582448
binlog_cache_mngr *const cache_mngr= thd->binlog_get_cache_mngr();
24592449

24602450
if (!cache_mngr)
24612451
{
2462-
DBUG_ASSERT(WSREP(thd) || rollback_online);
2463-
DBUG_ASSERT(thd->lex->sql_command != SQLCOM_XA_ROLLBACK || rollback_online);
2452+
DBUG_ASSERT(WSREP(thd));
2453+
DBUG_ASSERT(thd->lex->sql_command != SQLCOM_XA_ROLLBACK);
24642454

24652455
DBUG_RETURN(0);
24662456
}
@@ -12538,3 +12528,61 @@ void wsrep_register_binlog_handler(THD *thd, bool trx)
1253812528
}
1253912529

1254012530
#endif /* WITH_WSREP */
12531+
12532+
static int online_alter_close_connection(handlerton *hton, THD *thd)
12533+
{
12534+
DBUG_ASSERT(thd->online_alter_cache_list.empty());
12535+
return 0;
12536+
}
12537+
12538+
handlerton *online_alter_hton;
12539+
12540+
int online_alter_log_init(void *p)
12541+
{
12542+
online_alter_hton= (handlerton *)p;
12543+
online_alter_hton->db_type= DB_TYPE_ONLINE_ALTER;
12544+
online_alter_hton->savepoint_offset= sizeof(my_off_t);
12545+
online_alter_hton->close_connection= online_alter_close_connection;
12546+
12547+
online_alter_hton->savepoint_set= // Done by online_alter_savepoint_set
12548+
[](handlerton *, THD *, void *){ return 0; };
12549+
online_alter_hton->savepoint_rollback= // Done by online_alter_savepoint_rollback
12550+
[](handlerton *, THD *, void *){ return 0; };
12551+
online_alter_hton->savepoint_rollback_can_release_mdl=
12552+
[](handlerton *hton, THD *thd){ return true; };
12553+
12554+
online_alter_hton->commit= [](handlerton *, THD *thd, bool all)
12555+
{ return binlog_online_alter_end_trans(thd, all, true); };
12556+
online_alter_hton->rollback= [](handlerton *, THD *thd, bool all)
12557+
{ return binlog_online_alter_end_trans(thd, all, false); };
12558+
online_alter_hton->commit_by_xid= [](handlerton *hton, XID *xid)
12559+
{ return binlog_online_alter_end_trans(current_thd, true, true); };
12560+
online_alter_hton->rollback_by_xid= [](handlerton *hton, XID *xid)
12561+
{ return binlog_online_alter_end_trans(current_thd, true, false); };
12562+
12563+
online_alter_hton->drop_table= [](handlerton *, const char*) { return -1; };
12564+
online_alter_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN
12565+
| HTON_NO_ROLLBACK;
12566+
return 0;
12567+
}
12568+
12569+
struct st_mysql_storage_engine online_alter_storage_engine=
12570+
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
12571+
12572+
maria_declare_plugin(online_alter_log)
12573+
{
12574+
MYSQL_STORAGE_ENGINE_PLUGIN,
12575+
&online_alter_storage_engine,
12576+
"online_alter_log",
12577+
"MariaDB PLC",
12578+
"This is a pseudo storage engine to represent the online alter log in a transaction",
12579+
PLUGIN_LICENSE_GPL,
12580+
online_alter_log_init,
12581+
NULL,
12582+
0x0100, // 1.0
12583+
NULL, // no status vars
12584+
NULL, // no sysvars
12585+
"1.0",
12586+
MariaDB_PLUGIN_MATURITY_STABLE
12587+
}
12588+
maria_declare_plugin_end;

sql/log.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,7 @@ binlog_cache_data* binlog_get_cache_data(binlog_cache_mngr *cache_mngr,
13511351

13521352
extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log;
13531353
extern handlerton *binlog_hton;
1354+
extern handlerton *online_alter_hton;
13541355
extern LOGGER logger;
13551356

13561357
extern const char *log_bin_index;

0 commit comments

Comments
 (0)