MXS-1744 Rename and clean up gtid code, move to separate file
Renamed the two gtid-classes to better match MariaDB documentation. One domain-server-sequence-combination is now a "Gtid", as these identify a transaction. A "GtidList" contains a list of Gtid:s for handling multi-domain server variables composed of multiple comma separated Gtid:s. Removed unused methods and renamed some existing ones. Moved the Gtid-code to its own file.
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
add_library(mariadbmon SHARED mariadbmon.cc mariadbserver.cc cluster_manipulation.cc cluster_discovery.cc
|
add_library(mariadbmon SHARED mariadbmon.cc mariadbserver.cc cluster_manipulation.cc cluster_discovery.cc
|
||||||
utilities.cc)
|
utilities.cc gtid.cc)
|
||||||
target_link_libraries(mariadbmon maxscale-common)
|
target_link_libraries(mariadbmon maxscale-common)
|
||||||
add_dependencies(mariadbmon pcre2)
|
add_dependencies(mariadbmon pcre2)
|
||||||
set_target_properties(mariadbmon PROPERTIES VERSION "1.4.0")
|
set_target_properties(mariadbmon PROPERTIES VERSION "1.4.0")
|
||||||
|
@ -811,7 +811,7 @@ bool MariaDBMonitor::failover_wait_relay_log(MXS_MONITORED_SERVER* new_master, i
|
|||||||
new_master->server->unique_name, master_info->relay_log_events());
|
new_master->server->unique_name, master_info->relay_log_events());
|
||||||
thread_millisleep(1000); // Sleep for a while before querying server again.
|
thread_millisleep(1000); // Sleep for a while before querying server again.
|
||||||
// Todo: check server version before entering failover.
|
// Todo: check server version before entering failover.
|
||||||
Gtid old_gtid_io_pos = master_info->slave_status.gtid_io_pos;
|
GtidList old_gtid_io_pos = master_info->slave_status.gtid_io_pos;
|
||||||
// Update gtid:s first to make sure Gtid_IO_Pos is the more recent value.
|
// Update gtid:s first to make sure Gtid_IO_Pos is the more recent value.
|
||||||
// It doesn't matter here, but is a general rule.
|
// It doesn't matter here, but is a general rule.
|
||||||
query_ok = master_info->update_gtids() && master_info->do_show_slave_status();
|
query_ok = master_info->update_gtids() && master_info->do_show_slave_status();
|
||||||
@ -961,7 +961,7 @@ bool MariaDBMonitor::switchover_demote_master(MXS_MONITORED_SERVER* current_mast
|
|||||||
* @param err_out json object for error printing. Can be NULL.
|
* @param err_out json object for error printing. Can be NULL.
|
||||||
* @return True, if target gtid was reached within allotted time for all servers
|
* @return True, if target gtid was reached within allotted time for all servers
|
||||||
*/
|
*/
|
||||||
bool MariaDBMonitor::switchover_wait_slaves_catchup(const ServerVector& slaves, const Gtid& gtid,
|
bool MariaDBMonitor::switchover_wait_slaves_catchup(const ServerVector& slaves, const GtidList& gtid,
|
||||||
int total_timeout, int read_timeout, json_t** err_out)
|
int total_timeout, int read_timeout, json_t** err_out)
|
||||||
{
|
{
|
||||||
bool success = true;
|
bool success = true;
|
||||||
@ -1002,7 +1002,7 @@ bool MariaDBMonitor::switchover_wait_slaves_catchup(const ServerVector& slaves,
|
|||||||
* @param err_out json object for error printing. Can be NULL.
|
* @param err_out json object for error printing. Can be NULL.
|
||||||
* @return True, if target gtid was reached within allotted time
|
* @return True, if target gtid was reached within allotted time
|
||||||
*/
|
*/
|
||||||
bool MariaDBMonitor::switchover_wait_slave_catchup(MXS_MONITORED_SERVER* slave, const Gtid& gtid,
|
bool MariaDBMonitor::switchover_wait_slave_catchup(MXS_MONITORED_SERVER* slave, const GtidList& gtid,
|
||||||
int total_timeout, int read_timeout,
|
int total_timeout, int read_timeout,
|
||||||
json_t** err_out)
|
json_t** err_out)
|
||||||
{
|
{
|
||||||
@ -1075,7 +1075,7 @@ bool MariaDBMonitor::wait_cluster_stabilization(MXS_MONITORED_SERVER* new_master
|
|||||||
int query_fails = 0;
|
int query_fails = 0;
|
||||||
int repl_fails = 0;
|
int repl_fails = 0;
|
||||||
int successes = 0;
|
int successes = 0;
|
||||||
const Gtid& target = new_master_info->gtid_current_pos;
|
const GtidList& target = new_master_info->gtid_current_pos;
|
||||||
ServerVector wait_list = slaves; // Check all the servers in the list
|
ServerVector wait_list = slaves; // Check all the servers in the list
|
||||||
bool first_round = true;
|
bool first_round = true;
|
||||||
bool time_is_up = false;
|
bool time_is_up = false;
|
||||||
@ -1105,8 +1105,8 @@ bool MariaDBMonitor::wait_cluster_stabilization(MXS_MONITORED_SERVER* new_master
|
|||||||
wait_list.erase(wait_list.begin() + i);
|
wait_list.erase(wait_list.begin() + i);
|
||||||
repl_fails++;
|
repl_fails++;
|
||||||
}
|
}
|
||||||
else if (Gtid::events_ahead(target, slave_info->gtid_current_pos,
|
else if (GtidList::events_ahead(target, slave_info->gtid_current_pos,
|
||||||
Gtid::MISSING_DOMAIN_IGNORE) == 0)
|
GtidList::MISSING_DOMAIN_IGNORE) == 0)
|
||||||
{
|
{
|
||||||
// This slave has reached the same gtid as master, remove from list
|
// This slave has reached the same gtid as master, remove from list
|
||||||
wait_list.erase(wait_list.begin() + i);
|
wait_list.erase(wait_list.begin() + i);
|
||||||
@ -1329,10 +1329,10 @@ bool MariaDBMonitor::server_is_excluded(const MXS_MONITORED_SERVER* server)
|
|||||||
bool MariaDBMonitor::is_candidate_better(const MariaDBServer* current_best_info,
|
bool MariaDBMonitor::is_candidate_better(const MariaDBServer* current_best_info,
|
||||||
const MariaDBServer* candidate_info, uint32_t gtid_domain)
|
const MariaDBServer* candidate_info, uint32_t gtid_domain)
|
||||||
{
|
{
|
||||||
uint64_t cand_io = candidate_info->slave_status.gtid_io_pos.get_triplet(gtid_domain).sequence;
|
uint64_t cand_io = candidate_info->slave_status.gtid_io_pos.get_gtid(gtid_domain).m_sequence;
|
||||||
uint64_t cand_processed = candidate_info->gtid_current_pos.get_triplet(gtid_domain).sequence;
|
uint64_t cand_processed = candidate_info->gtid_current_pos.get_gtid(gtid_domain).m_sequence;
|
||||||
uint64_t curr_io = current_best_info->slave_status.gtid_io_pos.get_triplet(gtid_domain).sequence;
|
uint64_t curr_io = current_best_info->slave_status.gtid_io_pos.get_gtid(gtid_domain).m_sequence;
|
||||||
uint64_t curr_processed = current_best_info->gtid_current_pos.get_triplet(gtid_domain).sequence;
|
uint64_t curr_processed = current_best_info->gtid_current_pos.get_gtid(gtid_domain).m_sequence;
|
||||||
|
|
||||||
bool cand_updates = candidate_info->rpl_settings.log_slave_updates;
|
bool cand_updates = candidate_info->rpl_settings.log_slave_updates;
|
||||||
bool curr_updates = current_best_info->rpl_settings.log_slave_updates;
|
bool curr_updates = current_best_info->rpl_settings.log_slave_updates;
|
||||||
|
237
server/modules/monitor/mariadbmon/gtid.cc
Normal file
237
server/modules/monitor/mariadbmon/gtid.cc
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 MariaDB Corporation Ab
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License included
|
||||||
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
|
*
|
||||||
|
* Change Date: 2020-01-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source License, use
|
||||||
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
* Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gtid.hh"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include "utilities.hh"
|
||||||
|
|
||||||
|
GtidList GtidList::from_string(const std::string& gtid_string)
|
||||||
|
{
|
||||||
|
ss_dassert(gtid_string.size());
|
||||||
|
GtidList rval;
|
||||||
|
bool error = false;
|
||||||
|
bool have_more = false;
|
||||||
|
const char* str = gtid_string.c_str();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
char* endptr = NULL;
|
||||||
|
auto new_triplet = Gtid::from_string(str, &endptr);
|
||||||
|
if (new_triplet.m_server_id == SERVER_ID_UNKNOWN)
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rval.m_triplets.push_back(new_triplet);
|
||||||
|
// The last number must be followed by ',' (another triplet) or \0 (last triplet)
|
||||||
|
if (*endptr == ',')
|
||||||
|
{
|
||||||
|
have_more = true;
|
||||||
|
str = endptr + 1;
|
||||||
|
}
|
||||||
|
else if (*endptr == '\0')
|
||||||
|
{
|
||||||
|
have_more = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (have_more && !error);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
// If error occurred, clear the gtid as something is very wrong.
|
||||||
|
rval.m_triplets.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Usually the servers gives the triplets ordered by domain id:s, but this is not 100%.
|
||||||
|
std::sort(rval.m_triplets.begin(), rval.m_triplets.end(), Gtid::compare_domains);
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
string GtidList::to_string() const
|
||||||
|
{
|
||||||
|
string rval;
|
||||||
|
string separator;
|
||||||
|
for (auto iter = m_triplets.begin(); iter != m_triplets.end(); iter++)
|
||||||
|
{
|
||||||
|
rval += separator + iter->to_string();
|
||||||
|
separator = ",";
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtidList::can_replicate_from(const GtidList& master_gtid)
|
||||||
|
{
|
||||||
|
/* The result of this function is false if the source and master have a common domain id where
|
||||||
|
* the source is ahead of the master. */
|
||||||
|
return (events_ahead(*this, master_gtid, MISSING_DOMAIN_IGNORE) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtidList::empty() const
|
||||||
|
{
|
||||||
|
return m_triplets.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtidList::operator == (const GtidList& rhs) const
|
||||||
|
{
|
||||||
|
return m_triplets == rhs.m_triplets;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GtidList::events_ahead(const GtidList& lhs, const GtidList& rhs, substraction_mode_t domain_substraction_mode)
|
||||||
|
{
|
||||||
|
const size_t n_lhs = lhs.m_triplets.size();
|
||||||
|
const size_t n_rhs = rhs.m_triplets.size();
|
||||||
|
size_t ind_lhs = 0, ind_rhs = 0;
|
||||||
|
uint64_t events = 0;
|
||||||
|
|
||||||
|
while (ind_lhs < n_lhs && ind_rhs < n_rhs)
|
||||||
|
{
|
||||||
|
auto lhs_triplet = lhs.m_triplets[ind_lhs];
|
||||||
|
auto rhs_triplet = rhs.m_triplets[ind_rhs];
|
||||||
|
// Server id -1 should never be saved in a real gtid variable.
|
||||||
|
ss_dassert(lhs_triplet.m_server_id != SERVER_ID_UNKNOWN &&
|
||||||
|
rhs_triplet.m_server_id != SERVER_ID_UNKNOWN);
|
||||||
|
// Search for matching domain_id:s, advance the smaller one.
|
||||||
|
if (lhs_triplet.m_domain < rhs_triplet.m_domain)
|
||||||
|
{
|
||||||
|
if (domain_substraction_mode == MISSING_DOMAIN_LHS_ADD)
|
||||||
|
{
|
||||||
|
// The domain on lhs does not exist on rhs. Add entire sequence number of lhs to the result.
|
||||||
|
events += lhs_triplet.m_sequence;
|
||||||
|
}
|
||||||
|
ind_lhs++;
|
||||||
|
}
|
||||||
|
else if (lhs_triplet.m_domain > rhs_triplet.m_domain)
|
||||||
|
{
|
||||||
|
ind_rhs++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Domains match, check sequences.
|
||||||
|
if (lhs_triplet.m_sequence > rhs_triplet.m_sequence)
|
||||||
|
{
|
||||||
|
/* Same domains, but lhs sequence is equal or ahead of rhs sequence. */
|
||||||
|
events += lhs_triplet.m_sequence - rhs_triplet.m_sequence;
|
||||||
|
}
|
||||||
|
// Continue to next domains.
|
||||||
|
ind_lhs++;
|
||||||
|
ind_rhs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtid Gtid::from_string(const char* str, char** endptr)
|
||||||
|
{
|
||||||
|
/* Error checking the gtid string is a bit questionable, as having an error means that the server is
|
||||||
|
buggy or network has faults, in which case nothing can be trusted. But without error checking
|
||||||
|
MaxScale may crash if string is wrong. */
|
||||||
|
ss_dassert(endptr);
|
||||||
|
const char* ptr = str;
|
||||||
|
char* strtoull_endptr = NULL;
|
||||||
|
// Parse three numbers separated by -
|
||||||
|
uint64_t parsed_numbers[3];
|
||||||
|
bool error = false;
|
||||||
|
for (int i = 0; i < 3 && !error; i++)
|
||||||
|
{
|
||||||
|
errno = 0;
|
||||||
|
parsed_numbers[i] = strtoull(ptr, &strtoull_endptr, 10);
|
||||||
|
// No parse error
|
||||||
|
if (errno != 0)
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
else if (i < 2)
|
||||||
|
{
|
||||||
|
// First two numbers must be followed by a -
|
||||||
|
if (*strtoull_endptr == '-')
|
||||||
|
{
|
||||||
|
ptr = strtoull_endptr + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that none of the parsed numbers are unexpectedly large. This shouldn't really be possible unless
|
||||||
|
// server has a bug or network had an error.
|
||||||
|
if (!error && (parsed_numbers[0] > UINT32_MAX || parsed_numbers[1] > UINT32_MAX))
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!error)
|
||||||
|
{
|
||||||
|
*endptr = strtoull_endptr;
|
||||||
|
return Gtid((uint32_t)parsed_numbers[0], parsed_numbers[1], parsed_numbers[2]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Gtid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtid::Gtid()
|
||||||
|
: m_domain(0)
|
||||||
|
, m_server_id(SERVER_ID_UNKNOWN)
|
||||||
|
, m_sequence(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Gtid::Gtid(uint32_t domain, int64_t server_id, uint64_t sequence)
|
||||||
|
: m_domain(domain)
|
||||||
|
, m_server_id(server_id)
|
||||||
|
, m_sequence(sequence)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool Gtid::eq(const Gtid& rhs) const
|
||||||
|
{
|
||||||
|
return m_domain == rhs.m_domain && m_server_id == rhs.m_server_id && m_sequence == rhs.m_sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
string Gtid::to_string() const
|
||||||
|
{
|
||||||
|
string rval;
|
||||||
|
if (m_server_id != SERVER_ID_UNKNOWN)
|
||||||
|
{
|
||||||
|
rval += std::to_string(m_domain) + "-" + std::to_string(m_server_id) + "-" +
|
||||||
|
std::to_string(m_sequence);
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
string GtidList::generate_master_gtid_wait_cmd(double timeout) const
|
||||||
|
{
|
||||||
|
return "SELECT MASTER_GTID_WAIT(\"" + to_string() + "\", " + std::to_string(timeout) + ");";
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtid GtidList::get_gtid(uint32_t domain) const
|
||||||
|
{
|
||||||
|
Gtid rval;
|
||||||
|
// Make a dummy triplet for the domain search
|
||||||
|
Gtid search_val(domain, -1, 0);
|
||||||
|
auto found = std::lower_bound(m_triplets.begin(), m_triplets.end(), search_val,
|
||||||
|
Gtid::compare_domains);
|
||||||
|
if (found != m_triplets.end() && found->m_domain == domain)
|
||||||
|
{
|
||||||
|
rval = *found;
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
161
server/modules/monitor/mariadbmon/gtid.hh
Normal file
161
server/modules/monitor/mariadbmon/gtid.hh
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 MariaDB Corporation Ab
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License included
|
||||||
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
|
*
|
||||||
|
* Change Date: 2020-01-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source License, use
|
||||||
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
* Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <maxscale/cppdefs.hh>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class which encapsulates a gtid (one domain-server_id-sequence combination)
|
||||||
|
*/
|
||||||
|
class Gtid
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an invalid Gtid.
|
||||||
|
*/
|
||||||
|
Gtid();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a gtid with given values. The values are not checked.
|
||||||
|
*
|
||||||
|
* @param domain Domain
|
||||||
|
* @param server_id Server id
|
||||||
|
* @param sequence Sequence
|
||||||
|
*/
|
||||||
|
Gtid(uint32_t domain, int64_t server_id, uint64_t sequence);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse one gtid from null-terminated string. Handles multi-domain gtid:s properly. Should be called
|
||||||
|
* repeatedly for a multi-domain gtid string by giving the value of @c endptr as @c str.
|
||||||
|
*
|
||||||
|
* @param str First number of a gtid in a gtid-string
|
||||||
|
* @param endptr A pointer to save the position at after the last parsed character.
|
||||||
|
* @return A new gtid. If an error occurs, the server_id of the returned triplet is -1.
|
||||||
|
*/
|
||||||
|
static Gtid from_string(const char* str, char** endptr);
|
||||||
|
|
||||||
|
bool eq(const Gtid& rhs) const;
|
||||||
|
|
||||||
|
std::string to_string() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparator, used when sorting by domain id.
|
||||||
|
*
|
||||||
|
* @param lhs Left side
|
||||||
|
* @param rhs Right side
|
||||||
|
* @return True if lhs should be before rhs
|
||||||
|
*/
|
||||||
|
static bool compare_domains(const Gtid& lhs, const Gtid& rhs)
|
||||||
|
{
|
||||||
|
return lhs.m_domain < rhs.m_domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t m_domain;
|
||||||
|
int64_t m_server_id; // Valid values are 32bit unsigned. 0 is only used by server versions <= 10.1
|
||||||
|
uint64_t m_sequence;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool operator == (const Gtid& lhs, const Gtid& rhs)
|
||||||
|
{
|
||||||
|
return lhs.eq(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class which encapsulates a list of gtid:s (e.g. 1-2-3,2-2-4). Server variables such as gtid_binlog_pos
|
||||||
|
* are GtidLists. */
|
||||||
|
class GtidList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Used with events_ahead()
|
||||||
|
enum substraction_mode_t
|
||||||
|
{
|
||||||
|
MISSING_DOMAIN_IGNORE,
|
||||||
|
MISSING_DOMAIN_LHS_ADD
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the gtid string and return an object. Orders the triplets by domain id.
|
||||||
|
*
|
||||||
|
* @param gtid_string gtid as given by server. String must not be empty.
|
||||||
|
* @return The parsed (possibly multidomain) gtid. In case of error, the gtid will be empty.
|
||||||
|
*/
|
||||||
|
static GtidList from_string(const std::string& gtid_string);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a string version of the gtid list.
|
||||||
|
*
|
||||||
|
* @return A string similar in form to how the server displays gtid:s
|
||||||
|
*/
|
||||||
|
std::string to_string() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a server with this gtid can replicate from a master with a given gtid. Only considers
|
||||||
|
* gtid:s and only detects obvious errors. The non-detected errors will mostly be detected once
|
||||||
|
* the slave tries to start replicating.
|
||||||
|
*
|
||||||
|
* TODO: Add support for Replicate_Do/Ignore_Id:s
|
||||||
|
*
|
||||||
|
* @param master_gtid Master server gtid
|
||||||
|
* @return True if replication looks possible
|
||||||
|
*/
|
||||||
|
bool can_replicate_from(const GtidList& master_gtid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the gtid empty.
|
||||||
|
*
|
||||||
|
* @return True if gtid has 0 triplets
|
||||||
|
*/
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full comparison.
|
||||||
|
*
|
||||||
|
* @param rhs Other gtid
|
||||||
|
* @return True if both gtid:s have identical triplets or both are empty
|
||||||
|
*/
|
||||||
|
bool operator == (const GtidList& rhs) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the number of events between two gtid:s with possibly multiple triplets. The
|
||||||
|
* result is always 0 or greater: if a sequence number of a domain on rhs is greater than on the same
|
||||||
|
* domain on lhs, the sequences are considered identical. Missing domains are handled depending on the
|
||||||
|
* value of @c domain_substraction_mode.
|
||||||
|
*
|
||||||
|
* @param lhs The value substracted from
|
||||||
|
* @param io_pos The value doing the substracting
|
||||||
|
* @param domain_substraction_mode How domains that exist on one side but not the other are handled. If
|
||||||
|
* MISSING_DOMAIN_IGNORE, these are simply ignored. If MISSING_DOMAIN_LHS_ADD, the sequence number on lhs
|
||||||
|
* is added to the total difference.
|
||||||
|
* @return The number of events between the two gtid:s
|
||||||
|
*/
|
||||||
|
static uint64_t events_ahead(const GtidList& lhs, const GtidList& rhs,
|
||||||
|
substraction_mode_t domain_substraction_mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a MASTER_GTID_WAIT()-query to this gtid.
|
||||||
|
*
|
||||||
|
* @param timeout Maximum wait time in seconds
|
||||||
|
* @return The query
|
||||||
|
*/
|
||||||
|
std::string generate_master_gtid_wait_cmd(double timeout) const;
|
||||||
|
|
||||||
|
Gtid get_gtid(uint32_t domain) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Gtid> m_triplets;
|
||||||
|
};
|
@ -197,9 +197,9 @@ private:
|
|||||||
bool failover_wait_relay_log(MXS_MONITORED_SERVER* new_master, int seconds_remaining, json_t** err_out);
|
bool failover_wait_relay_log(MXS_MONITORED_SERVER* new_master, int seconds_remaining, json_t** err_out);
|
||||||
bool switchover_demote_master(MXS_MONITORED_SERVER* current_master, MariaDBServer* info,
|
bool switchover_demote_master(MXS_MONITORED_SERVER* current_master, MariaDBServer* info,
|
||||||
json_t** err_out);
|
json_t** err_out);
|
||||||
bool switchover_wait_slaves_catchup(const ServerVector& slaves, const Gtid& gtid, int total_timeout,
|
bool switchover_wait_slaves_catchup(const ServerVector& slaves, const GtidList& gtid, int total_timeout,
|
||||||
int read_timeout, json_t** err_out);
|
int read_timeout, json_t** err_out);
|
||||||
bool switchover_wait_slave_catchup(MXS_MONITORED_SERVER* slave, const Gtid& gtid,
|
bool switchover_wait_slave_catchup(MXS_MONITORED_SERVER* slave, const GtidList& gtid,
|
||||||
int total_timeout, int read_timeout, json_t** err_out);
|
int total_timeout, int read_timeout, json_t** err_out);
|
||||||
bool wait_cluster_stabilization(MXS_MONITORED_SERVER* new_master, const ServerVector& slaves,
|
bool wait_cluster_stabilization(MXS_MONITORED_SERVER* new_master, const ServerVector& slaves,
|
||||||
int seconds_remaining);
|
int seconds_remaining);
|
||||||
|
@ -48,7 +48,8 @@ int64_t MariaDBServer::relay_log_events()
|
|||||||
* rare but is possible (I guess?) if the server is replicating a domain from multiple masters
|
* rare but is possible (I guess?) if the server is replicating a domain from multiple masters
|
||||||
* and decides to process events from one relay log before getting new events to the other. In
|
* and decides to process events from one relay log before getting new events to the other. In
|
||||||
* any case, such events are obsolete and the server can be considered to have processed such logs. */
|
* any case, such events are obsolete and the server can be considered to have processed such logs. */
|
||||||
return Gtid::events_ahead(slave_status.gtid_io_pos, gtid_current_pos, Gtid::MISSING_DOMAIN_LHS_ADD);
|
return GtidList::events_ahead(slave_status.gtid_io_pos, gtid_current_pos,
|
||||||
|
GtidList::MISSING_DOMAIN_LHS_ADD);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::auto_ptr<QueryResult> MariaDBServer::execute_query(const string& query)
|
std::auto_ptr<QueryResult> MariaDBServer::execute_query(const string& query)
|
||||||
@ -199,11 +200,11 @@ bool MariaDBServer::do_show_slave_status()
|
|||||||
if (!gtid_io_pos.empty() &&
|
if (!gtid_io_pos.empty() &&
|
||||||
(using_gtid == "Current_Pos" || using_gtid == "Slave_Pos"))
|
(using_gtid == "Current_Pos" || using_gtid == "Slave_Pos"))
|
||||||
{
|
{
|
||||||
slave_status.gtid_io_pos = Gtid::from_string(gtid_io_pos);
|
slave_status.gtid_io_pos = GtidList::from_string(gtid_io_pos);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
slave_status.gtid_io_pos = Gtid();
|
slave_status.gtid_io_pos = GtidList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -242,21 +243,21 @@ bool MariaDBServer::update_gtids()
|
|||||||
bool binlog_ok = false;
|
bool binlog_ok = false;
|
||||||
if (current_str.empty())
|
if (current_str.empty())
|
||||||
{
|
{
|
||||||
gtid_current_pos = Gtid();
|
gtid_current_pos = GtidList();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gtid_current_pos = Gtid::from_string(current_str);
|
gtid_current_pos = GtidList::from_string(current_str);
|
||||||
current_ok = !gtid_current_pos.empty();
|
current_ok = !gtid_current_pos.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (binlog_str.empty())
|
if (binlog_str.empty())
|
||||||
{
|
{
|
||||||
gtid_binlog_pos = Gtid();
|
gtid_binlog_pos = GtidList();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gtid_binlog_pos = Gtid::from_string(binlog_str);
|
gtid_binlog_pos = GtidList::from_string(binlog_str);
|
||||||
binlog_ok = !gtid_binlog_pos.empty();
|
binlog_ok = !gtid_binlog_pos.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <maxscale/monitor.h>
|
#include <maxscale/monitor.h>
|
||||||
#include "utilities.hh"
|
#include "utilities.hh"
|
||||||
|
#include "gtid.hh"
|
||||||
|
|
||||||
enum mysql_server_version
|
enum mysql_server_version
|
||||||
{
|
{
|
||||||
@ -47,7 +48,7 @@ public:
|
|||||||
* reading from. */
|
* reading from. */
|
||||||
uint64_t read_master_log_pos; /**< Position up to which the I/O thread has read in the current master
|
uint64_t read_master_log_pos; /**< Position up to which the I/O thread has read in the current master
|
||||||
* binary log file. */
|
* binary log file. */
|
||||||
Gtid gtid_io_pos; /**< Gtid I/O position of the slave thread. */
|
GtidList gtid_io_pos; /**< Gtid I/O position of the slave thread. */
|
||||||
std::string last_error; /**< Last IO or SQL error encountered. */
|
std::string last_error; /**< Last IO or SQL error encountered. */
|
||||||
|
|
||||||
SlaveStatusInfo();
|
SlaveStatusInfo();
|
||||||
@ -91,8 +92,8 @@ public:
|
|||||||
time_t latest_event; /**< Time when latest event was received from the master */
|
time_t latest_event; /**< Time when latest event was received from the master */
|
||||||
int64_t gtid_domain_id; /**< The value of gtid_domain_id, the domain which is used for
|
int64_t gtid_domain_id; /**< The value of gtid_domain_id, the domain which is used for
|
||||||
* new non-replicated events. */
|
* new non-replicated events. */
|
||||||
Gtid gtid_current_pos; /**< Gtid of latest event. */
|
GtidList gtid_current_pos; /**< Gtid of latest event. */
|
||||||
Gtid gtid_binlog_pos; /**< Gtid of latest event written to binlog. */
|
GtidList gtid_binlog_pos; /**< Gtid of latest event written to binlog. */
|
||||||
SlaveStatusInfo slave_status; /**< Data returned from SHOW SLAVE STATUS */
|
SlaveStatusInfo slave_status; /**< Data returned from SHOW SLAVE STATUS */
|
||||||
ReplicationSettings rpl_settings; /**< Miscellaneous replication related settings */
|
ReplicationSettings rpl_settings; /**< Miscellaneous replication related settings */
|
||||||
|
|
||||||
|
@ -13,13 +13,10 @@
|
|||||||
|
|
||||||
#include "utilities.hh"
|
#include "utilities.hh"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <stdio.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <maxscale/dcb.h>
|
|
||||||
#include <maxscale/debug.h>
|
#include <maxscale/debug.h>
|
||||||
#include <maxscale/mysql_utils.h>
|
#include <maxscale/mysql_utils.h>
|
||||||
|
|
||||||
@ -201,268 +198,3 @@ bool QueryResult::get_bool(int64_t column_ind) const
|
|||||||
char* data = m_rowdata[column_ind];
|
char* data = m_rowdata[column_ind];
|
||||||
return data ? (strcmp(data,"Y") == 0 || strcmp(data, "1") == 0) : false;
|
return data ? (strcmp(data,"Y") == 0 || strcmp(data, "1") == 0) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GtidTriplet QueryResult::get_gtid(int64_t column_ind, int64_t gtid_domain) const
|
|
||||||
{
|
|
||||||
ss_dassert(column_ind < m_columns);
|
|
||||||
char* data = m_rowdata[column_ind];
|
|
||||||
GtidTriplet rval;
|
|
||||||
if (data && *data)
|
|
||||||
{
|
|
||||||
rval = GtidTriplet(data, gtid_domain);
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtid Gtid::from_string(const std::string& gtid_string)
|
|
||||||
{
|
|
||||||
ss_dassert(gtid_string.size());
|
|
||||||
Gtid rval;
|
|
||||||
bool error = false;
|
|
||||||
bool have_more = false;
|
|
||||||
const char* str = gtid_string.c_str();
|
|
||||||
do
|
|
||||||
{
|
|
||||||
char* endptr = NULL;
|
|
||||||
auto new_triplet = GtidTriplet::parse_one_triplet(str, &endptr);
|
|
||||||
if (new_triplet.server_id == SERVER_ID_UNKNOWN)
|
|
||||||
{
|
|
||||||
error = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rval.m_triplets.push_back(new_triplet);
|
|
||||||
// The last number must be followed by ',' (another triplet) or \0 (last triplet)
|
|
||||||
if (*endptr == ',')
|
|
||||||
{
|
|
||||||
have_more = true;
|
|
||||||
str = endptr + 1;
|
|
||||||
}
|
|
||||||
else if (*endptr == '\0')
|
|
||||||
{
|
|
||||||
have_more = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (have_more && !error);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
// If error occurred, clear the gtid as something is very wrong.
|
|
||||||
rval.m_triplets.clear();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Usually the servers gives the triplets ordered by domain id:s, but this is not 100%.
|
|
||||||
std::sort(rval.m_triplets.begin(), rval.m_triplets.end(), GtidTriplet::compare_domains);
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Gtid::to_string() const
|
|
||||||
{
|
|
||||||
string rval;
|
|
||||||
string separator;
|
|
||||||
for (auto iter = m_triplets.begin(); iter != m_triplets.end(); iter++)
|
|
||||||
{
|
|
||||||
rval += separator + iter->to_string();
|
|
||||||
separator = ",";
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Gtid::can_replicate_from(const Gtid& master_gtid)
|
|
||||||
{
|
|
||||||
/* The result of this function is false if the source and master have a common domain id where
|
|
||||||
* the source is ahead of the master. */
|
|
||||||
return (events_ahead(*this, master_gtid, MISSING_DOMAIN_IGNORE) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Gtid::empty() const
|
|
||||||
{
|
|
||||||
return m_triplets.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Gtid::operator == (const Gtid& rhs) const
|
|
||||||
{
|
|
||||||
return m_triplets == rhs.m_triplets;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t Gtid::events_ahead(const Gtid& lhs, const Gtid& rhs, substraction_mode_t domain_substraction_mode)
|
|
||||||
{
|
|
||||||
const size_t n_lhs = lhs.m_triplets.size();
|
|
||||||
const size_t n_rhs = rhs.m_triplets.size();
|
|
||||||
size_t ind_lhs = 0, ind_rhs = 0;
|
|
||||||
uint64_t events = 0;
|
|
||||||
|
|
||||||
while (ind_lhs < n_lhs && ind_rhs < n_rhs)
|
|
||||||
{
|
|
||||||
auto lhs_triplet = lhs.m_triplets[ind_lhs];
|
|
||||||
auto rhs_triplet = rhs.m_triplets[ind_rhs];
|
|
||||||
// Server id -1 should never be saved in a real gtid variable.
|
|
||||||
ss_dassert(lhs_triplet.server_id != SERVER_ID_UNKNOWN &&
|
|
||||||
rhs_triplet.server_id != SERVER_ID_UNKNOWN);
|
|
||||||
// Search for matching domain_id:s, advance the smaller one.
|
|
||||||
if (lhs_triplet.domain < rhs_triplet.domain)
|
|
||||||
{
|
|
||||||
if (domain_substraction_mode == MISSING_DOMAIN_LHS_ADD)
|
|
||||||
{
|
|
||||||
// The domain on lhs does not exist on rhs. Add entire sequence number of lhs to the result.
|
|
||||||
events += lhs_triplet.sequence;
|
|
||||||
}
|
|
||||||
ind_lhs++;
|
|
||||||
}
|
|
||||||
else if (lhs_triplet.domain > rhs_triplet.domain)
|
|
||||||
{
|
|
||||||
ind_rhs++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Domains match, check sequences.
|
|
||||||
if (lhs_triplet.sequence > rhs_triplet.sequence)
|
|
||||||
{
|
|
||||||
/* Same domains, but lhs sequence is equal or ahead of rhs sequence. */
|
|
||||||
events += lhs_triplet.sequence - rhs_triplet.sequence;
|
|
||||||
}
|
|
||||||
// Continue to next domains.
|
|
||||||
ind_lhs++;
|
|
||||||
ind_rhs++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return events;
|
|
||||||
}
|
|
||||||
|
|
||||||
GtidTriplet GtidTriplet::parse_one_triplet(const char* str, char** endptr)
|
|
||||||
{
|
|
||||||
/* Error checking the gtid string is a bit questionable, as having an error means that the server is
|
|
||||||
buggy or network has faults, in which case nothing can be trusted. But without error checking
|
|
||||||
MaxScale may crash if string is wrong. */
|
|
||||||
ss_dassert(endptr);
|
|
||||||
const char* ptr = str;
|
|
||||||
char* strtoull_endptr = NULL;
|
|
||||||
// Parse three numbers separated by -
|
|
||||||
uint64_t parsed_numbers[3];
|
|
||||||
bool error = false;
|
|
||||||
for (int i = 0; i < 3 && !error; i++)
|
|
||||||
{
|
|
||||||
errno = 0;
|
|
||||||
parsed_numbers[i] = strtoull(ptr, &strtoull_endptr, 10);
|
|
||||||
// No parse error
|
|
||||||
if (errno != 0)
|
|
||||||
{
|
|
||||||
error = true;
|
|
||||||
}
|
|
||||||
else if (i < 2)
|
|
||||||
{
|
|
||||||
// First two numbers must be followed by a -
|
|
||||||
if (*strtoull_endptr == '-')
|
|
||||||
{
|
|
||||||
ptr = strtoull_endptr + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that none of the parsed numbers are unexpectedly large. This shouldn't really be possible unless
|
|
||||||
// server has a bug or network had an error.
|
|
||||||
if (!error && (parsed_numbers[0] > UINT32_MAX || parsed_numbers[1] > UINT32_MAX))
|
|
||||||
{
|
|
||||||
error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!error)
|
|
||||||
{
|
|
||||||
*endptr = strtoull_endptr;
|
|
||||||
return GtidTriplet((uint32_t)parsed_numbers[0], parsed_numbers[1], parsed_numbers[2]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return GtidTriplet();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GtidTriplet::GtidTriplet()
|
|
||||||
: domain(0)
|
|
||||||
, server_id(SERVER_ID_UNKNOWN)
|
|
||||||
, sequence(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
GtidTriplet::GtidTriplet(uint32_t _domain, int64_t _server_id, uint64_t _sequence)
|
|
||||||
: domain(_domain)
|
|
||||||
, server_id(_server_id)
|
|
||||||
, sequence(_sequence)
|
|
||||||
{}
|
|
||||||
|
|
||||||
GtidTriplet::GtidTriplet(const char* str, int64_t search_domain)
|
|
||||||
: domain(0)
|
|
||||||
, server_id(SERVER_ID_UNKNOWN)
|
|
||||||
, sequence(0)
|
|
||||||
{
|
|
||||||
// Autoselect only allowed with one triplet
|
|
||||||
ss_dassert(search_domain >= 0 || strchr(str, ',') == NULL);
|
|
||||||
parse_triplet(str);
|
|
||||||
if (search_domain >= 0 && domain != search_domain)
|
|
||||||
{
|
|
||||||
// Search for the correct triplet.
|
|
||||||
bool found = false;
|
|
||||||
for (const char* next_triplet = strchr(str, ',');
|
|
||||||
next_triplet != NULL && !found;
|
|
||||||
next_triplet = strchr(next_triplet, ','))
|
|
||||||
{
|
|
||||||
parse_triplet(++next_triplet);
|
|
||||||
if (domain == search_domain)
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ss_dassert(found);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GtidTriplet::eq(const GtidTriplet& rhs) const
|
|
||||||
{
|
|
||||||
return domain == rhs.domain && server_id == rhs.server_id && sequence == rhs.sequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
string GtidTriplet::to_string() const
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
if (server_id != SERVER_ID_UNKNOWN)
|
|
||||||
{
|
|
||||||
ss << domain << "-" << server_id << "-" << sequence;
|
|
||||||
}
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtidTriplet::parse_triplet(const char* str)
|
|
||||||
{
|
|
||||||
ss_debug(int rv = ) sscanf(str, "%" PRIu32 "-%" PRId64 "-%" PRIu64, &domain, &server_id, &sequence);
|
|
||||||
ss_dassert(rv == 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
string Gtid::generate_master_gtid_wait_cmd(double timeout) const
|
|
||||||
{
|
|
||||||
std::stringstream query_ss;
|
|
||||||
query_ss << "SELECT MASTER_GTID_WAIT(\"" << to_string() << "\", " << timeout << ");";
|
|
||||||
return query_ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
GtidTriplet Gtid::get_triplet(uint32_t domain) const
|
|
||||||
{
|
|
||||||
GtidTriplet rval;
|
|
||||||
// Make a dummy triplet for the domain search
|
|
||||||
GtidTriplet search_val(domain, -1, 0);
|
|
||||||
auto found = std::lower_bound(m_triplets.begin(), m_triplets.end(), search_val,
|
|
||||||
GtidTriplet::compare_domains);
|
|
||||||
if (found != m_triplets.end() && found->domain == domain)
|
|
||||||
{
|
|
||||||
rval = *found;
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
}
|
|
@ -16,7 +16,6 @@
|
|||||||
#include <maxscale/cppdefs.hh>
|
#include <maxscale/cppdefs.hh>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <maxscale/monitor.h>
|
#include <maxscale/monitor.h>
|
||||||
|
|
||||||
/** Utility macro for printing both MXS_ERROR and json error */
|
/** Utility macro for printing both MXS_ERROR and json error */
|
||||||
@ -72,145 +71,6 @@ string get_connection_errors(const ServerVector& servers);
|
|||||||
*/
|
*/
|
||||||
string monitored_servers_to_string(const ServerVector& array);
|
string monitored_servers_to_string(const ServerVector& array);
|
||||||
|
|
||||||
/**
|
|
||||||
* Class which encapsulates a gtid triplet (one <domain>-<server>-<sequence>)
|
|
||||||
*/
|
|
||||||
class GtidTriplet
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
uint32_t domain;
|
|
||||||
int64_t server_id; // Is actually 32bit unsigned. 0 is only used by server versions <= 10.1
|
|
||||||
uint64_t sequence;
|
|
||||||
|
|
||||||
GtidTriplet();
|
|
||||||
GtidTriplet(uint32_t _domain, int64_t _server_id, uint64_t _sequence);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a Gtid-triplet from a string. In case of a multi-triplet value, only the triplet with
|
|
||||||
* the given domain is returned. TODO: Remove once no longer used
|
|
||||||
*
|
|
||||||
* @param str Gtid string
|
|
||||||
* @param search_domain The Gtid domain whose triplet should be returned. Negative domain stands for
|
|
||||||
* autoselect, which is only allowed when the string contains one triplet.
|
|
||||||
*/
|
|
||||||
GtidTriplet(const char* str, int64_t search_domain = -1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse one triplet from null-terminated string. Handles multi-domain gtid:s properly. Should be called
|
|
||||||
* repeatedly for a multi-domain gtid string by giving the value of @c endptr as @c str.
|
|
||||||
*
|
|
||||||
* @param str First number of a triplet in a gtid-string
|
|
||||||
* @param endptr A pointer to save the position at after the last parsed character.
|
|
||||||
* @return A new GtidTriplet. If an error occurs, the server_id of the returned triplet is -1.
|
|
||||||
*/
|
|
||||||
static GtidTriplet parse_one_triplet(const char* str, char** endptr);
|
|
||||||
|
|
||||||
bool eq(const GtidTriplet& rhs) const;
|
|
||||||
|
|
||||||
std::string to_string() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Comparator, used when sorting by domain id.
|
|
||||||
*
|
|
||||||
* @param triplet1 Left side
|
|
||||||
* @param triplet2 Right side
|
|
||||||
* @return True if left < right
|
|
||||||
*/
|
|
||||||
static bool compare_domains(const GtidTriplet& triplet1, const GtidTriplet& triplet2)
|
|
||||||
{
|
|
||||||
return triplet1.domain < triplet2.domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void parse_triplet(const char* str);
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bool operator == (const GtidTriplet& lhs, const GtidTriplet& rhs)
|
|
||||||
{
|
|
||||||
return lhs.eq(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
class Gtid
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum substraction_mode_t
|
|
||||||
{
|
|
||||||
MISSING_DOMAIN_IGNORE,
|
|
||||||
MISSING_DOMAIN_LHS_ADD
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the gtid string and return an object. Orders the triplets by domain id.
|
|
||||||
*
|
|
||||||
* @param gtid_string gtid as given by server. String must not be empty.
|
|
||||||
* @return The parsed (possibly multidomain) gtid. In case of error, the gtid will be empty.
|
|
||||||
*/
|
|
||||||
static Gtid from_string(const std::string& gtid_string);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a string version of the gtid.
|
|
||||||
*
|
|
||||||
* @return A string similar in form to how the server displays it
|
|
||||||
*/
|
|
||||||
std::string to_string() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a server with this gtid can replicate from a master with a given gtid. Only considers
|
|
||||||
* gtid:s and only detects obvious errors. The non-detected errors will mostly be detected once
|
|
||||||
* the slave tries to start replicating.
|
|
||||||
*
|
|
||||||
* TODO: Add support for Replicate_Do/Ignore_Id:s
|
|
||||||
*
|
|
||||||
* @param master_gtid Master server gtid
|
|
||||||
* @return True if replication looks possible
|
|
||||||
*/
|
|
||||||
bool can_replicate_from(const Gtid& master_gtid);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is the gtid empty.
|
|
||||||
*
|
|
||||||
* @return True if gtid has 0 triplets
|
|
||||||
*/
|
|
||||||
bool empty() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Full comparison.
|
|
||||||
*
|
|
||||||
* @param rhs Other gtid
|
|
||||||
* @return True if both gtid:s have identical triplets or both are empty
|
|
||||||
*/
|
|
||||||
bool operator == (const Gtid& rhs) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the number of events between two gtid:s with possibly multiple triplets. The
|
|
||||||
* result is always 0 or greater: if a sequence number of a domain on rhs is greater than on the same
|
|
||||||
* domain on lhs, the sequences are considered identical. Missing domains are handled depending on the
|
|
||||||
* value of @c domain_substraction_mode.
|
|
||||||
*
|
|
||||||
* @param lhs The value substracted from
|
|
||||||
* @param io_pos The value doing the substracting
|
|
||||||
* @param domain_substraction_mode How domains that exist on one side but not the other are handled. If
|
|
||||||
* MISSING_DOMAIN_IGNORE, these are simply ignored. If MISSING_DOMAIN_LHS_ADD, the sequence number on lhs
|
|
||||||
* is added to the total difference.
|
|
||||||
* @return The number of events between the two gtid:s
|
|
||||||
*/
|
|
||||||
static uint64_t events_ahead(const Gtid& lhs, const Gtid& rhs,
|
|
||||||
substraction_mode_t domain_substraction_mode);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a MASTER_GTID_WAIT()-query to this gtid.
|
|
||||||
*
|
|
||||||
* @param timeout Maximum wait time in seconds
|
|
||||||
* @return The query
|
|
||||||
*/
|
|
||||||
std::string generate_master_gtid_wait_cmd(double timeout) const;
|
|
||||||
|
|
||||||
GtidTriplet get_triplet(uint32_t domain) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<GtidTriplet> m_triplets;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for simplifying working with resultsets. Used in MariaDBServer.
|
* Helper class for simplifying working with resultsets. Used in MariaDBServer.
|
||||||
*/
|
*/
|
||||||
@ -278,16 +138,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool get_bool(int64_t column_ind) const;
|
bool get_bool(int64_t column_ind) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a gtid values from the current row and given column. If the field is empty, will return an invalid
|
|
||||||
* gtid.
|
|
||||||
*
|
|
||||||
* @param column_ind Column index
|
|
||||||
* @param gtid_domain Which gtid domain to parse
|
|
||||||
* @return Value as a gtid.
|
|
||||||
*/
|
|
||||||
GtidTriplet get_gtid(int64_t column_ind, int64_t gtid_domain) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MYSQL_RES* m_resultset; // Underlying result set, freed at dtor.
|
MYSQL_RES* m_resultset; // Underlying result set, freed at dtor.
|
||||||
std::tr1::unordered_map<string, int64_t> m_col_indexes; // Map of column name -> index
|
std::tr1::unordered_map<string, int64_t> m_col_indexes; // Map of column name -> index
|
||||||
|
Reference in New Issue
Block a user