Skip to content
/ server Public

Commit d613f03

Browse files
committed
MDEV-37541 Race of rolling back and committing transaction to binlog
Two transactions could binlog their completions in opposite to how it is done in Engine. That is is rare situations ROLLBACK in Engine of the dependency parent transaction could be scheduled by the transaction before its binlogging. That give a follower dependency child one get binlogged ahead of the parent. For fixing this bug its necessary to ensure the binlogging phase is always first one in the internal one-phase rollback protocol. The commit combines 1. a code polishing piece over a part of MDEV-21117 that made binlog handlerton always commit first in no-2pc cases and 2. the same rule now applies to the rollback. An added test demonstrates how the child could otherwise reach binlog before its parent.
1 parent 22db2df commit d613f03

File tree

4 files changed

+58
-25
lines changed

4 files changed

+58
-25
lines changed

mysql-test/suite/binlog/t/binlog_mdev37541.test

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22
--source include/have_innodb.inc
33
--source include/have_binlog_format_mixed.inc
44

5+
# MDEV-37541 Race of rolling back and committing transaction to binlog
6+
# The tests reproduce a scenario of two conflicting transactions
7+
# of which the dependency decides to rollback. In order to reproduce the bug
8+
# the parent features side effects (produced by DROP TEMPORARY TABLE)
9+
# and multiple engines.
10+
# The tests prove that the child transaction can not win race to binlogging
11+
# with its parent as it gets stuck at engine lock acquiring all time
12+
# while the parent conducts binlogging on its own.
13+
514
create table t1 (a int primary key, b text) engine=innodb;
615

716
connect(trx1_rollback,localhost,root,,);

sql/handler.cc

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2152,11 +2152,34 @@ static bool is_ro_1pc_trans(THD *thd, Ha_trx_info *ha_info, bool all,
21522152
return !rw_trans;
21532153
}
21542154

2155-
static bool has_binlog_hton(Ha_trx_info *ha_info)
2155+
inline Ha_trx_info* get_binlog_hton(Ha_trx_info *ha_info)
21562156
{
2157-
bool rc;
2158-
for (rc= false; ha_info && !rc; ha_info= ha_info->next())
2159-
rc= ha_info->ht() == binlog_hton;
2157+
for (; ha_info; ha_info= ha_info->next())
2158+
if (ha_info->ht() == binlog_hton)
2159+
return ha_info;
2160+
2161+
return ha_info;
2162+
}
2163+
2164+
static int run_binlog_first(THD *thd, bool all, THD_TRANS *trans,
2165+
bool is_real_trans, bool is_commit)
2166+
{
2167+
int rc= 0;
2168+
Ha_trx_info *ha_info= trans->ha_list;
2169+
2170+
if ((ha_info= get_binlog_hton(ha_info)))
2171+
{
2172+
int err;
2173+
if ((err= is_commit ? binlog_commit(thd, all,
2174+
is_ro_1pc_trans(thd, ha_info, all,
2175+
is_real_trans))
2176+
: binlog_rollback(ha_info->ht(), thd, all)))
2177+
{
2178+
my_error(is_commit ? ER_ERROR_DURING_COMMIT : ER_ERROR_DURING_ROLLBACK,
2179+
MYF(0), err);
2180+
rc= 1;
2181+
}
2182+
}
21602183

21612184
return rc;
21622185
}
@@ -2174,18 +2197,19 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
21742197
if (ha_info)
21752198
{
21762199
int err= 0;
2177-
2178-
if (has_binlog_hton(ha_info) &&
2179-
(err= binlog_commit(thd, all,
2180-
is_ro_1pc_trans(thd, ha_info, all, is_real_trans))))
2200+
/*
2201+
Binlog hton must be called first regardless of its position
2202+
in trans->ha_list at least to prevent from commiting any engine
2203+
branches when afterward a duplicate GTID error out of binlog_commit()
2204+
is generated.
2205+
*/
2206+
for (int binlog_err= error=
2207+
run_binlog_first(thd, all, trans, is_real_trans, true);
2208+
ha_info; ha_info= ha_info_next)
21812209
{
2182-
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
2183-
error= 1;
2210+
if (binlog_err)
2211+
goto err;
21842212

2185-
goto err;
2186-
}
2187-
for (; ha_info; ha_info= ha_info_next)
2188-
{
21892213
handlerton *ht= ha_info->ht();
21902214
if ((err= ht->commit(ht, thd, all)))
21912215
{
@@ -2311,17 +2335,17 @@ int ha_rollback_trans(THD *thd, bool all)
23112335
if (is_real_trans) /* not a statement commit */
23122336
thd->stmt_map.close_transient_cursors();
23132337

2314-
int err;
2315-
if (has_binlog_hton(ha_info) &&
2316-
(err= binlog_hton->rollback(binlog_hton, thd, all)))
2317-
{
2318-
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
2319-
error= 1;
2320-
}
2321-
for (; ha_info; ha_info= ha_info_next)
2338+
/*
2339+
Binlog hton must be called first regardless of its position
2340+
in trans->ha_list in order to avoid race to binlog between the current
2341+
rollbacker and any transaction that depends on it. This guarantees
2342+
the execution time dependency identifies binlog ordering.
2343+
*/
2344+
for (error= run_binlog_first(thd, all, trans, is_real_trans, false);
2345+
ha_info; ha_info= ha_info_next)
23222346
{
2347+
int err;
23232348
handlerton *ht= ha_info->ht();
2324-
23252349
if (ht != binlog_hton && (err= ht->rollback(ht, thd, all)))
23262350
{
23272351
// cannot happen

sql/log.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv);
9797
static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv);
9898
static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton,
9999
THD *thd);
100-
static int binlog_rollback(handlerton *hton, THD *thd, bool all);
101100
static int binlog_prepare(handlerton *hton, THD *thd, bool all);
102101
static int binlog_start_consistent_snapshot(handlerton *hton, THD *thd);
103102
static int binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr,
@@ -2489,7 +2488,7 @@ int binlog_commit(THD *thd, bool all, bool ro_1pc)
24892488
24902489
@see handlerton::rollback
24912490
*/
2492-
static int binlog_rollback(handlerton *hton, THD *thd, bool all)
2491+
int binlog_rollback(handlerton *hton, THD *thd, bool all)
24932492
{
24942493
DBUG_ENTER("binlog_rollback");
24952494

sql/log.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,7 @@ const char *
12771277
get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list);
12781278

12791279
int binlog_commit(THD *thd, bool all, bool is_ro_1pc= false);
1280+
int binlog_rollback(handlerton *hton, THD *thd, bool all);
12801281
int binlog_commit_by_xid(handlerton *hton, XID *xid);
12811282
int binlog_rollback_by_xid(handlerton *hton, XID *xid);
12821283
bool write_bin_log_start_alter(THD *thd, bool& partial_alter,

0 commit comments

Comments
 (0)