mysql5/mysql-5.7.27/sql/rpl_slave_commit_order_manager.cc

167 lines
4.8 KiB
C++

/* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "rpl_slave_commit_order_manager.h"
#include "rpl_rli_pdb.h" // Slave_worker
#include "debug_sync.h" // debug_sync_set_action
Commit_order_manager::Commit_order_manager(uint32 worker_numbers)
: m_rollback_trx(false), m_workers(worker_numbers), queue_head(QUEUE_EOF),
queue_tail(QUEUE_EOF)
{
mysql_mutex_init(key_commit_order_manager_mutex, &m_mutex, NULL);
for (uint32 i= 0; i < worker_numbers; i++)
{
mysql_cond_init(key_commit_order_manager_cond, &m_workers[i].cond);
m_workers[i].status= OCS_FINISH;
}
}
Commit_order_manager::~Commit_order_manager()
{
mysql_mutex_destroy(&m_mutex);
for (uint32 i= 0; i < m_workers.size(); i++)
{
mysql_cond_destroy(&m_workers[i].cond);
}
}
void Commit_order_manager::register_trx(Slave_worker *worker)
{
DBUG_ENTER("Commit_order_manager::register_trx");
mysql_mutex_lock(&m_mutex);
m_workers[worker->id].status= OCS_WAIT;
queue_push(worker->id);
mysql_mutex_unlock(&m_mutex);
DBUG_VOID_RETURN;
}
/**
Waits until it becomes the queue head.
@retval false All previous threads succeeded so this thread can go
ahead and commit.
*/
bool Commit_order_manager::wait_for_its_turn(Slave_worker *worker,
bool all)
{
DBUG_ENTER("Commit_order_manager::wait_for_its_turn");
/*
When prior transaction fail, current trx should stop and wait for signal
to rollback itself
*/
if ((all || ending_single_stmt_trans(worker->info_thd, all) || m_rollback_trx) &&
m_workers[worker->id].status == OCS_WAIT)
{
PSI_stage_info old_stage;
mysql_cond_t *cond= &m_workers[worker->id].cond;
THD *thd= worker->info_thd;
DBUG_PRINT("info", ("Worker %lu is waiting for commit signal", worker->id));
mysql_mutex_lock(&m_mutex);
thd->ENTER_COND(cond, &m_mutex,
&stage_worker_waiting_for_its_turn_to_commit,
&old_stage);
while (queue_front() != worker->id)
{
if (unlikely(worker->found_order_commit_deadlock()))
{
mysql_mutex_unlock(&m_mutex);
thd->EXIT_COND(&old_stage);
DBUG_RETURN(true);
}
mysql_cond_wait(cond, &m_mutex);
}
mysql_mutex_unlock(&m_mutex);
thd->EXIT_COND(&old_stage);
m_workers[worker->id].status= OCS_SIGNAL;
if (m_rollback_trx)
{
unregister_trx(worker);
DBUG_PRINT("info", ("thd has seen an error signal from old thread"));
thd->get_stmt_da()->set_overwrite_status(true);
my_error(ER_SLAVE_WORKER_STOPPED_PREVIOUS_THD_ERROR, MYF(0));
}
}
DBUG_RETURN(m_rollback_trx);
}
void Commit_order_manager::unregister_trx(Slave_worker *worker)
{
DBUG_ENTER("Commit_order_manager::unregister_trx");
if (m_workers[worker->id].status == OCS_SIGNAL)
{
DBUG_PRINT("info", ("Worker %lu is signalling next transaction", worker->id));
mysql_mutex_lock(&m_mutex);
DBUG_ASSERT(!queue_empty());
/* Set next manager as the head and signal the trx to commit. */
queue_pop();
if (!queue_empty())
mysql_cond_signal(&m_workers[queue_front()].cond);
m_workers[worker->id].status= OCS_FINISH;
mysql_mutex_unlock(&m_mutex);
}
DBUG_VOID_RETURN;
}
void Commit_order_manager::report_rollback(Slave_worker *worker)
{
DBUG_ENTER("Commit_order_manager::report_rollback");
(void) wait_for_its_turn(worker, true);
/* No worker can set m_rollback_trx unless it is its turn to commit */
m_rollback_trx= true;
unregister_trx(worker);
DBUG_VOID_RETURN;
}
void Commit_order_manager::report_deadlock(Slave_worker *worker)
{
DBUG_ENTER("Commit_order_manager::report_deadlock");
mysql_mutex_lock(&m_mutex);
worker->report_order_commit_deadlock();
DBUG_EXECUTE_IF("rpl_fake_cod_deadlock",
{
const char act[]= "now signal reported_deadlock";
DBUG_ASSERT(!debug_sync_set_action(current_thd,
STRING_WITH_LEN(act)));
});
mysql_cond_signal(&m_workers[worker->id].cond);
mysql_mutex_unlock(&m_mutex);
DBUG_VOID_RETURN;
}