summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorKristian Nielsen <knielsen@knielsen-hq.org>2014-08-19 14:26:42 +0200
committerKristian Nielsen <knielsen@knielsen-hq.org>2014-08-19 14:26:42 +0200
commit453c29c3f772d7bec69be2a2bf5a5747444f0a77 (patch)
tree2c05e85227ab49bffb2bbf8f2cf621b12a09bed7 /sql
parent4cb1e0eea0c965df3e91c1637f17127c1e7d6db7 (diff)
downloadmariadb-git-453c29c3f772d7bec69be2a2bf5a5747444f0a77.tar.gz
MDEV-6321: close_temporary_tables() in format description event not serialised correctly
Follow-up patch, fixing a possible deadlock issue. If the master crashes in the middle of an event group, there can be an active transaction in a worker thread when we encounter the following master restart format description event. In this case, we need to notify that worker thread to abort and roll back the partial event group. Otherwise a deadlock occurs: the worker thread waits for the commit that never arrives, and the SQL driver thread waits for the worker thread to complete its event group, which it never does.
Diffstat (limited to 'sql')
-rw-r--r--sql/rpl_parallel.cc81
-rw-r--r--sql/rpl_parallel.h13
2 files changed, 90 insertions, 4 deletions
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index 9ba155bebb4..21234d7fd38 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -587,6 +587,30 @@ handle_rpl_parallel_thread(void *arg)
events= next;
continue;
}
+ else if (events->typ ==
+ rpl_parallel_thread::queued_event::QUEUED_MASTER_RESTART)
+ {
+ if (in_event_group)
+ {
+ /*
+ Master restarted (crashed) in the middle of an event group.
+ So we need to roll back and discard that event group.
+ */
+ group_rgi->cleanup_context(thd, 1);
+ in_event_group= false;
+ finish_event_group(thd, group_rgi->gtid_sub_id,
+ events->entry_for_queued, group_rgi);
+
+ group_rgi->next= rgis_to_free;
+ rgis_to_free= group_rgi;
+ thd->rgi_slave= group_rgi= NULL;
+ }
+
+ events->next= qevs_to_free;
+ qevs_to_free= events;
+ events= next;
+ continue;
+ }
DBUG_ASSERT(events->typ==rpl_parallel_thread::queued_event::QUEUED_EVENT);
thd->rgi_slave= group_rgi= rgi;
@@ -1617,6 +1641,54 @@ rpl_parallel::workers_idle()
}
+int
+rpl_parallel_entry::queue_master_restart(rpl_group_info *rgi,
+ Format_description_log_event *fdev)
+{
+ uint32 idx;
+ rpl_parallel_thread *thr;
+ rpl_parallel_thread::queued_event *qev;
+ Relay_log_info *rli= rgi->rli;
+
+ /*
+ We only need to queue the server restart if we still have a thread working
+ on a (potentially partial) event group.
+
+ If the last thread we queued for has finished, then it cannot have any
+ partial event group that needs aborting.
+
+ Thus there is no need for the full complexity of choose_thread(). We only
+ need to check if we have a current worker thread, and queue for it if so.
+ */
+ idx= rpl_thread_idx;
+ thr= rpl_threads[idx];
+ if (!thr)
+ return 0;
+ mysql_mutex_lock(&thr->LOCK_rpl_thread);
+ if (thr->current_owner != &rpl_threads[idx])
+ {
+ /* No active worker thread, so no need to queue the master restart. */
+ mysql_mutex_unlock(&thr->LOCK_rpl_thread);
+ return 0;
+ }
+
+ if (!(qev= thr->get_qev(fdev, 0, rli)))
+ {
+ mysql_mutex_unlock(&thr->LOCK_rpl_thread);
+ return 1;
+ }
+
+ qev->rgi= rgi;
+ qev->typ= rpl_parallel_thread::queued_event::QUEUED_MASTER_RESTART;
+ qev->entry_for_queued= this;
+ qev->ir= rli->last_inuse_relaylog;
+ ++qev->ir->queued_count;
+ thr->enqueue(qev);
+ mysql_mutex_unlock(&thr->LOCK_rpl_thread);
+ return 0;
+}
+
+
void
rpl_parallel::wait_for_workers_idle(THD *thd)
{
@@ -1727,7 +1799,16 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
Thus we need to wait for all prior events to execute to completion,
in case they need access to any of the temporary tables.
+
+ We also need to notify the worker thread running the prior incomplete
+ event group (if any), as such event group signifies an incompletely
+ written group cut short by a master crash, and must be rolled back.
*/
+ if (current->queue_master_restart(serial_rgi, fdev))
+ {
+ delete ev;
+ return 1;
+ }
wait_for_workers_idle(rli->sql_driver_thd);
}
}
diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h
index d59205cc72a..befe08b3d9b 100644
--- a/sql/rpl_parallel.h
+++ b/sql/rpl_parallel.h
@@ -76,10 +76,15 @@ struct rpl_parallel_thread {
queued_event can hold either an event to be executed, or just a binlog
position to be updated without any associated event.
*/
- enum queued_event_t { QUEUED_EVENT, QUEUED_POS_UPDATE } typ;
+ enum queued_event_t {
+ QUEUED_EVENT,
+ QUEUED_POS_UPDATE,
+ QUEUED_MASTER_RESTART
+ } typ;
union {
Log_event *ev; /* QUEUED_EVENT */
- rpl_parallel_entry *entry_for_queued; /* QUEUED_POS_UPDATE */
+ rpl_parallel_entry *entry_for_queued; /* QUEUED_POS_UPDATE and
+ QUEUED_MASTER_RESTART */
};
rpl_group_info *rgi;
inuse_relaylog *ir;
@@ -224,8 +229,8 @@ struct rpl_parallel_entry {
rpl_parallel_thread * choose_thread(rpl_group_info *rgi, bool *did_enter_cond,
PSI_stage_info *old_stage, bool reuse);
- group_commit_orderer *get_gco();
- void free_gco(group_commit_orderer *gco);
+ int queue_master_restart(rpl_group_info *rgi,
+ Format_description_log_event *fdev);
};
struct rpl_parallel {
HASH domain_hash;