Skip to content

Commit f57b647

Browse files
author
Debarun Banerjee
committed
BUG#23021168 REPLICATION STOPS AFTER TRX IS ROLLED BACK ASYNC
Problem : --------- After a transaction trx1 is asynchronously rolled back by a "high priority" transaction trx2, any statements executed on trx1 gets deadlock error ER_LOCK_DEADLOCK. This is currently true also for rollback statement i.e. ROLLBACK also returns ER_LOCK_DEADLOCK. This causes issue with replication. After ER_LOCK_DEADLOCK is returned while executing a statement, server attempts complete rollback of the transaction with each storage engine. In above scenario, Innodb returns ER_LOCK_DEADLOCK again when innobase_rollback is called. The error on rollback causes MYSQL_BIN_LOG::rollback to skip some clean up operation. As a result, the statements for the rolled back transaction are also logged in binary log and replayed in slave. For a table with unique index, the slave receives two entries with duplicate values(one of which is actually rolled back in master) causing "duplicate key" error and replication stops. Solution : ---------- 1. innobase_rollback to return SUCCESS if transaction is rolled back 2. innobase_rollback to ensure that the transaction is completely rolled back before returning. 3. other internal calls to innobase_rollback() that expects abort error, to return DB_FORCED_ABORT explicitly. Reviewed-by: Sunny Bains <[email protected]> RB: 12278
1 parent ae6ab7b commit f57b647

File tree

6 files changed

+118
-34
lines changed

6 files changed

+118
-34
lines changed

mysql-test/suite/innodb/r/high_prio_trx_7.result

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,5 +205,4 @@ UPDATE t1 SET col2 = 20 where col1 > 10 and col1 <30 and col2 = 30;
205205
COMMIT WORK;
206206
#connection 0
207207
ROLLBACK WORK;
208-
ERROR HY000: Got error 149 during ROLLBACK
209208
DROP TABLE t1;

mysql-test/suite/innodb/r/high_prio_trx_rpl.result

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,37 @@ include/sync_slave_sql_with_master.inc
2828
SELECT * FROM t1;
2929
c1
3030
2
31+
#
32+
# Bug#23021168 REPLICATION STOPS AFTER TRANSACTION IS ROLLED BACK
33+
# ASYNCHRONOUSLY IN MASTER
34+
#
35+
36+
# On connection master
37+
START TRANSACTION;
38+
INSERT INTO t1 VALUES (1);
39+
40+
# On connection con1
41+
include/start_transaction_high_prio.inc
42+
START TRANSACTION /* HIGH PRIORITY */;
43+
INSERT INTO t1 VALUES (1);
44+
COMMIT;
45+
SELECT * FROM t1 ORDER BY c1;
46+
c1
47+
1
48+
2
49+
50+
# On connection master
51+
INSERT INTO t1 VALUES (3);
52+
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
53+
INSERT INTO t1 VALUES (4);
54+
COMMIT;
55+
include/sync_slave_sql_with_master.inc
56+
# Rows 1, 2, 3 & 4 is expected on slave.
57+
SELECT * FROM t1 ORDER BY c1;
58+
c1
59+
1
60+
2
61+
4
3162

3263
# On connection master
3364
DROP TABLE t1;

mysql-test/suite/innodb/t/high_prio_trx_7.test

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,6 @@ COMMIT WORK;
440440

441441
--echo #connection 0
442442
--connection default
443-
--error ER_ERROR_DURING_ROLLBACK
444443
ROLLBACK WORK;
445444
DROP TABLE t1;
446445

mysql-test/suite/innodb/t/high_prio_trx_rpl.test

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ UPDATE t1 SET c1=1 WHERE c1=0;
2727
--source include/start_transaction_high_prio.inc
2828
UPDATE t1 SET c1=2 WHERE c1=0;
2929
COMMIT;
30-
--disconnect con1
3130

3231
--echo
3332
--echo # On connection master
@@ -43,6 +42,39 @@ SELECT * FROM t1;
4342
--echo # Row with value 2 is expected on slave.
4443
SELECT * FROM t1;
4544

45+
--echo #
46+
--echo # Bug#23021168 REPLICATION STOPS AFTER TRANSACTION IS ROLLED BACK
47+
--echo # ASYNCHRONOUSLY IN MASTER
48+
--echo #
49+
50+
--echo
51+
--echo # On connection master
52+
--connection master
53+
START TRANSACTION;
54+
INSERT INTO t1 VALUES (1);
55+
56+
--echo
57+
--echo # On connection con1
58+
--connection con1
59+
--source include/start_transaction_high_prio.inc
60+
INSERT INTO t1 VALUES (1);
61+
COMMIT;
62+
SELECT * FROM t1 ORDER BY c1;
63+
64+
--echo
65+
--echo # On connection master
66+
--connection master
67+
--error ER_LOCK_DEADLOCK
68+
INSERT INTO t1 VALUES (3); ## This will hit ER_LOCK_DEADLOCK
69+
INSERT INTO t1 VALUES (4); ## This will succeed.
70+
COMMIT;
71+
72+
--source include/sync_slave_sql_with_master.inc
73+
-- connection slave
74+
--echo # Rows 1, 2, 3 & 4 is expected on slave.
75+
SELECT * FROM t1 ORDER BY c1;
76+
77+
--disconnect con1
4678
--echo
4779
--echo # On connection master
4880
-- connection master

