Skip to content

Commit 98fc5b3

Browse files
committed
MDEV-5262, MDEV-5914, MDEV-5941, MDEV-6020: Deadlocks during parallel replication causing replication to fail.
After-review changes. For this patch in 10.0, we do not introduce a new public storage engine API, we just fix the InnoDB/XtraDB issues. In 10.1, we will make a better public API that can be used for all storage engines (MDEV-6429). Eliminate the background thread that did deadlock kills asynchroneously. Instead, we ensure that the InnoDB/XtraDB code can handle doing the kill from inside the deadlock detection code (when thd_report_wait_for() needs to kill a later thread to resolve a deadlock). (We preserve the part of the original patch that introduces dedicated mutex and condition for the slave init thread, to remove the abuse of LOCK_thread_count for start/stop synchronisation of the slave init thread).
1 parent e5149fa commit 98fc5b3

File tree

25 files changed

+382
-265
lines changed

25 files changed

+382
-265
lines changed

include/mysql/plugin.h

Lines changed: 1 addition & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,7 @@ void **thd_ha_data(const MYSQL_THD thd, const struct handlerton *hton);
622622
void thd_storage_lock_wait(MYSQL_THD thd, long long value);
623623
int thd_tx_isolation(const MYSQL_THD thd);
624624
int thd_tx_is_read_only(const MYSQL_THD thd);
625+
int thd_rpl_is_parallel(const MYSQL_THD thd);
625626
/**
626627
Create a temporary file.
627628
@@ -729,80 +730,6 @@ void thd_set_ha_data(MYSQL_THD thd, const struct handlerton *hton,
729730
*/
730731
void thd_wakeup_subsequent_commits(MYSQL_THD thd, int wakeup_error);
731732

732-
/*
733-
Used by a storage engine to report that one transaction THD is about to
734-
go to wait for a transactional lock held by another transactions OTHER_THD.
735-
736-
This is used for parallel replication, where transactions are required to
737-
commit in the same order on the slave as they did on the master. If the
738-
transactions on the slave can encounter lock conflicts on the slave that did
739-
not exist on the master, this can cause deadlocks.
740-
741-
The storage engine can report such conflicting locks using this call. This
742-
will allow parallel replication to detect such conflicts and resolve the
743-
deadlock (by killing the second transaction to release the locks that the
744-
first is waiting for, and then later re-try the second killed transaction).
745-
746-
The storage engine should not report false positives. That is, it should not
747-
report any lock waits that do not actually require one transaction to wait
748-
for the other. Nor should it report waits for locks that will be released
749-
before the commit of the other transactions.
750-
*/
751-
void thd_report_wait_for(const MYSQL_THD thd, MYSQL_THD other_thd);
752-
753-
/*
754-
This function can optionally be called to check if thd_report_wait_for()
755-
needs to be called for waits done by a given transaction.
756-
757-
If this function returns false for a given thd, there is no need to do any
758-
calls to thd_report_wait_for() on that thd.
759-
760-
This call is optional; it is safe to call thd_report_wait_for() in any case.
761-
This call can be used to save some redundant calls to thd_report_wait_for()
762-
if desired. (This is unlikely to matter much unless there are _lots_ of
763-
waits to report, as the overhead of thd_report_wait_for() is small).
764-
*/
765-
int thd_need_wait_for(const MYSQL_THD thd);
766-
767-
/*
768-
This function can be called by storage engines to check if the commit order
769-
of two transactions has already been decided by the upper layer. This
770-
happens in parallel replication, where the commit order is forced to be the
771-
same on the slave as it was originally on the master.
772-
773-
If this function returns false, it means that such commit order will be
774-
enforced. This allows the storage engine to optionally omit gap lock waitss
775-
or similar measures that would otherwise be needed to ensure that
776-
transactions would be serialised in a way that would cause a commit order
777-
that is correct for binlogging for statement-based replication.
778-
779-
If this function returns true, normal locking should be done as required by
780-
the binlogging and transaction isolation level in effect.
781-
*/
782-
int thd_need_ordering_with(const MYSQL_THD thd, const MYSQL_THD other_thd);
783-
784-
/*
785-
If the storage engine detects a deadlock, and needs to choose a victim
786-
transaction to roll back, it can call this function to ask the upper
787-
server layer for which of two possible transactions is prefered to be
788-
aborted and rolled back.
789-
790-
In parallel replication, if two transactions are running in parallel and
791-
one is fixed to commit before the other, then the one that commits later
792-
will be prefered as the victim - chosing the early transaction as a victim
793-
will not resolve the deadlock anyway, as the later transaction still needs
794-
to wait for the earlier to commit.
795-
796-
Otherwise, a transaction that uses only transactional tables, and can thus
797-
be safely rolled back, will be prefered as a deadlock victim over a
798-
transaction that also modified non-transactional (eg. MyISAM) tables.
799-
800-
The return value is -1 if the first transaction is prefered as a deadlock
801-
victim, 1 if the second transaction is prefered, or 0 for no preference (in
802-
which case the storage engine can make the choice as it prefers).
803-
*/
804-
int thd_deadlock_victim_preference(const MYSQL_THD thd1, const MYSQL_THD thd2);
805-
806733
#ifdef __cplusplus
807734
}
808735
#endif

