-
Notifications
You must be signed in to change notification settings - Fork 149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PXC-4173: PXC node stalls with parallel replication workers executing DDLs via async node #1927
base: 8.0
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2704,6 +2704,14 @@ Slave_worker *Log_event::get_slave_worker(Relay_log_info *rli) { | |
|
||
Gtid_log_event *gtid_log_ev = static_cast<Gtid_log_event *>(this); | ||
rli->started_processing(gtid_log_ev); | ||
#ifdef WITH_WSREP | ||
Wsrep_async_monitor *wsrep_async_monitor {rli->get_wsrep_async_monitor()}; | ||
if (wsrep_async_monitor) { | ||
auto seqno = gtid_log_ev->sequence_number; | ||
wsrep_async_monitor->schedule(seqno); | ||
fprintf(stderr, "Scheduled the seqno: %llu in async monitor\n", seqno); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fprintf |
||
} | ||
#endif /* WITH_WSREP */ | ||
} | ||
|
||
if (schedule_next_event(this, rli)) { | ||
|
@@ -11207,6 +11215,21 @@ static enum_tbl_map_status check_table_map(Relay_log_info const *rli, | |
} | ||
} | ||
|
||
#ifdef WITH_WSREP | ||
// This transaction is anyways going to be skipped. So skip the transaction | ||
// in the async monitor as well | ||
if (WSREP(rli->info_thd) && res == FILTERED_OUT) { | ||
Slave_worker *sw = static_cast<Slave_worker *>(const_cast<Relay_log_info *>(rli)); | ||
Wsrep_async_monitor *wsrep_async_monitor {sw->get_wsrep_async_monitor()}; | ||
if (wsrep_async_monitor) { | ||
auto seqno = sw->sequence_number(); | ||
assert(seqno > 0); | ||
wsrep_async_monitor->skip(seqno); | ||
fprintf(stderr, "Filtered DML seqno: %llu in async monitor\n", seqno); | ||
} | ||
} | ||
#endif /* WITH_WSREP */ | ||
|
||
DBUG_PRINT("debug", ("check of table map ended up with: %u", res)); | ||
|
||
return res; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,9 @@ | |
#include "sql/mysqld.h" // connection_events_loop_aborted | ||
#include "sql/rpl_gtid.h" | ||
#include "sql/rpl_rli.h" // Relay_log_info | ||
#ifdef WITH_WSREP | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we keep WSREP-dependant includes after the standard ones, like we do in other cases? |
||
#include "sql/rpl_rli_pdb.h" // Slave_worker | ||
#endif /* WITH_WSREP */ | ||
#include "sql/sql_class.h" // THD | ||
#include "sql/sql_lex.h" | ||
#include "sql/sql_parse.h" // stmt_causes_implicit_commit | ||
|
@@ -354,6 +357,19 @@ static inline void skip_statement(THD *thd) { | |
to notify that its session ticket was consumed. | ||
*/ | ||
Commit_stage_manager::get_instance().finish_session_ticket(thd); | ||
#ifdef WITH_WSREP | ||
/* Despite the transaction was skipped, it needs to be updated in the Wsrep_async_monitor */ | ||
if (thd->slave_thread) { | ||
Slave_worker *sw = dynamic_cast<Slave_worker*>(thd->rli_slave); | ||
Wsrep_async_monitor *wsrep_async_monitor {sw->get_wsrep_async_monitor()}; | ||
if (wsrep_async_monitor) { | ||
auto seqno = sw->sequence_number(); | ||
assert(seqno > 0); | ||
wsrep_async_monitor->skip(seqno); | ||
fprintf(stderr, "Skipped the seqno: %llu in async monitor\n", seqno); | ||
} | ||
} | ||
#endif /* WITH_WSREP */ | ||
|
||
#ifndef NDEBUG | ||
const Gtid_set *executed_gtids = gtid_state->get_executed_gtids(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,6 +63,9 @@ | |
#include "sql/sql_class.h" // THD | ||
#include "sql/system_variables.h" | ||
#include "sql/table.h" | ||
#ifdef WITH_WSREP | ||
#include "sql/wsrep_async_monitor.h" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like forward type declaration is enough, and the header can be included in *.cc file |
||
#endif /* WITH_WSREP */ | ||
|
||
class Commit_order_manager; | ||
class Master_info; | ||
|
@@ -1793,6 +1796,15 @@ class Relay_log_info : public Rpl_info { | |
commit_order_mngr = mngr; | ||
} | ||
|
||
#ifdef WITH_WSREP | ||
Wsrep_async_monitor* get_wsrep_async_monitor() { | ||
return wsrep_async_monitor; | ||
} | ||
void set_wsrep_async_monitor(Wsrep_async_monitor *monitor) { | ||
wsrep_async_monitor = monitor; | ||
} | ||
#endif /* WITH_WSREP */ | ||
|
||
/* | ||
Following set function is required to initialize the 'until_option' during | ||
MTS relay log recovery process. | ||
|
@@ -1850,6 +1862,12 @@ class Relay_log_info : public Rpl_info { | |
*/ | ||
Commit_order_manager *commit_order_mngr; | ||
|
||
#ifdef WITH_WSREP | ||
/* | ||
Wsrep_async_monitor orders DMLs and DDls in galera. | ||
*/ | ||
Wsrep_async_monitor *wsrep_async_monitor; | ||
#endif /* WITH_WSREP */ | ||
/** | ||
Delay slave SQL thread by this amount of seconds. | ||
The delay is applied per transaction and based on the immediate master's | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/* Copyright (c) 2024 Percona LLC 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 */ | ||
|
||
#ifdef WITH_WSREP | ||
#include "sql/wsrep_async_monitor.h" | ||
#include <algorithm> | ||
#include <cassert> | ||
|
||
// Method for main thread to add scheduled seqnos | ||
void Wsrep_async_monitor::schedule(seqno_t seqno) { | ||
std::unique_lock<std::mutex> lock(m_mutex); | ||
scheduled_seqnos.push(seqno); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seqons should be growing monolithically, right? |
||
} | ||
|
||
// Method for both DDL and DML to enter the monitor | ||
void Wsrep_async_monitor::enter(seqno_t seqno) { | ||
std::unique_lock<std::mutex> lock(m_mutex); | ||
|
||
// Wait until this transaction is at the head of the scheduled queue | ||
m_cond.wait(lock, [this, seqno] { | ||
// Remove skipped transactions | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When this can happen? Could you add a code comment? |
||
while (!scheduled_seqnos.empty() && | ||
skipped_seqnos.count(scheduled_seqnos.front()) > 0) { | ||
scheduled_seqnos.pop(); | ||
} | ||
return !scheduled_seqnos.empty() && scheduled_seqnos.front() == seqno; | ||
}); | ||
} | ||
|
||
// Method to be called after DDL/DML processing is complete | ||
void Wsrep_async_monitor::leave(seqno_t seqno) { | ||
std::unique_lock<std::mutex> lock(m_mutex); | ||
|
||
// Check if the sequence number matches the front of the queue. | ||
// In a correctly functioning monitor this should not happen | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a correctly functioning monitor this should not happen -> |
||
// as each transaction should exit in the order it was scheduled | ||
// and processed. | ||
if (!scheduled_seqnos.empty() && scheduled_seqnos.front() == seqno) { | ||
// Remove the seqno from the scheduled queue now that it has completed | ||
scheduled_seqnos.pop(); | ||
} else { | ||
// std::cout << "Error: Mismatch in sequence numbers. Expected " | ||
// << (scheduled_seqnos.empty() | ||
// ? "none" | ||
// : std::to_string(scheduled_seqnos.front())) | ||
// << " but got " << seqno << "." << std::endl; | ||
assert(false && "Sequence number mismatch in leave()"); | ||
exit(1); | ||
} | ||
|
||
// Notify waiting threads in case the next scheduled sequence can enter | ||
m_cond.notify_all(); | ||
} | ||
|
||
// Method to skip a transaction that will not call enter() and exit() | ||
void Wsrep_async_monitor::skip(unsigned long seqno) { | ||
std::unique_lock<std::mutex> lock(m_mutex); | ||
|
||
// Check if the seqno is already marked as skipped | ||
if (skipped_seqnos.count(seqno) > 0) { | ||
return; // Already skipped, so do nothing | ||
} | ||
|
||
// Mark the seqno as skipped | ||
skipped_seqnos.insert(seqno); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't see any place where seqnos are removed from |
||
|
||
// Remove it from the scheduled queue if it is at the front | ||
if (!scheduled_seqnos.empty() && scheduled_seqnos.front() == seqno) { | ||
scheduled_seqnos.pop(); | ||
} | ||
|
||
// Notify in case other transactions are waiting to enter | ||
m_cond.notify_all(); | ||
} | ||
|
||
// Method to return if the monitor is empty, used by the unittests | ||
bool Wsrep_async_monitor::is_empty() { | ||
std::unique_lock<std::mutex> lock(m_mutex); | ||
return scheduled_seqnos.empty(); | ||
} | ||
#endif /* WITH_WSREP */ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* Copyright (c) 2024 Percona LLC 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 */ | ||
|
||
#ifndef WSREP_ASYNC_MONITOR_H | ||
#define WSREP_ASYNC_MONITOR_H | ||
|
||
#ifdef WITH_WSREP | ||
#include <condition_variable> | ||
#include <iostream> | ||
#include <mutex> | ||
#include <queue> | ||
#include <set> | ||
|
||
class Wsrep_async_monitor { | ||
public: | ||
using seqno_t = unsigned long long; | ||
|
||
// Method for main thread to add scheduled seqnos | ||
void schedule(seqno_t seqno); | ||
|
||
// Method for both DDL and DML to enter the monitor | ||
void enter(seqno_t seqno); | ||
|
||
// Method to be called after DDL/DML processing is complete | ||
void leave(seqno_t seqno); | ||
|
||
// Method to skip a transaction that will not call enter() and exit() | ||
void skip(unsigned long seqno); | ||
|
||
// Method to return if the monitor is empty, used by the unittests | ||
bool is_empty(); | ||
|
||
private: | ||
std::mutex m_mutex; | ||
std::condition_variable m_cond; | ||
std::set<seqno_t> skipped_seqnos; // Tracks skipped sequence numbers | ||
std::queue<seqno_t> scheduled_seqnos; // Queue to track scheduled seqnos | ||
}; | ||
|
||
#endif /* WITH_WSREP */ | ||
#endif /* WSREP_ASYNC_MONITOR_H */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is my understandig correct, that in this point transactions appear in the order they were committed on the source node?
We create the queue of ordered transactions, and will use AsyncMonitor to enforce this order of commit in Galera, right?