
Refactoring continues. This update moves some of the replication manipulation functions to a separate file and turns them into class methods.
191 lines
6.7 KiB
C++
191 lines
6.7 KiB
C++
#include "mariadbmon.hh"
|
|
|
|
#include <sstream>
|
|
#include <maxscale/mysql_utils.h>
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
/**
|
|
* Generate a CHANGE MASTER TO-query.
|
|
*
|
|
* @param master_host Master hostname/address
|
|
* @param master_port Master port
|
|
* @return Generated query
|
|
*/
|
|
string MariaDBMonitor::generate_change_master_cmd(const string& master_host, int master_port)
|
|
{
|
|
std::stringstream change_cmd;
|
|
change_cmd << "CHANGE MASTER TO MASTER_HOST = '" << master_host << "', ";
|
|
change_cmd << "MASTER_PORT = " << master_port << ", ";
|
|
change_cmd << "MASTER_USE_GTID = current_pos, ";
|
|
change_cmd << "MASTER_USER = '" << m_replication_user << "', ";
|
|
const char MASTER_PW[] = "MASTER_PASSWORD = '";
|
|
const char END[] = "';";
|
|
#if defined(SS_DEBUG)
|
|
std::stringstream change_cmd_nopw;
|
|
change_cmd_nopw << change_cmd.str();
|
|
change_cmd_nopw << MASTER_PW << "******" << END;;
|
|
MXS_DEBUG("Change master command is '%s'.", change_cmd_nopw.str().c_str());
|
|
#endif
|
|
change_cmd << MASTER_PW << m_replication_password << END;
|
|
return change_cmd.str();
|
|
}
|
|
|
|
/**
|
|
* Redirects slaves to replicate from another master server.
|
|
*
|
|
* @param slaves An array of slaves
|
|
* @param new_master The replication master
|
|
* @param redirected_slaves A vector where to insert successfully redirected slaves.
|
|
* @return The number of slaves successfully redirected.
|
|
*/
|
|
int MariaDBMonitor::redirect_slaves(MXS_MONITORED_SERVER* new_master, const ServerVector& slaves,
|
|
ServerVector* redirected_slaves)
|
|
{
|
|
ss_dassert(redirected_slaves != NULL);
|
|
MXS_NOTICE("Redirecting slaves to new master.");
|
|
string change_cmd = generate_change_master_cmd(new_master->server->name, new_master->server->port);
|
|
int successes = 0;
|
|
for (ServerVector::const_iterator iter = slaves.begin(); iter != slaves.end(); iter++)
|
|
{
|
|
if (redirect_one_slave(*iter, change_cmd.c_str()))
|
|
{
|
|
successes++;
|
|
redirected_slaves->push_back(*iter);
|
|
}
|
|
}
|
|
return successes;
|
|
}
|
|
|
|
/**
|
|
* Set the new master to replicate from the cluster external master.
|
|
*
|
|
* @param new_master The server being promoted
|
|
* @param err_out Error output
|
|
* @return True if new master accepted commands
|
|
*/
|
|
bool MariaDBMonitor::start_external_replication(MXS_MONITORED_SERVER* new_master, json_t** err_out)
|
|
{
|
|
bool rval = false;
|
|
string change_cmd = generate_change_master_cmd(external_master_host, external_master_port);
|
|
if (mxs_mysql_query(new_master->con, change_cmd.c_str()) == 0 &&
|
|
mxs_mysql_query(new_master->con, "START SLAVE;") == 0)
|
|
{
|
|
MXS_NOTICE("New master starting replication from external master %s:%d.",
|
|
external_master_host.c_str(), external_master_port);
|
|
rval = true;
|
|
}
|
|
else
|
|
{
|
|
PRINT_MXS_JSON_ERROR(err_out, "Could not start replication from external master: '%s'.",
|
|
mysql_error(new_master->con));
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
/**
|
|
* Starts a new slave connection on a server. Should be used on a demoted master server.
|
|
*
|
|
* @param old_master The server which will start replication
|
|
* @param new_master Replication target
|
|
* @return True if commands were accepted. This does not guarantee that replication proceeds
|
|
* successfully.
|
|
*/
|
|
bool MariaDBMonitor::switchover_start_slave(MXS_MONITORED_SERVER* old_master, SERVER* new_master)
|
|
{
|
|
bool rval = false;
|
|
string change_cmd = generate_change_master_cmd(new_master->name, new_master->port);
|
|
if (mxs_mysql_query(old_master->con, change_cmd.c_str()) == 0 &&
|
|
mxs_mysql_query(old_master->con, "START SLAVE;") == 0)
|
|
{
|
|
MXS_NOTICE("Old master '%s' starting replication from '%s'.",
|
|
old_master->server->unique_name, new_master->unique_name);
|
|
rval = true;
|
|
}
|
|
else
|
|
{
|
|
MXS_ERROR("Old master '%s' could not start replication: '%s'.",
|
|
old_master->server->unique_name, mysql_error(old_master->con));
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
/**
|
|
* Redirect one slave server to another master
|
|
*
|
|
* @param slave Server to redirect
|
|
* @param change_cmd Change master command, usually generated by generate_change_master_cmd()
|
|
* @return True if slave accepted all commands
|
|
*/
|
|
bool MariaDBMonitor::redirect_one_slave(MXS_MONITORED_SERVER* slave, const char* change_cmd)
|
|
{
|
|
bool rval = false;
|
|
if (mxs_mysql_query(slave->con, "STOP SLAVE;") == 0 &&
|
|
mxs_mysql_query(slave->con, "RESET SLAVE;") == 0 && // To erase any old I/O or SQL errors
|
|
mxs_mysql_query(slave->con, change_cmd) == 0 &&
|
|
mxs_mysql_query(slave->con, "START SLAVE;") == 0)
|
|
{
|
|
rval = true;
|
|
MXS_NOTICE("Slave '%s' redirected to new master.", slave->server->unique_name);
|
|
}
|
|
else
|
|
{
|
|
MXS_WARNING("Slave '%s' redirection failed: '%s'.", slave->server->unique_name,
|
|
mysql_error(slave->con));
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
uint32_t MariaDBMonitor::do_rejoin(const ServerVector& joinable_servers)
|
|
{
|
|
SERVER* master = this->master->server;
|
|
uint32_t servers_joined = 0;
|
|
if (!joinable_servers.empty())
|
|
{
|
|
string change_cmd = generate_change_master_cmd(master->name, master->port);
|
|
for (ServerVector::const_iterator iter = joinable_servers.begin();
|
|
iter != joinable_servers.end();
|
|
iter++)
|
|
{
|
|
MXS_MONITORED_SERVER* joinable = *iter;
|
|
const char* name = joinable->server->unique_name;
|
|
const char* master_name = master->unique_name;
|
|
MySqlServerInfo* redir_info = get_server_info(this, joinable);
|
|
|
|
bool op_success;
|
|
if (redir_info->n_slaves_configured == 0)
|
|
{
|
|
MXS_NOTICE("Directing standalone server '%s' to replicate from '%s'.", name, master_name);
|
|
op_success = join_cluster(joinable, change_cmd.c_str());
|
|
}
|
|
else
|
|
{
|
|
MXS_NOTICE("Server '%s' is replicating from a server other than '%s', "
|
|
"redirecting it to '%s'.", name, master_name, master_name);
|
|
op_success = redirect_one_slave(joinable, change_cmd.c_str());
|
|
}
|
|
|
|
if (op_success)
|
|
{
|
|
servers_joined++;
|
|
}
|
|
}
|
|
}
|
|
return servers_joined;
|
|
}
|
|
|
|
bool MariaDBMonitor::cluster_can_be_joined()
|
|
{
|
|
return (master != NULL && SERVER_IS_MASTER(master->server) && master_gtid_domain >= 0);
|
|
} |