include/mysql/plugin_audit.h.pp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@
303303
void thd_storage_lock_wait(void* thd, long long value);
304304
int thd_tx_isolation(const void* thd);
305305
int thd_tx_is_read_only(const void* thd);
306+
int thd_rpl_is_parallel(const void* thd);
306307
int mysql_tmpfile(const char *prefix);
307308
unsigned long thd_get_thread_id(const void* thd);
308309
void thd_get_xid(const void* thd, MYSQL_XID *xid);
@@ -313,10 +314,6 @@
313314
void thd_set_ha_data(void* thd, const struct handlerton *hton,
314315
const void *ha_data);
315316
void thd_wakeup_subsequent_commits(void* thd, int wakeup_error);
316-
void thd_report_wait_for(const void* thd, void *other_thd);
317-
int thd_need_wait_for(const void* thd);
318-
int thd_need_ordering_with(const void* thd, const void* other_thd);
319-
int thd_deadlock_victim_preference(const void* thd1, const void* thd2);
320317
struct mysql_event_general
321318
{
322319
unsigned int event_subclass;

include/mysql/plugin_auth.h.pp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@
303303
void thd_storage_lock_wait(void* thd, long long value);
304304
int thd_tx_isolation(const void* thd);
305305
int thd_tx_is_read_only(const void* thd);
306+
int thd_rpl_is_parallel(const void* thd);
306307
int mysql_tmpfile(const char *prefix);
307308
unsigned long thd_get_thread_id(const void* thd);
308309
void thd_get_xid(const void* thd, MYSQL_XID *xid);
@@ -313,10 +314,6 @@
313314
void thd_set_ha_data(void* thd, const struct handlerton *hton,
314315
const void *ha_data);
315316
void thd_wakeup_subsequent_commits(void* thd, int wakeup_error);
316-
void thd_report_wait_for(const void* thd, void *other_thd);
317-
int thd_need_wait_for(const void* thd);
318-
int thd_need_ordering_with(const void* thd, const void* other_thd);
319-
int thd_deadlock_victim_preference(const void* thd1, const void* thd2);
320317
#include <mysql/plugin_auth_common.h>
321318
typedef struct st_plugin_vio_info
322319
{

include/mysql/plugin_ftparser.h.pp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@
256256
void thd_storage_lock_wait(void* thd, long long value);
257257
int thd_tx_isolation(const void* thd);
258258
int thd_tx_is_read_only(const void* thd);
259+
int thd_rpl_is_parallel(const void* thd);
259260
int mysql_tmpfile(const char *prefix);
260261
unsigned long thd_get_thread_id(const void* thd);
261262
void thd_get_xid(const void* thd, MYSQL_XID *xid);
@@ -266,10 +267,6 @@
266267
void thd_set_ha_data(void* thd, const struct handlerton *hton,
267268
const void *ha_data);
268269
void thd_wakeup_subsequent_commits(void* thd, int wakeup_error);
269-
void thd_report_wait_for(const void* thd, void *other_thd);
270-
int thd_need_wait_for(const void* thd);
271-
int thd_need_ordering_with(const void* thd, const void* other_thd);
272-
int thd_deadlock_victim_preference(const void* thd1, const void* thd2);
273270
enum enum_ftparser_mode
274271
{
275272
MYSQL_FTPARSER_SIMPLE_MODE= 0,

mysql-test/suite/perfschema/r/threads_mysql.result

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,6 @@ processlist_info NULL
4444
unified_parent_thread_id unified parent_thread_id
4545
role NULL
4646
instrumented YES
47-
name thread/sql/slave_background
48-
type BACKGROUND
49-
processlist_user NULL
50-
processlist_host NULL
51-
processlist_db NULL
52-
processlist_command NULL
53-
processlist_info NULL
54-
unified_parent_thread_id unified parent_thread_id
55-
role NULL
56-
instrumented YES
5747
CREATE TEMPORARY TABLE t1 AS
5848
SELECT thread_id FROM performance_schema.threads
5949
WHERE name LIKE 'thread/sql%';
@@ -115,5 +105,4 @@ parent_thread_name child_thread_name
115105
thread/sql/event_scheduler thread/sql/event_worker
116106
thread/sql/main thread/sql/one_connection
117107
thread/sql/main thread/sql/signal_handler
118-
thread/sql/main thread/sql/slave_background
119108
thread/sql/one_connection thread/sql/event_scheduler

sql/log.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4115,7 +4115,7 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
41154115
included= false;
41164116
break;
41174117
}
4118-
if (!included && 0 == strcmp(ir->name, rli->group_relay_log_name))
4118+
if (!included && !strcmp(ir->name, rli->group_relay_log_name))
41194119
break;
41204120
if (!next)
41214121
{
@@ -9369,7 +9369,7 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
93699369
file= -1;
93709370
}
93719371

9372-
if (0 == strcmp(linfo->log_file_name, last_log_name))
9372+
if (!strcmp(linfo->log_file_name, last_log_name))
93739373
break; // No more files to do
93749374
if ((file= open_binlog(&log, linfo->log_file_name, &errmsg)) < 0)
93759375
{

sql/log_event.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7328,6 +7328,13 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
73287328
uint64 sub_id= 0;
73297329
Relay_log_info const *rli= rgi->rli;
73307330

7331+
/*
7332+
XID_EVENT works like a COMMIT statement. And it also updates the
7333+
mysql.gtid_slave_pos table with the GTID of the current transaction.
7334+
7335+
Therefore, it acts much like a normal SQL statement, so we need to do
7336+
mysql_reset_thd_for_next_command() as if starting a new statement.
7337+
*/
73317338
mysql_reset_thd_for_next_command(thd);
73327339
/*
73337340
Record any GTID in the same transaction, so slave state is transactionally

sql/mysqld.cc

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ static I_List<THD> thread_cache;
368368
static bool binlog_format_used= false;
369369
LEX_STRING opt_init_connect, opt_init_slave;
370370
static mysql_cond_t COND_thread_cache, COND_flush_thread_cache;
371-
mysql_cond_t COND_slave_background;
371+
mysql_cond_t COND_slave_init;
372372
static DYNAMIC_ARRAY all_options;
373373

374374
/* Global variables */
@@ -707,7 +707,7 @@ mysql_mutex_t
707707
LOCK_crypt,
708708
LOCK_global_system_variables,
709709
LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
710-
LOCK_connection_count, LOCK_error_messages, LOCK_slave_background;
710+
LOCK_connection_count, LOCK_error_messages, LOCK_slave_init;
711711

712712
mysql_mutex_t LOCK_stats, LOCK_global_user_client_stats,
713713
LOCK_global_table_stats, LOCK_global_index_stats;
@@ -882,7 +882,7 @@ PSI_mutex_key key_LOCK_stats,
882882
PSI_mutex_key key_LOCK_gtid_waiting;
883883

884884
PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered,
885-
key_LOCK_slave_background;
885+
key_LOCK_slave_init;
886886
PSI_mutex_key key_TABLE_SHARE_LOCK_share;
887887

888888
static PSI_mutex_info all_server_mutexes[]=
@@ -945,7 +945,7 @@ static PSI_mutex_info all_server_mutexes[]=
945945
{ &key_LOCK_error_messages, "LOCK_error_messages", PSI_FLAG_GLOBAL},
946946
{ &key_LOCK_prepare_ordered, "LOCK_prepare_ordered", PSI_FLAG_GLOBAL},
947947
{ &key_LOCK_commit_ordered, "LOCK_commit_ordered", PSI_FLAG_GLOBAL},
948-
{ &key_LOCK_slave_background, "LOCK_slave_background", PSI_FLAG_GLOBAL},
948+
{ &key_LOCK_slave_init, "LOCK_slave_init", PSI_FLAG_GLOBAL},
949949
{ &key_LOG_INFO_lock, "LOG_INFO::lock", 0},
950950
{ &key_LOCK_thread_count, "LOCK_thread_count", PSI_FLAG_GLOBAL},
951951
{ &key_LOCK_thread_cache, "LOCK_thread_cache", PSI_FLAG_GLOBAL},
@@ -1000,7 +1000,7 @@ PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy;
10001000
PSI_cond_key key_COND_rpl_thread_queue, key_COND_rpl_thread,
10011001
key_COND_rpl_thread_pool,
10021002
key_COND_parallel_entry, key_COND_group_commit_orderer,
1003-
key_COND_prepare_ordered, key_COND_slave_background;
1003+
key_COND_prepare_ordered, key_COND_slave_init;
10041004
PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates;
10051005

10061006
static PSI_cond_info all_server_conds[]=
@@ -1049,15 +1049,15 @@ static PSI_cond_info all_server_conds[]=
10491049
{ &key_COND_parallel_entry, "COND_parallel_entry", 0},
10501050
{ &key_COND_group_commit_orderer, "COND_group_commit_orderer", 0},
10511051
{ &key_COND_prepare_ordered, "COND_prepare_ordered", 0},
1052-
{ &key_COND_slave_background, "COND_slave_background", 0},
1052+
{ &key_COND_slave_init, "COND_slave_init", 0},
10531053
{ &key_COND_wait_gtid, "COND_wait_gtid", 0},
10541054
{ &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0}
10551055
};
10561056

10571057
PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
10581058
key_thread_handle_manager, key_thread_main,
10591059
key_thread_one_connection, key_thread_signal_hand,
1060-
key_thread_slave_background, key_rpl_parallel_thread;
1060+
key_thread_slave_init, key_rpl_parallel_thread;
10611061

10621062
static PSI_thread_info all_server_threads[]=
10631063
{
@@ -1083,7 +1083,7 @@ static PSI_thread_info all_server_threads[]=
10831083
{ &key_thread_main, "main", PSI_FLAG_GLOBAL},
10841084
{ &key_thread_one_connection, "one_connection", 0},
10851085
{ &key_thread_signal_hand, "signal_handler", PSI_FLAG_GLOBAL},
1086-
{ &key_thread_slave_background, "slave_background", PSI_FLAG_GLOBAL},
1086+
{ &key_thread_slave_init, "slave_init", PSI_FLAG_GLOBAL},
10871087
{ &key_rpl_parallel_thread, "rpl_parallel_thread", 0}
10881088
};
10891089

@@ -2177,8 +2177,8 @@ static void clean_up_mutexes()
21772177
mysql_mutex_destroy(&LOCK_prepare_ordered);
21782178
mysql_cond_destroy(&COND_prepare_ordered);
21792179
mysql_mutex_destroy(&LOCK_commit_ordered);
2180-
mysql_mutex_destroy(&LOCK_slave_background);
2181-
mysql_cond_destroy(&COND_slave_background);
2180+
mysql_mutex_destroy(&LOCK_slave_init);
2181+
mysql_cond_destroy(&COND_slave_init);
21822182
DBUG_VOID_RETURN;
21832183
}
21842184

@@ -4393,9 +4393,9 @@ static int init_thread_environment()
43934393
mysql_cond_init(key_COND_prepare_ordered, &COND_prepare_ordered, NULL);
43944394
mysql_mutex_init(key_LOCK_commit_ordered, &LOCK_commit_ordered,
43954395
MY_MUTEX_INIT_SLOW);
4396-
mysql_mutex_init(key_LOCK_slave_background, &LOCK_slave_background,
4396+
mysql_mutex_init(key_LOCK_slave_init, &LOCK_slave_init,
43974397
MY_MUTEX_INIT_SLOW);
4398-
mysql_cond_init(key_COND_slave_background, &COND_slave_background, NULL);
4398+
mysql_cond_init(key_COND_slave_init, &COND_slave_init, NULL);
43994399

44004400
#ifdef HAVE_OPENSSL
44014401
mysql_mutex_init(key_LOCK_des_key_file,
@@ -9477,8 +9477,6 @@ PSI_stage_info stage_waiting_for_room_in_worker_thread= { 0, "Waiting for room i
94779477
PSI_stage_info stage_master_gtid_wait_primary= { 0, "Waiting in MASTER_GTID_WAIT() (primary waiter)", 0};
94789478
PSI_stage_info stage_master_gtid_wait= { 0, "Waiting in MASTER_GTID_WAIT()", 0};
94799479
PSI_stage_info stage_gtid_wait_other_connection= { 0, "Waiting for other master connection to process GTID received on multiple master connections", 0};
9480-
PSI_stage_info stage_slave_background_process_request= { 0, "Processing requests", 0};
9481-
PSI_stage_info stage_slave_background_wait_request= { 0, "Waiting for requests", 0};
94829480

94839481
#ifdef HAVE_PSI_INTERFACE
94849482

@@ -9602,9 +9600,7 @@ PSI_stage_info *all_server_stages[]=
96029600
& stage_waiting_to_get_readlock,
96039601
& stage_master_gtid_wait_primary,
96049602
& stage_master_gtid_wait,
9605-
& stage_gtid_wait_other_connection,
9606-
& stage_slave_background_process_request,
9607-
& stage_slave_background_wait_request
9603+
& stage_gtid_wait_other_connection
96089604
};
96099605

96109606
PSI_socket_key key_socket_tcpip, key_socket_unix, key_socket_client_connection;

sql/mysqld.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,8 @@ extern PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates;
309309

310310
extern PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
311311
key_thread_handle_manager, key_thread_kill_server, key_thread_main,
312-
key_thread_one_connection, key_thread_signal_hand,
313-
key_thread_slave_background, key_rpl_parallel_thread;
312+
key_thread_one_connection, key_thread_signal_hand, key_thread_slave_init,
313+
key_rpl_parallel_thread;
314314

315315
extern PSI_file_key key_file_binlog, key_file_binlog_index, key_file_casetest,
316316
key_file_dbopt, key_file_des_key_file, key_file_ERRMSG, key_select_to_file,
@@ -451,8 +451,6 @@ extern PSI_stage_info stage_waiting_for_room_in_worker_thread;
451451
extern PSI_stage_info stage_master_gtid_wait_primary;
452452
extern PSI_stage_info stage_master_gtid_wait;
453453
extern PSI_stage_info stage_gtid_wait_other_connection;
454-
extern PSI_stage_info stage_slave_background_process_request;
455-
extern PSI_stage_info stage_slave_background_wait_request;
456454

457455
#ifdef HAVE_PSI_STATEMENT_INTERFACE
458456
/**
@@ -521,7 +519,7 @@ extern mysql_mutex_t
521519
LOCK_slave_list, LOCK_active_mi, LOCK_manager,
522520
LOCK_global_system_variables, LOCK_user_conn,
523521
LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count,
524-
LOCK_slave_background;
522+
LOCK_slave_init;
525523
extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
526524
#ifdef HAVE_OPENSSL
527525
extern mysql_mutex_t LOCK_des_key_file;
@@ -532,7 +530,7 @@ extern mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
532530
extern mysql_rwlock_t LOCK_system_variables_hash;
533531
extern mysql_cond_t COND_thread_count;
534532
extern mysql_cond_t COND_manager;
535-
extern mysql_cond_t COND_slave_background;
533+
extern mysql_cond_t COND_slave_init;
536534
extern int32 thread_running;
537535
extern int32 thread_count;
538536
extern my_atomic_rwlock_t thread_running_lock, thread_count_lock;

sql/rpl_parallel.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,6 @@ convert_kill_to_deadlock_error(rpl_group_info *rgi)
240240
rgi->killed_for_retry)
241241
{
242242
thd->clear_error();
243-
thd->get_stmt_da()->reset_diagnostics_area();
244243
my_error(ER_LOCK_DEADLOCK, MYF(0));
245244
rgi->killed_for_retry= false;
246245
thd->reset_killed();
@@ -325,7 +324,7 @@ retry_event_group(rpl_group_info *rgi, rpl_parallel_thread *rpt,
325324
register_wait_for_prior_event_group_commit(rgi, entry);
326325
mysql_mutex_unlock(&entry->LOCK_parallel_entry);
327326

328-
strcpy(log_name, ir->name);
327+
strmake_buf(log_name, ir->name);
329328
if ((fd= open_binlog(&rlog, log_name, &errmsg)) <0)
330329
{
331330
err= 1;

0 commit comments

Comments
 (0)