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:
Esa Korhonen
2018-04-04 17:34:14 +03:00
parent e43678bed9
commit 174db469f3
9 changed files with 423 additions and 441 deletions

View File

@ -1,5 +1,5 @@
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)
add_dependencies(mariadbmon pcre2)
set_target_properties(mariadbmon PROPERTIES VERSION "1.4.0")

View File

@ -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());
thread_millisleep(1000); // Sleep for a while before querying server again.
// 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.
// It doesn't matter here, but is a general rule.
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.
* @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)
{
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.
* @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,
json_t** err_out)
{
@ -1075,7 +1075,7 @@ bool MariaDBMonitor::wait_cluster_stabilization(MXS_MONITORED_SERVER* new_master
int query_fails = 0;
int repl_fails = 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
bool first_round = true;
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);
repl_fails++;
}
else if (Gtid::events_ahead(target, slave_info->gtid_current_pos,
Gtid::MISSING_DOMAIN_IGNORE) == 0)
else if (GtidList::events_ahead(target, slave_info->gtid_current_pos,
GtidList::MISSING_DOMAIN_IGNORE) == 0)
{
// This slave has reached the same gtid as master, remove from list
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,
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_processed = candidate_info->gtid_current_pos.get_triplet(gtid_domain).sequence;
uint64_t curr_io = current_best_info->slave_status.gtid_io_pos.get_triplet(gtid_domain).sequence;
uint64_t curr_processed = current_best_info->gtid_current_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_gtid(gtid_domain).m_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_gtid(gtid_domain).m_sequence;
bool cand_updates = candidate_info->rpl_settings.log_slave_updates;
bool curr_updates = current_best_info->rpl_settings.log_slave_updates;

View 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;
}

View 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;
};

View File

@ -197,9 +197,9 @@ private:
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,
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);
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);
bool wait_cluster_stabilization(MXS_MONITORED_SERVER* new_master, const ServerVector& slaves,
int seconds_remaining);

View File

@ -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
* 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. */
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)
@ -199,11 +200,11 @@ bool MariaDBServer::do_show_slave_status()
if (!gtid_io_pos.empty() &&
(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
{
slave_status.gtid_io_pos = Gtid();
slave_status.gtid_io_pos = GtidList();
}
}
}
@ -242,21 +243,21 @@ bool MariaDBServer::update_gtids()
bool binlog_ok = false;
if (current_str.empty())
{
gtid_current_pos = Gtid();
gtid_current_pos = GtidList();
}
else
{
gtid_current_pos = Gtid::from_string(current_str);
gtid_current_pos = GtidList::from_string(current_str);
current_ok = !gtid_current_pos.empty();
}
if (binlog_str.empty())
{
gtid_binlog_pos = Gtid();
gtid_binlog_pos = GtidList();
}
else
{
gtid_binlog_pos = Gtid::from_string(binlog_str);
gtid_binlog_pos = GtidList::from_string(binlog_str);
binlog_ok = !gtid_binlog_pos.empty();
}

View File

@ -19,6 +19,7 @@
#include <memory>
#include <maxscale/monitor.h>
#include "utilities.hh"
#include "gtid.hh"
enum mysql_server_version
{
@ -47,7 +48,7 @@ public:
* reading from. */
uint64_t read_master_log_pos; /**< Position up to which the I/O thread has read in the current master
* 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. */
SlaveStatusInfo();
@ -91,8 +92,8 @@ public:
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
* new non-replicated events. */
Gtid gtid_current_pos; /**< Gtid of latest event. */
Gtid gtid_binlog_pos; /**< Gtid of latest event written to binlog. */
GtidList gtid_current_pos; /**< Gtid of latest event. */
GtidList gtid_binlog_pos; /**< Gtid of latest event written to binlog. */
SlaveStatusInfo slave_status; /**< Data returned from SHOW SLAVE STATUS */
ReplicationSettings rpl_settings; /**< Miscellaneous replication related settings */

View File

@ -13,13 +13,10 @@
#include "utilities.hh"
#include <algorithm>
#include <inttypes.h>
#include <limits>
#include <stdio.h>
#include <string>
#include <sstream>
#include <maxscale/dcb.h>
#include <maxscale/debug.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];
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;
}

View File

@ -16,7 +16,6 @@
#include <maxscale/cppdefs.hh>
#include <string>
#include <vector>
#include <maxscale/monitor.h>
/** 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);
/**
* 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.
*/
@ -278,16 +138,6 @@ public:
*/
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:
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