storage/innobase/handler/ha_innodb.cc

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4239,7 +4239,10 @@ innobase_commit(
42394239

42404240
if (trx_in_innodb.is_aborted()) {
42414241

4242-
DBUG_RETURN(innobase_rollback(hton, thd, commit_trx));
4242+
innobase_rollback(hton, thd, commit_trx);
4243+
4244+
DBUG_RETURN(convert_error_code_to_mysql(
4245+
DB_FORCED_ABORT, 0, thd));
42434246
}
42444247

42454248
ut_ad(trx->dict_operation_lock_mode == 0);
@@ -4418,24 +4421,18 @@ innobase_rollback(
44184421
error = trx_rollback_for_mysql(trx);
44194422

44204423
if (trx->state == TRX_STATE_FORCED_ROLLBACK) {
4421-
4424+
#ifdef UNIV_DEBUG
44224425
char buffer[1024];
44234426

44244427
ib::info() << "Forced rollback : "
44254428
<< thd_security_context(thd, buffer,
44264429
sizeof(buffer), 512);
4427-
4428-
error = DB_FORCED_ABORT;
4429-
4430+
#endif /* UNIV_DEBUG */
44304431
trx->state = TRX_STATE_NOT_STARTED;
44314432
}
44324433

44334434
trx_deregister_from_2pc(trx);
44344435

4435-
} else if (trx_in_innodb.is_aborted()) {
4436-
4437-
error = DB_FORCED_ABORT;
4438-
44394436
} else {
44404437

44414438
error = trx_rollback_last_sql_stat_for_mysql(trx);
@@ -7344,7 +7341,10 @@ ha_innobase::write_row(
73447341
if (!dict_table_is_intrinsic(m_prebuilt->table)
73457342
&& trx_in_innodb.is_aborted()) {
73467343

7347-
DBUG_RETURN(innobase_rollback(ht, m_user_thd, false));
7344+
innobase_rollback(ht, m_user_thd, false);
7345+
7346+
DBUG_RETURN(convert_error_code_to_mysql(
7347+
DB_FORCED_ABORT, 0, m_user_thd));
73487348
}
73497349

73507350
/* Step-1: Validation checks before we commence write_row operation. */
@@ -8115,7 +8115,10 @@ ha_innobase::update_row(
81158115
if (!dict_table_is_intrinsic(m_prebuilt->table)
81168116
&& TrxInInnoDB::is_aborted(trx)) {
81178117

8118-
DBUG_RETURN(innobase_rollback(ht, m_user_thd, false));
8118+
innobase_rollback(ht, m_user_thd, false);
8119+
8120+
DBUG_RETURN(convert_error_code_to_mysql(
8121+
DB_FORCED_ABORT, 0, m_user_thd));
81198122
}
81208123

81218124
/* This is not a delete */
@@ -8209,7 +8212,10 @@ ha_innobase::delete_row(
82098212
if (!dict_table_is_intrinsic(m_prebuilt->table)
82108213
&& trx_in_innodb.is_aborted()) {
82118214

8212-
DBUG_RETURN(innobase_rollback(ht, m_user_thd, false));
8215+
innobase_rollback(ht, m_user_thd, false);
8216+
8217+
DBUG_RETURN(convert_error_code_to_mysql(
8218+
DB_FORCED_ABORT, 0, m_user_thd));
82138219
}
82148220

82158221
ut_a(m_prebuilt->trx == trx);
@@ -8602,8 +8608,10 @@ ha_innobase::index_read(
86028608

86038609
if (TrxInInnoDB::is_aborted(m_prebuilt->trx)) {
86048610

8605-
DBUG_RETURN(innobase_rollback(
8606-
ht, m_user_thd, false));
8611+
innobase_rollback(ht, m_user_thd, false);
8612+
8613+
DBUG_RETURN(convert_error_code_to_mysql(
8614+
DB_FORCED_ABORT, 0, m_user_thd));
86078615
}
86088616

86098617
m_prebuilt->ins_sel_stmt = thd_is_ins_sel_stmt(
@@ -8775,7 +8783,10 @@ ha_innobase::change_active_index(
87758783
if (!dict_table_is_intrinsic(m_prebuilt->table)
87768784
&& trx_in_innodb.is_aborted()) {
87778785

8778-
DBUG_RETURN(innobase_rollback(ht, m_user_thd, false));
8786+
innobase_rollback(ht, m_user_thd, false);
8787+
8788+
DBUG_RETURN(convert_error_code_to_mysql(
8789+
DB_FORCED_ABORT, 0, m_user_thd));
87798790
}
87808791

87818792
active_index = keynr;
@@ -8893,7 +8904,10 @@ ha_innobase::general_fetch(
88938904

88948905
if (!intrinsic && TrxInInnoDB::is_aborted(trx)) {
88958906

8896-
DBUG_RETURN(innobase_rollback(ht, m_user_thd, false));
8907+
innobase_rollback(ht, m_user_thd, false);
8908+
8909+
DBUG_RETURN(convert_error_code_to_mysql(
8910+
DB_FORCED_ABORT, 0, m_user_thd));
88978911
}
88988912

88998913
innobase_srv_conc_enter_innodb(m_prebuilt);
@@ -9254,9 +9268,13 @@ ha_innobase::ft_init_ext(
92549268

92559269
if (trx_in_innodb.is_aborted()) {
92569270

9257-
int ret = innobase_rollback(ht, m_user_thd, false);
9271+
innobase_rollback(ht, m_user_thd, false);
9272+
9273+
int err;
9274+
err = convert_error_code_to_mysql(
9275+
DB_FORCED_ABORT, 0, m_user_thd);
92589276

9259-
my_error(ret, MYF(0));
9277+
my_error(err, MYF(0));
92609278

92619279
return(NULL);
92629280
}
@@ -9401,7 +9419,10 @@ ha_innobase::ft_read(
94019419

94029420
if (trx_in_innodb.is_aborted()) {
94039421

9404-
return(innobase_rollback(ht, m_user_thd, false));
9422+
innobase_rollback(ht, m_user_thd, false);
9423+
9424+
return(convert_error_code_to_mysql(
9425+
DB_FORCED_ABORT, 0, m_user_thd));
94059426
}
94069427

94079428
row_prebuilt_t* ft_prebuilt;
@@ -12092,7 +12113,10 @@ ha_innobase::discard_or_import_tablespace(
1209212113

1209312114
if (trx_in_innodb.is_aborted()) {
1209412115

12095-
DBUG_RETURN(innobase_rollback(ht, m_user_thd, false));
12116+
innobase_rollback(ht, m_user_thd, false);
12117+
12118+
DBUG_RETURN(convert_error_code_to_mysql(
12119+
DB_FORCED_ABORT, 0, m_user_thd));
1209612120
}
1209712121

1209812122
trx_start_if_not_started(m_prebuilt->trx, true);
@@ -16744,7 +16768,10 @@ innobase_xa_prepare(
1674416768

1674516769
if (trx_in_innodb.is_aborted()) {
1674616770

16747-
return(innobase_rollback(hton, thd, prepare_trx));
16771+
innobase_rollback(hton, thd, prepare_trx);
16772+
16773+
return(convert_error_code_to_mysql(
16774+
DB_FORCED_ABORT, 0, thd));
1674816775
}
1674916776

1675016777
if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
@@ -16766,7 +16793,11 @@ innobase_xa_prepare(
1676616793
ut_ad(err == DB_SUCCESS || err == DB_FORCED_ABORT);
1676716794

1676816795
if (err == DB_FORCED_ABORT) {
16769-
return(innobase_rollback(hton, thd, prepare_trx));
16796+
16797+
innobase_rollback(hton, thd, prepare_trx);
16798+
16799+
return(convert_error_code_to_mysql(
16800+
DB_FORCED_ABORT, 0, thd));
1677016801
}
1677116802

1677216803
} else {

storage/innobase/trx/trx0roll.cc

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,7 @@ trx_rollback_low(
204204
case TRX_STATE_NOT_STARTED:
205205
trx->will_lock = 0;
206206
ut_ad(trx->in_mysql_trx_list);
207-
return(trx->state == TRX_STATE_NOT_STARTED
208-
? DB_SUCCESS : DB_FORCED_ABORT);
207+
return(DB_SUCCESS);
209208

210209
case TRX_STATE_ACTIVE:
211210
ut_ad(trx->in_mysql_trx_list);
@@ -287,11 +286,6 @@ trx_rollback_for_mysql(
287286

288287
TrxInInnoDB trx_in_innodb(trx, true);
289288

290-
if (trx_in_innodb.is_aborted()) {
291-
292-
return(DB_FORCED_ABORT);
293-
}
294-
295289
return(trx_rollback_low(trx));
296290
}
297291
}
@@ -314,8 +308,6 @@ trx_rollback_last_sql_stat_for_mysql(
314308

315309
switch (trx->state) {
316310
case TRX_STATE_FORCED_ROLLBACK:
317-
return(DB_FORCED_ABORT);
318-
319311
case TRX_STATE_NOT_STARTED:
320312
return(DB_SUCCESS);
321313

0 commit comments

Comments
 (0)