Reorganize MariaDBServer code
The server-class keeps growing, so the additional classes are moved out of the main class 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
|
||||||
mariadbmon_common.cc gtid.cc)
|
mariadbmon_common.cc server_utils.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")
|
||||||
|
@ -1,155 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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: 2022-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.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include "mariadbmon_common.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 this GtidList is ahead of the given GtidList. The
|
|
||||||
* result is always 0 or greater: if a sequence number of a domain on rhs is greater than on the same
|
|
||||||
* domain on the calling GtidList, the sequences are considered identical. Missing domains are
|
|
||||||
* handled depending on the value of @c domain_substraction_mode.
|
|
||||||
*
|
|
||||||
* @param rhs The value doing the substracting
|
|
||||||
* @param domain_substraction_mode How domains that exist on the caller but not on @c rhs 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
|
|
||||||
*/
|
|
||||||
uint64_t events_ahead(const GtidList& rhs, substraction_mode_t domain_substraction_mode) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an individual gtid with the given domain.
|
|
||||||
*
|
|
||||||
* @param domain Which domain to search for
|
|
||||||
* @return The gtid within the list. If domain is not found, an invalid gtid is returned.
|
|
||||||
*/
|
|
||||||
Gtid get_gtid(uint32_t domain) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Gtid> m_triplets;
|
|
||||||
};
|
|
@ -33,6 +33,13 @@ typedef std::unordered_map<int64_t, MariaDBServer*> IdToServerMap;
|
|||||||
// Map of cycle number to cycle members. The elements should be in order for predictability when iterating.
|
// Map of cycle number to cycle members. The elements should be in order for predictability when iterating.
|
||||||
typedef std::map<int, ServerArray> CycleMap;
|
typedef std::map<int, ServerArray> CycleMap;
|
||||||
|
|
||||||
|
// Some methods need a log on/off setting.
|
||||||
|
enum class Log
|
||||||
|
{
|
||||||
|
OFF,
|
||||||
|
ON
|
||||||
|
};
|
||||||
|
|
||||||
// MariaDB Monitor instance data
|
// MariaDB Monitor instance data
|
||||||
class MariaDBMonitor : public maxscale::MonitorInstance
|
class MariaDBMonitor : public maxscale::MonitorInstance
|
||||||
{
|
{
|
||||||
|
@ -32,23 +32,3 @@ void DelimitedPrinter::cat(string& target, const string& addition)
|
|||||||
target += m_current_separator + addition;
|
target += m_current_separator + addition;
|
||||||
m_current_separator = m_separator;
|
m_current_separator = m_separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClusterOperation::ClusterOperation(OperationType type,
|
|
||||||
MariaDBServer* promotion_target, MariaDBServer* demotion_target,
|
|
||||||
bool demo_target_is_master, bool handle_events,
|
|
||||||
string& promotion_sql_file, string& demotion_sql_file,
|
|
||||||
string& replication_user, string& replication_password,
|
|
||||||
json_t** error, maxbase::Duration time_remaining)
|
|
||||||
: type(type)
|
|
||||||
, promotion_target(promotion_target)
|
|
||||||
, demotion_target(demotion_target)
|
|
||||||
, demotion_target_is_master(demo_target_is_master)
|
|
||||||
, handle_events(handle_events)
|
|
||||||
, promotion_sql_file(promotion_sql_file)
|
|
||||||
, demotion_sql_file(demotion_sql_file)
|
|
||||||
, replication_user(replication_user)
|
|
||||||
, replication_password(replication_password)
|
|
||||||
, error_out(error)
|
|
||||||
, time_remaining(time_remaining)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <maxscale/json_api.h>
|
#include <maxscale/json_api.h>
|
||||||
#include <maxbase/stopwatch.hh>
|
|
||||||
|
|
||||||
/** Utility macros for printing both MXS_ERROR and json error */
|
/** Utility macros for printing both MXS_ERROR and json error */
|
||||||
#define PRINT_MXS_JSON_ERROR(err_out, format, ...) \
|
#define PRINT_MXS_JSON_ERROR(err_out, format, ...) \
|
||||||
@ -66,49 +65,3 @@ private:
|
|||||||
const std::string m_separator;
|
const std::string m_separator;
|
||||||
std::string m_current_separator;
|
std::string m_current_separator;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Log
|
|
||||||
{
|
|
||||||
OFF,
|
|
||||||
ON
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class OperationType
|
|
||||||
{
|
|
||||||
SWITCHOVER,
|
|
||||||
FAILOVER
|
|
||||||
};
|
|
||||||
|
|
||||||
class MariaDBServer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class which encapsulates many settings and status descriptors for a failover/switchover.
|
|
||||||
* Is more convenient to pass around than the separate elements. Most fields are constants or constant
|
|
||||||
* pointers since they should not change during an operation.
|
|
||||||
*/
|
|
||||||
class ClusterOperation
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
ClusterOperation(const ClusterOperation&) = delete;
|
|
||||||
ClusterOperation& operator=(const ClusterOperation&) = delete;
|
|
||||||
|
|
||||||
public:
|
|
||||||
const OperationType type; // Failover or switchover
|
|
||||||
MariaDBServer* const promotion_target; // Which server will be promoted
|
|
||||||
MariaDBServer* const demotion_target; // Which server will be demoted
|
|
||||||
const bool demotion_target_is_master; // Was the demotion target the master?
|
|
||||||
const bool handle_events; // Should scheduled server events be disabled/enabled?
|
|
||||||
const std::string promotion_sql_file; // SQL commands ran on a server promoted to master
|
|
||||||
const std::string demotion_sql_file; // SQL commands ran on a server demoted from master
|
|
||||||
const std::string replication_user; // User for CHANGE MASTER TO ...
|
|
||||||
const std::string replication_password; // Password for CHANGE MASTER TO ...
|
|
||||||
json_t** const error_out; // Json error output
|
|
||||||
maxbase::Duration time_remaining; // How much time remains to complete the operation
|
|
||||||
|
|
||||||
ClusterOperation(OperationType type,
|
|
||||||
MariaDBServer* promotion_target, MariaDBServer* demotion_target,
|
|
||||||
bool demo_target_is_master, bool handle_events,
|
|
||||||
std::string& promotion_sql_file, std::string& demotion_sql_file,
|
|
||||||
std::string& replication_user, std::string& replication_password,
|
|
||||||
json_t** error, maxbase::Duration time_remaining);
|
|
||||||
};
|
|
||||||
|
@ -26,15 +26,6 @@ using maxscale::string_printf;
|
|||||||
using maxbase::Duration;
|
using maxbase::Duration;
|
||||||
using maxbase::StopWatch;
|
using maxbase::StopWatch;
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// Used for Slave_IO_Running
|
|
||||||
const char YES[] = "Yes";
|
|
||||||
const char PREPARING[] = "Preparing";
|
|
||||||
const char CONNECTING[] = "Connecting";
|
|
||||||
const char NO[] = "No";
|
|
||||||
}
|
|
||||||
|
|
||||||
class MariaDBServer::EventInfo
|
class MariaDBServer::EventInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -2046,190 +2037,3 @@ bool MariaDBServer::redirect_existing_slave_conn(ClusterOperation& op)
|
|||||||
} // 'stop_slave_conn' prints its own errors
|
} // 'stop_slave_conn' prints its own errors
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
string SlaveStatus::to_string() const
|
|
||||||
{
|
|
||||||
// Print all of this on the same line to make things compact. Are the widths reasonable? The format is
|
|
||||||
// not quite array-like since usually there is just one row. May be changed later.
|
|
||||||
// Form the components of the line.
|
|
||||||
string host_port = string_printf("[%s]:%d", master_host.c_str(), master_port);
|
|
||||||
string running_states = string_printf("%s/%s",
|
|
||||||
slave_io_to_string(slave_io_running).c_str(),
|
|
||||||
slave_sql_running ? "Yes" : "No");
|
|
||||||
|
|
||||||
string rval = string_printf(
|
|
||||||
" Host: %22s, IO/SQL running: %7s, Master ID: %4" PRId64 ", Gtid_IO_Pos: %s, R.Lag: %d",
|
|
||||||
host_port.c_str(),
|
|
||||||
running_states.c_str(),
|
|
||||||
master_server_id,
|
|
||||||
gtid_io_pos.to_string().c_str(),
|
|
||||||
seconds_behind_master);
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
string SlaveStatus::to_short_string(const string& owner) const
|
|
||||||
{
|
|
||||||
if (name.empty())
|
|
||||||
{
|
|
||||||
return string_printf("Slave connection from %s to [%s]:%i",
|
|
||||||
owner.c_str(), master_host.c_str(), master_port);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return string_printf("Slave connection '%s' from %s to [%s]:%i",
|
|
||||||
name.c_str(), owner.c_str(), master_host.c_str(), master_port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
json_t* SlaveStatus::to_json() const
|
|
||||||
{
|
|
||||||
json_t* result = json_object();
|
|
||||||
json_object_set_new(result, "connection_name", json_string(name.c_str()));
|
|
||||||
json_object_set_new(result, "master_host", json_string(master_host.c_str()));
|
|
||||||
json_object_set_new(result, "master_port", json_integer(master_port));
|
|
||||||
json_object_set_new(result,
|
|
||||||
"slave_io_running",
|
|
||||||
json_string(slave_io_to_string(slave_io_running).c_str()));
|
|
||||||
json_object_set_new(result, "slave_sql_running", json_string(slave_sql_running ? "Yes" : "No"));
|
|
||||||
json_object_set_new(result,
|
|
||||||
"seconds_behing_master",
|
|
||||||
seconds_behind_master == MXS_RLAG_UNDEFINED ? json_null() :
|
|
||||||
json_integer(seconds_behind_master));
|
|
||||||
json_object_set_new(result, "master_server_id", json_integer(master_server_id));
|
|
||||||
json_object_set_new(result, "last_io_or_sql_error", json_string(last_error.c_str()));
|
|
||||||
json_object_set_new(result, "gtid_io_pos", json_string(gtid_io_pos.to_string().c_str()));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
SlaveStatus::slave_io_running_t SlaveStatus::slave_io_from_string(const std::string& str)
|
|
||||||
{
|
|
||||||
slave_io_running_t rval = SLAVE_IO_NO;
|
|
||||||
if (str == YES)
|
|
||||||
{
|
|
||||||
rval = SLAVE_IO_YES;
|
|
||||||
}
|
|
||||||
// Interpret "Preparing" as "Connecting". It's not quite clear if the master server id has been read
|
|
||||||
// or if server versions between master and slave have been checked, so better be on the safe side.
|
|
||||||
else if (str == CONNECTING || str == PREPARING)
|
|
||||||
{
|
|
||||||
rval = SLAVE_IO_CONNECTING;
|
|
||||||
}
|
|
||||||
else if (str != NO)
|
|
||||||
{
|
|
||||||
MXS_ERROR("Unexpected value for Slave_IO_Running: '%s'.", str.c_str());
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
string SlaveStatus::slave_io_to_string(SlaveStatus::slave_io_running_t slave_io)
|
|
||||||
{
|
|
||||||
string rval;
|
|
||||||
switch (slave_io)
|
|
||||||
{
|
|
||||||
case SlaveStatus::SLAVE_IO_YES:
|
|
||||||
rval = YES;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SlaveStatus::SLAVE_IO_CONNECTING:
|
|
||||||
rval = CONNECTING;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SlaveStatus::SLAVE_IO_NO:
|
|
||||||
rval = NO;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
mxb_assert(!false);
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryResult::QueryResult(MYSQL_RES* resultset)
|
|
||||||
: m_resultset(resultset)
|
|
||||||
{
|
|
||||||
if (m_resultset)
|
|
||||||
{
|
|
||||||
auto columns = mysql_num_fields(m_resultset);
|
|
||||||
MYSQL_FIELD* field_info = mysql_fetch_fields(m_resultset);
|
|
||||||
for (int64_t column_index = 0; column_index < columns; column_index++)
|
|
||||||
{
|
|
||||||
string key(field_info[column_index].name);
|
|
||||||
// TODO: Think of a way to handle duplicate names nicely. Currently this should only be used
|
|
||||||
// for known queries.
|
|
||||||
mxb_assert(m_col_indexes.count(key) == 0);
|
|
||||||
m_col_indexes[key] = column_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryResult::~QueryResult()
|
|
||||||
{
|
|
||||||
if (m_resultset)
|
|
||||||
{
|
|
||||||
mysql_free_result(m_resultset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QueryResult::next_row()
|
|
||||||
{
|
|
||||||
mxb_assert(m_resultset);
|
|
||||||
m_rowdata = mysql_fetch_row(m_resultset);
|
|
||||||
if (m_rowdata)
|
|
||||||
{
|
|
||||||
m_current_row_ind++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t QueryResult::get_current_row_index() const
|
|
||||||
{
|
|
||||||
return m_current_row_ind;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t QueryResult::get_col_count() const
|
|
||||||
{
|
|
||||||
return m_resultset ? mysql_num_fields(m_resultset) : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t QueryResult::get_row_count() const
|
|
||||||
{
|
|
||||||
return m_resultset ? mysql_num_rows(m_resultset) : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t QueryResult::get_col_index(const string& col_name) const
|
|
||||||
{
|
|
||||||
auto iter = m_col_indexes.find(col_name);
|
|
||||||
return (iter != m_col_indexes.end()) ? iter->second : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
string QueryResult::get_string(int64_t column_ind) const
|
|
||||||
{
|
|
||||||
mxb_assert(column_ind < get_col_count() && column_ind >= 0);
|
|
||||||
char* data = m_rowdata[column_ind];
|
|
||||||
return data ? data : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t QueryResult::get_uint(int64_t column_ind) const
|
|
||||||
{
|
|
||||||
mxb_assert(column_ind < get_col_count() && column_ind >= 0);
|
|
||||||
char* data = m_rowdata[column_ind];
|
|
||||||
int64_t rval = -1;
|
|
||||||
if (data && *data)
|
|
||||||
{
|
|
||||||
errno = 0; // strtoll sets this
|
|
||||||
char* endptr = NULL;
|
|
||||||
auto parsed = strtoll(data, &endptr, 10);
|
|
||||||
if (parsed >= 0 && errno == 0 && *endptr == '\0')
|
|
||||||
{
|
|
||||||
rval = parsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QueryResult::get_bool(int64_t column_ind) const
|
|
||||||
{
|
|
||||||
mxb_assert(column_ind < get_col_count() && column_ind >= 0);
|
|
||||||
char* data = m_rowdata[column_ind];
|
|
||||||
return data ? (strcmp(data, "Y") == 0 || strcmp(data, "1") == 0) : false;
|
|
||||||
}
|
|
||||||
|
@ -17,56 +17,13 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <maxscale/monitor.h>
|
#include <maxscale/monitor.h>
|
||||||
#include <maxbase/stopwatch.hh>
|
#include <maxbase/stopwatch.hh>
|
||||||
#include "gtid.hh"
|
#include "server_utils.hh"
|
||||||
|
|
||||||
class QueryResult;
|
class QueryResult;
|
||||||
class MariaDBServer;
|
class MariaDBServer;
|
||||||
// Server pointer array
|
// Server pointer array
|
||||||
typedef std::vector<MariaDBServer*> ServerArray;
|
typedef std::vector<MariaDBServer*> ServerArray;
|
||||||
|
|
||||||
// Contains data returned by one row of SHOW ALL SLAVES STATUS
|
|
||||||
class SlaveStatus
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum slave_io_running_t
|
|
||||||
{
|
|
||||||
SLAVE_IO_YES,
|
|
||||||
SLAVE_IO_CONNECTING,
|
|
||||||
SLAVE_IO_NO,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool exists = true; /* Has this connection been removed from the
|
|
||||||
* server but the monitor hasn't updated yet? */
|
|
||||||
bool seen_connected = false; /* Has this slave connection been seen connected,
|
|
||||||
* meaning that the master server id is correct?
|
|
||||||
**/
|
|
||||||
std::string name; /* Slave connection name. Must be unique for
|
|
||||||
* the server.*/
|
|
||||||
int64_t master_server_id = SERVER_ID_UNKNOWN; /* The master's server_id value. Valid ids are
|
|
||||||
* 32bit unsigned. -1 is unread/error. */
|
|
||||||
std::string master_host; /* Master server host name. */
|
|
||||||
int master_port = PORT_UNKNOWN; /* Master server port. */
|
|
||||||
slave_io_running_t slave_io_running = SLAVE_IO_NO; /* Slave I/O thread running state: * "Yes",
|
|
||||||
* "Connecting" or "No" */
|
|
||||||
bool slave_sql_running = false; /* Slave SQL thread running state, true if "Yes"
|
|
||||||
* */
|
|
||||||
GtidList gtid_io_pos; /* Gtid I/O position of the slave thread. */
|
|
||||||
std::string last_error; /* Last IO or SQL error encountered. */
|
|
||||||
int seconds_behind_master = MXS_RLAG_UNDEFINED; /* How much behind the slave is. */
|
|
||||||
int64_t received_heartbeats = 0; /* How many heartbeats the connection has received
|
|
||||||
* */
|
|
||||||
|
|
||||||
/* Time of the latest gtid event or heartbeat the slave connection has received, timed by the monitor. */
|
|
||||||
maxbase::Clock::time_point last_data_time = maxbase::Clock::now();
|
|
||||||
|
|
||||||
|
|
||||||
std::string to_string() const;
|
|
||||||
json_t* to_json() const;
|
|
||||||
std::string to_short_string(const std::string& owner) const;
|
|
||||||
static slave_io_running_t slave_io_from_string(const std::string& str);
|
|
||||||
static std::string slave_io_to_string(slave_io_running_t slave_io);
|
|
||||||
};
|
|
||||||
|
|
||||||
// This class groups some miscellaneous replication related settings together.
|
// This class groups some miscellaneous replication related settings together.
|
||||||
class ReplicationSettings
|
class ReplicationSettings
|
||||||
{
|
{
|
||||||
@ -572,84 +529,3 @@ private:
|
|||||||
SlaveStatus* slave_connection_status_mutable(const MariaDBServer* target);
|
SlaveStatus* slave_connection_status_mutable(const MariaDBServer* target);
|
||||||
std::string generate_change_master_cmd(ClusterOperation& op, const SlaveStatus& slave_conn);
|
std::string generate_change_master_cmd(ClusterOperation& op, const SlaveStatus& slave_conn);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class for simplifying working with resultsets. Used in MariaDBServer.
|
|
||||||
*/
|
|
||||||
class QueryResult
|
|
||||||
{
|
|
||||||
// These need to be banned to avoid premature destruction.
|
|
||||||
QueryResult(const QueryResult&) = delete;
|
|
||||||
QueryResult& operator=(const QueryResult&) = delete;
|
|
||||||
|
|
||||||
public:
|
|
||||||
QueryResult(MYSQL_RES* resultset = NULL);
|
|
||||||
~QueryResult();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Advance to next row. Affects all result returning functions.
|
|
||||||
*
|
|
||||||
* @return True if the next row has data, false if the current row was the last one.
|
|
||||||
*/
|
|
||||||
bool next_row();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the index of the current row.
|
|
||||||
*
|
|
||||||
* @return Current row index, or -1 if no data or next_row() has not been called yet.
|
|
||||||
*/
|
|
||||||
int64_t get_current_row_index() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How many columns the result set has.
|
|
||||||
*
|
|
||||||
* @return Column count, or -1 if no data.
|
|
||||||
*/
|
|
||||||
int64_t get_col_count() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How many rows does the result set have?
|
|
||||||
*
|
|
||||||
* @return The number of rows or -1 on error
|
|
||||||
*/
|
|
||||||
int64_t get_row_count() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a numeric index for a column name. May give wrong results if column names are not unique.
|
|
||||||
*
|
|
||||||
* @param col_name Column name
|
|
||||||
* @return Index or -1 if not found.
|
|
||||||
*/
|
|
||||||
int64_t get_col_index(const std::string& col_name) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a string value from the current row and given column. Empty string and (null) are both interpreted
|
|
||||||
* as the empty string.
|
|
||||||
*
|
|
||||||
* @param column_ind Column index
|
|
||||||
* @return Value as string
|
|
||||||
*/
|
|
||||||
std::string get_string(int64_t column_ind) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a non-negative integer value from the current row and given column.
|
|
||||||
*
|
|
||||||
* @param column_ind Column index
|
|
||||||
* @return Value as integer. 0 or greater indicates success, -1 is returned on error.
|
|
||||||
*/
|
|
||||||
int64_t get_uint(int64_t column_ind) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a boolean value from the current row and given column.
|
|
||||||
*
|
|
||||||
* @param column_ind Column index
|
|
||||||
* @return Value as boolean. Returns true if the text is either 'Y' or '1'.
|
|
||||||
*/
|
|
||||||
bool get_bool(int64_t column_ind) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
MYSQL_RES* m_resultset = NULL; // Underlying result set, freed at dtor.
|
|
||||||
std::unordered_map<std::string, int64_t> m_col_indexes; // Map of column name -> index
|
|
||||||
MYSQL_ROW m_rowdata = NULL; // Data for current row
|
|
||||||
int64_t m_current_row_ind = -1;// Index of current row
|
|
||||||
};
|
|
||||||
|
@ -10,8 +10,7 @@
|
|||||||
* of this software will be governed by version 2 or later of the General
|
* of this software will be governed by version 2 or later of the General
|
||||||
* Public License.
|
* Public License.
|
||||||
*/
|
*/
|
||||||
|
#include "server_utils.hh"
|
||||||
#include "gtid.hh"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
@ -21,6 +20,130 @@
|
|||||||
using std::string;
|
using std::string;
|
||||||
using maxscale::string_printf;
|
using maxscale::string_printf;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Used for Slave_IO_Running
|
||||||
|
const char YES[] = "Yes";
|
||||||
|
const char PREPARING[] = "Preparing";
|
||||||
|
const char CONNECTING[] = "Connecting";
|
||||||
|
const char NO[] = "No";
|
||||||
|
}
|
||||||
|
|
||||||
|
string SlaveStatus::to_string() const
|
||||||
|
{
|
||||||
|
// Print all of this on the same line to make things compact. Are the widths reasonable? The format is
|
||||||
|
// not quite array-like since usually there is just one row. May be changed later.
|
||||||
|
// Form the components of the line.
|
||||||
|
string host_port = string_printf("[%s]:%d", master_host.c_str(), master_port);
|
||||||
|
string running_states = string_printf("%s/%s",
|
||||||
|
slave_io_to_string(slave_io_running).c_str(),
|
||||||
|
slave_sql_running ? "Yes" : "No");
|
||||||
|
|
||||||
|
string rval = string_printf(
|
||||||
|
" Host: %22s, IO/SQL running: %7s, Master ID: %4" PRId64 ", Gtid_IO_Pos: %s, R.Lag: %d",
|
||||||
|
host_port.c_str(),
|
||||||
|
running_states.c_str(),
|
||||||
|
master_server_id,
|
||||||
|
gtid_io_pos.to_string().c_str(),
|
||||||
|
seconds_behind_master);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
string SlaveStatus::to_short_string(const string& owner) const
|
||||||
|
{
|
||||||
|
if (name.empty())
|
||||||
|
{
|
||||||
|
return string_printf("Slave connection from %s to [%s]:%i",
|
||||||
|
owner.c_str(), master_host.c_str(), master_port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return string_printf("Slave connection '%s' from %s to [%s]:%i",
|
||||||
|
name.c_str(), owner.c_str(), master_host.c_str(), master_port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t* SlaveStatus::to_json() const
|
||||||
|
{
|
||||||
|
json_t* result = json_object();
|
||||||
|
json_object_set_new(result, "connection_name", json_string(name.c_str()));
|
||||||
|
json_object_set_new(result, "master_host", json_string(master_host.c_str()));
|
||||||
|
json_object_set_new(result, "master_port", json_integer(master_port));
|
||||||
|
json_object_set_new(result,
|
||||||
|
"slave_io_running",
|
||||||
|
json_string(slave_io_to_string(slave_io_running).c_str()));
|
||||||
|
json_object_set_new(result, "slave_sql_running", json_string(slave_sql_running ? "Yes" : "No"));
|
||||||
|
json_object_set_new(result,
|
||||||
|
"seconds_behing_master",
|
||||||
|
seconds_behind_master == MXS_RLAG_UNDEFINED ? json_null() :
|
||||||
|
json_integer(seconds_behind_master));
|
||||||
|
json_object_set_new(result, "master_server_id", json_integer(master_server_id));
|
||||||
|
json_object_set_new(result, "last_io_or_sql_error", json_string(last_error.c_str()));
|
||||||
|
json_object_set_new(result, "gtid_io_pos", json_string(gtid_io_pos.to_string().c_str()));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
SlaveStatus::slave_io_running_t SlaveStatus::slave_io_from_string(const std::string& str)
|
||||||
|
{
|
||||||
|
slave_io_running_t rval = SLAVE_IO_NO;
|
||||||
|
if (str == YES)
|
||||||
|
{
|
||||||
|
rval = SLAVE_IO_YES;
|
||||||
|
}
|
||||||
|
// Interpret "Preparing" as "Connecting". It's not quite clear if the master server id has been read
|
||||||
|
// or if server versions between master and slave have been checked, so better be on the safe side.
|
||||||
|
else if (str == CONNECTING || str == PREPARING)
|
||||||
|
{
|
||||||
|
rval = SLAVE_IO_CONNECTING;
|
||||||
|
}
|
||||||
|
else if (str != NO)
|
||||||
|
{
|
||||||
|
MXS_ERROR("Unexpected value for Slave_IO_Running: '%s'.", str.c_str());
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
string SlaveStatus::slave_io_to_string(SlaveStatus::slave_io_running_t slave_io)
|
||||||
|
{
|
||||||
|
string rval;
|
||||||
|
switch (slave_io)
|
||||||
|
{
|
||||||
|
case SlaveStatus::SLAVE_IO_YES:
|
||||||
|
rval = YES;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SlaveStatus::SLAVE_IO_CONNECTING:
|
||||||
|
rval = CONNECTING;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SlaveStatus::SLAVE_IO_NO:
|
||||||
|
rval = NO;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
mxb_assert(!false);
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClusterOperation::ClusterOperation(OperationType type,
|
||||||
|
MariaDBServer* promotion_target, MariaDBServer* demotion_target,
|
||||||
|
bool demo_target_is_master, bool handle_events,
|
||||||
|
string& promotion_sql_file, string& demotion_sql_file,
|
||||||
|
string& replication_user, string& replication_password,
|
||||||
|
json_t** error, maxbase::Duration time_remaining)
|
||||||
|
: type(type)
|
||||||
|
, promotion_target(promotion_target)
|
||||||
|
, demotion_target(demotion_target)
|
||||||
|
, demotion_target_is_master(demo_target_is_master)
|
||||||
|
, handle_events(handle_events)
|
||||||
|
, promotion_sql_file(promotion_sql_file)
|
||||||
|
, demotion_sql_file(demotion_sql_file)
|
||||||
|
, replication_user(replication_user)
|
||||||
|
, replication_password(replication_password)
|
||||||
|
, error_out(error)
|
||||||
|
, time_remaining(time_remaining)
|
||||||
|
{}
|
||||||
|
|
||||||
GtidList GtidList::from_string(const string& gtid_string)
|
GtidList GtidList::from_string(const string& gtid_string)
|
||||||
{
|
{
|
||||||
mxb_assert(gtid_string.size());
|
mxb_assert(gtid_string.size());
|
||||||
@ -251,3 +374,94 @@ Gtid GtidList::get_gtid(uint32_t domain) const
|
|||||||
}
|
}
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryResult::QueryResult(MYSQL_RES* resultset)
|
||||||
|
: m_resultset(resultset)
|
||||||
|
{
|
||||||
|
if (m_resultset)
|
||||||
|
{
|
||||||
|
auto columns = mysql_num_fields(m_resultset);
|
||||||
|
MYSQL_FIELD* field_info = mysql_fetch_fields(m_resultset);
|
||||||
|
for (int64_t column_index = 0; column_index < columns; column_index++)
|
||||||
|
{
|
||||||
|
string key(field_info[column_index].name);
|
||||||
|
// TODO: Think of a way to handle duplicate names nicely. Currently this should only be used
|
||||||
|
// for known queries.
|
||||||
|
mxb_assert(m_col_indexes.count(key) == 0);
|
||||||
|
m_col_indexes[key] = column_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryResult::~QueryResult()
|
||||||
|
{
|
||||||
|
if (m_resultset)
|
||||||
|
{
|
||||||
|
mysql_free_result(m_resultset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QueryResult::next_row()
|
||||||
|
{
|
||||||
|
mxb_assert(m_resultset);
|
||||||
|
m_rowdata = mysql_fetch_row(m_resultset);
|
||||||
|
if (m_rowdata)
|
||||||
|
{
|
||||||
|
m_current_row_ind++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t QueryResult::get_current_row_index() const
|
||||||
|
{
|
||||||
|
return m_current_row_ind;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t QueryResult::get_col_count() const
|
||||||
|
{
|
||||||
|
return m_resultset ? mysql_num_fields(m_resultset) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t QueryResult::get_row_count() const
|
||||||
|
{
|
||||||
|
return m_resultset ? mysql_num_rows(m_resultset) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t QueryResult::get_col_index(const string& col_name) const
|
||||||
|
{
|
||||||
|
auto iter = m_col_indexes.find(col_name);
|
||||||
|
return (iter != m_col_indexes.end()) ? iter->second : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string QueryResult::get_string(int64_t column_ind) const
|
||||||
|
{
|
||||||
|
mxb_assert(column_ind < get_col_count() && column_ind >= 0);
|
||||||
|
char* data = m_rowdata[column_ind];
|
||||||
|
return data ? data : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t QueryResult::get_uint(int64_t column_ind) const
|
||||||
|
{
|
||||||
|
mxb_assert(column_ind < get_col_count() && column_ind >= 0);
|
||||||
|
char* data = m_rowdata[column_ind];
|
||||||
|
int64_t rval = -1;
|
||||||
|
if (data && *data)
|
||||||
|
{
|
||||||
|
errno = 0; // strtoll sets this
|
||||||
|
char* endptr = NULL;
|
||||||
|
auto parsed = strtoll(data, &endptr, 10);
|
||||||
|
if (parsed >= 0 && errno == 0 && *endptr == '\0')
|
||||||
|
{
|
||||||
|
rval = parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QueryResult::get_bool(int64_t column_ind) const
|
||||||
|
{
|
||||||
|
mxb_assert(column_ind < get_col_count() && column_ind >= 0);
|
||||||
|
char* data = m_rowdata[column_ind];
|
||||||
|
return data ? (strcmp(data, "Y") == 0 || strcmp(data, "1") == 0) : false;
|
||||||
|
}
|
322
server/modules/monitor/mariadbmon/server_utils.hh
Normal file
322
server/modules/monitor/mariadbmon/server_utils.hh
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
/*
|
||||||
|
* 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: 2022-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.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "mariadbmon_common.hh"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <maxbase/stopwatch.hh>
|
||||||
|
#include <maxscale/mysql_utils.h>
|
||||||
|
|
||||||
|
class MariaDBServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 this GtidList is ahead of the given GtidList. The
|
||||||
|
* result is always 0 or greater: if a sequence number of a domain on rhs is greater than on the same
|
||||||
|
* domain on the calling GtidList, the sequences are considered identical. Missing domains are
|
||||||
|
* handled depending on the value of @c domain_substraction_mode.
|
||||||
|
*
|
||||||
|
* @param rhs The value doing the substracting
|
||||||
|
* @param domain_substraction_mode How domains that exist on the caller but not on @c rhs 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
|
||||||
|
*/
|
||||||
|
uint64_t events_ahead(const GtidList& rhs, substraction_mode_t domain_substraction_mode) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an individual gtid with the given domain.
|
||||||
|
*
|
||||||
|
* @param domain Which domain to search for
|
||||||
|
* @return The gtid within the list. If domain is not found, an invalid gtid is returned.
|
||||||
|
*/
|
||||||
|
Gtid get_gtid(uint32_t domain) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Gtid> m_triplets;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Contains data returned by one row of SHOW ALL SLAVES STATUS
|
||||||
|
class SlaveStatus
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum slave_io_running_t
|
||||||
|
{
|
||||||
|
SLAVE_IO_YES,
|
||||||
|
SLAVE_IO_CONNECTING,
|
||||||
|
SLAVE_IO_NO,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool exists = true; /* Has this connection been removed from the
|
||||||
|
* server but the monitor hasn't updated yet? */
|
||||||
|
bool seen_connected = false; /* Has this slave connection been seen connected,
|
||||||
|
* meaning that the master server id is correct?
|
||||||
|
**/
|
||||||
|
std::string name; /* Slave connection name. Must be unique for
|
||||||
|
* the server.*/
|
||||||
|
int64_t master_server_id = SERVER_ID_UNKNOWN; /* The master's server_id value. Valid ids are
|
||||||
|
* 32bit unsigned. -1 is unread/error. */
|
||||||
|
std::string master_host; /* Master server host name. */
|
||||||
|
int master_port = PORT_UNKNOWN; /* Master server port. */
|
||||||
|
slave_io_running_t slave_io_running = SLAVE_IO_NO; /* Slave I/O thread running state: * "Yes",
|
||||||
|
* "Connecting" or "No" */
|
||||||
|
bool slave_sql_running = false; /* Slave SQL thread running state, true if "Yes"
|
||||||
|
* */
|
||||||
|
GtidList gtid_io_pos; /* Gtid I/O position of the slave thread. */
|
||||||
|
std::string last_error; /* Last IO or SQL error encountered. */
|
||||||
|
int seconds_behind_master = MXS_RLAG_UNDEFINED; /* How much behind the slave is. */
|
||||||
|
int64_t received_heartbeats = 0; /* How many heartbeats the connection has received
|
||||||
|
* */
|
||||||
|
|
||||||
|
/* Time of the latest gtid event or heartbeat the slave connection has received, timed by the monitor. */
|
||||||
|
maxbase::Clock::time_point last_data_time = maxbase::Clock::now();
|
||||||
|
|
||||||
|
|
||||||
|
std::string to_string() const;
|
||||||
|
json_t* to_json() const;
|
||||||
|
std::string to_short_string(const std::string& owner) const;
|
||||||
|
static slave_io_running_t slave_io_from_string(const std::string& str);
|
||||||
|
static std::string slave_io_to_string(slave_io_running_t slave_io);
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class OperationType
|
||||||
|
{
|
||||||
|
SWITCHOVER,
|
||||||
|
FAILOVER
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class which encapsulates many settings and status descriptors for a failover/switchover.
|
||||||
|
* Is more convenient to pass around than the separate elements. Most fields are constants or constant
|
||||||
|
* pointers since they should not change during an operation.
|
||||||
|
*/
|
||||||
|
class ClusterOperation
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
ClusterOperation(const ClusterOperation&) = delete;
|
||||||
|
ClusterOperation& operator=(const ClusterOperation&) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const OperationType type; // Failover or switchover
|
||||||
|
MariaDBServer* const promotion_target; // Which server will be promoted
|
||||||
|
MariaDBServer* const demotion_target; // Which server will be demoted
|
||||||
|
const bool demotion_target_is_master; // Was the demotion target the master?
|
||||||
|
const bool handle_events; // Should scheduled server events be disabled/enabled?
|
||||||
|
const std::string promotion_sql_file; // SQL commands ran on a server promoted to master
|
||||||
|
const std::string demotion_sql_file; // SQL commands ran on a server demoted from master
|
||||||
|
const std::string replication_user; // User for CHANGE MASTER TO ...
|
||||||
|
const std::string replication_password; // Password for CHANGE MASTER TO ...
|
||||||
|
json_t** const error_out; // Json error output
|
||||||
|
maxbase::Duration time_remaining; // How much time remains to complete the operation
|
||||||
|
|
||||||
|
ClusterOperation(OperationType type,
|
||||||
|
MariaDBServer* promotion_target, MariaDBServer* demotion_target,
|
||||||
|
bool demo_target_is_master, bool handle_events,
|
||||||
|
std::string& promotion_sql_file, std::string& demotion_sql_file,
|
||||||
|
std::string& replication_user, std::string& replication_password,
|
||||||
|
json_t** error, maxbase::Duration time_remaining);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for simplifying working with resultsets. Used in MariaDBServer.
|
||||||
|
*/
|
||||||
|
class QueryResult
|
||||||
|
{
|
||||||
|
// These need to be banned to avoid premature destruction.
|
||||||
|
QueryResult(const QueryResult&) = delete;
|
||||||
|
QueryResult& operator=(const QueryResult&) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
QueryResult(MYSQL_RES* resultset = NULL);
|
||||||
|
~QueryResult();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advance to next row. Affects all result returning functions.
|
||||||
|
*
|
||||||
|
* @return True if the next row has data, false if the current row was the last one.
|
||||||
|
*/
|
||||||
|
bool next_row();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index of the current row.
|
||||||
|
*
|
||||||
|
* @return Current row index, or -1 if no data or next_row() has not been called yet.
|
||||||
|
*/
|
||||||
|
int64_t get_current_row_index() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How many columns the result set has.
|
||||||
|
*
|
||||||
|
* @return Column count, or -1 if no data.
|
||||||
|
*/
|
||||||
|
int64_t get_col_count() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How many rows does the result set have?
|
||||||
|
*
|
||||||
|
* @return The number of rows or -1 on error
|
||||||
|
*/
|
||||||
|
int64_t get_row_count() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a numeric index for a column name. May give wrong results if column names are not unique.
|
||||||
|
*
|
||||||
|
* @param col_name Column name
|
||||||
|
* @return Index or -1 if not found.
|
||||||
|
*/
|
||||||
|
int64_t get_col_index(const std::string& col_name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a string value from the current row and given column. Empty string and (null) are both interpreted
|
||||||
|
* as the empty string.
|
||||||
|
*
|
||||||
|
* @param column_ind Column index
|
||||||
|
* @return Value as string
|
||||||
|
*/
|
||||||
|
std::string get_string(int64_t column_ind) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a non-negative integer value from the current row and given column.
|
||||||
|
*
|
||||||
|
* @param column_ind Column index
|
||||||
|
* @return Value as integer. 0 or greater indicates success, -1 is returned on error.
|
||||||
|
*/
|
||||||
|
int64_t get_uint(int64_t column_ind) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a boolean value from the current row and given column.
|
||||||
|
*
|
||||||
|
* @param column_ind Column index
|
||||||
|
* @return Value as boolean. Returns true if the text is either 'Y' or '1'.
|
||||||
|
*/
|
||||||
|
bool get_bool(int64_t column_ind) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MYSQL_RES* m_resultset = NULL; // Underlying result set, freed at dtor.
|
||||||
|
std::unordered_map<std::string, int64_t> m_col_indexes; // Map of column name -> index
|
||||||
|
MYSQL_ROW m_rowdata = NULL; // Data for current row
|
||||||
|
int64_t m_current_row_ind = -1;// Index of current row
|
||||||
|
};
|
@ -11,7 +11,7 @@
|
|||||||
* Public License.
|
* Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../gtid.hh"
|
#include "../server_utils.hh"
|
||||||
#include "maxbase/log.hh"
|
#include "maxbase/log.hh"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
Reference in New Issue
Block a user