You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
proxysql/test/tap/tests/test_cluster_sync_mysql_ser...

1022 lines
46 KiB

/**
* @file test_cluster_sync_mysql_servers-t.cpp
* @brief Checks that ProxySQL mysql_server and mysql_server_v2 is properly syncing.
* @details Checks the sync of the following tables:
* - 'mysql_servers_v2'
* - 'mysql_servers'
*
*
* Test Cluster Isolation:
* ----------------------
* For guaranteeing that this test doesn't invalidate the configuration of a running ProxySQL cluster and
* that after the test, the previous valid configuration is restored, the following actions are performed:
*
* 1. The Core nodes from the current cluster configuration are backup.
* 2. Primary (currently tested instance) is removed from the Core nodes.
* 3. A sync wait until all core nodes have performed the removal of primary is executed.
* 4. Now Primary is isolated from the previous cluster, tests can proceed. Primary is setup to hold itself
* in its 'proxysql_servers' as well as the target spawned replica.
* 5. After the tests recover the primary configuration and add it back to the Core nodes from Cluster:
* - Recover the previous 'mysql_servers' from disk, and load them to runtime, discarding any previous
* config performed during the test.
* - Insert the primary back into a Core node from cluster and wait for all nodes to sync including it.
* - Insert into the primary the previous backup Core nodes from Cluster and load to runtime.
*/
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <time.h>
#include <atomic>
#include <vector>
#include <string>
#include <thread>
#include <iostream>
#include <functional>
#include <utility>
#include "libconfig.h"
#include "proxysql_utils.h"
#include "mysql.h"
#ifndef SPOOKYV2
#include "SpookyV2.h"
#define SPOOKYV2
#endif
#include "tap.h"
#include "command_line.h"
#include "utils.h"
using std::pair;
using std::string;
using std::vector;
#define MYSQL_QUERY__(mysql, query) \
do { \
if (mysql_query(mysql, query)) { \
fprintf(stderr, "File %s, line %d, Error: %s\n", \
__FILE__, __LINE__, mysql_error(mysql)); \
goto cleanup; \
} \
} while(0)
// GLOBAL TEST PARAMETERS
const uint32_t SYNC_TIMEOUT = 10;
const uint32_t CONNECT_TIMEOUT = 10;
const uint32_t R_NOMONITOR_PORT = 96061;
const uint32_t R_WITHMONITOR_PORT = 96062;
const std::string t_debug_query = "mysql -u%s -p%s -h %s -P%d -C -e \"%s\"";
using mysql_server_tuple = std::tuple<int,std::string,int,int,std::string,int,int,int,int,int,int,std::string>;
using replication_hostgroups_tuple = std::tuple<int,int,std::string>;
/**
* @brief Computes the checksum for the resultset, excluding records labeled as 'OFFLINE_HARD', instead of checking each row individually.
*
* @param resultset mysql_servers
*
*/
uint64_t mysql_servers_raw_checksum(MYSQL_RES* resultset) {
if (resultset == nullptr) { return 0; }
uint64_t num_rows = mysql_num_rows(resultset);
if (num_rows == 0) { return 0; }
MYSQL_FIELD* fields = mysql_fetch_fields(resultset);
uint32_t num_fields = mysql_num_fields(resultset);
uint32_t status_idx = 0;
for (uint32_t i = 0; i < num_fields; i++) {
if (strcmp(fields[i].name, "status") == 0) {
status_idx = i;
}
}
SpookyHash myhash {};
myhash.Init(19,3);
while (MYSQL_ROW row = mysql_fetch_row(resultset)) {
for (uint32_t i = 0; i < num_fields; i++) {
if (strcmp(row[status_idx], "OFFLINE_HARD") == 0) {
continue;
}
if (row[i]) {
if (strcmp(fields[i].name, "status") == 0) {
if (strcmp(row[i], "ONLINE") == 0 || strcmp(row[i], "SHUNNED") == 0) {
myhash.Update("0", strlen("0"));
} else {
myhash.Update("2", strlen("1"));
}
} else {
// computing 'strlen' is required see @details
myhash.Update(row[i], strlen(row[i]));
}
} else {
myhash.Update("", 0);
}
}
}
// restore the initial resulset index
mysql_data_seek(resultset, 0);
uint64_t res_hash = 0, hash2 = 0;
myhash.Final(&res_hash, &hash2);
return res_hash;
}
int insert_mysql_servers_records(MYSQL* proxy_admin, const std::vector<mysql_server_tuple>& insert_mysql_servers_values,
const std::vector<replication_hostgroups_tuple>& insert_replication_hostgroups_values) {
MYSQL_QUERY(proxy_admin, "DELETE FROM mysql_servers");
MYSQL_QUERY(proxy_admin, "DELETE FROM mysql_replication_hostgroups");
// Configure 'mysql_servers' and check sync with NULL comments
const char* t_insert_mysql_servers =
"INSERT INTO mysql_servers ("
" hostgroup_id, hostname, port, gtid_port, status, weight, compression, max_connections,"
" max_replication_lag, use_ssl, max_latency_ms, comment"
") VALUES (%d, '%s', %d, %d, '%s', %d, %d, %d, %d, %d, %d, '%s')";
const char* t_mysql_replication_hostgroups =
"INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type) VALUES (%d,%d,'%s')";
for (auto const& values : insert_mysql_servers_values) {
std::string insert_mysql_servers_hostgroup_query;
string_format(
t_insert_mysql_servers,
insert_mysql_servers_hostgroup_query,
std::get<0>(values),
std::get<1>(values).c_str(),
std::get<2>(values),
std::get<3>(values),
std::get<4>(values).c_str(),
std::get<5>(values),
std::get<6>(values),
std::get<7>(values),
std::get<8>(values),
std::get<9>(values),
std::get<10>(values),
std::get<11>(values).c_str()
);
// Insert the new mysql_servers hostgroups values
MYSQL_QUERY(proxy_admin, insert_mysql_servers_hostgroup_query.c_str());
}
for (auto const& values : insert_replication_hostgroups_values) {
std::string insert_mysql_replication_hostgroups_query;
string_format(
t_mysql_replication_hostgroups,
insert_mysql_replication_hostgroups_query,
std::get<0>(values),
std::get<1>(values),
std::get<2>(values).c_str()
);
// Insert the new mysql_replication_hostgroups values
MYSQL_QUERY(proxy_admin, insert_mysql_replication_hostgroups_query.c_str());
}
MYSQL_QUERY(proxy_admin, "LOAD MYSQL SERVERS TO RUNTIME");
return EXIT_SUCCESS;
}
int wait_for_node_sync(MYSQL* r_proxy_admin, uint64_t master_checksum, const std::string& table) {
uint waited = 0;
bool not_synced = false;
const std::string& query = "SELECT * FROM " + table;
while (waited < SYNC_TIMEOUT) {
not_synced = false;
MYSQL_QUERY(r_proxy_admin, query.c_str());
MYSQL_RES* mysql_servers_res = mysql_store_result(r_proxy_admin);
auto replica_checksum = mysql_servers_raw_checksum(mysql_servers_res);
mysql_free_result(mysql_servers_res);
if (replica_checksum != master_checksum) {
not_synced = true;
diag("Waiting for '%s' to be synced", table.c_str());
}
if (not_synced) {
waited += 1;
sleep(1);
} else {
break;
}
}
if (not_synced) {
diag("'wait_for_node_sync' timeout for query '%s'", query.c_str());
}
return not_synced;
};
int check_mysql_servers_sync(
const CommandLine& cl, MYSQL* proxy_admin, MYSQL* r_proxy_withmonitor_admin, MYSQL* r_proxy_nomonitor_admin
) {
std::string print_master_mysql_servers_hostgroups;
string_format(t_debug_query, print_master_mysql_servers_hostgroups, cl.admin_username, cl.admin_password, cl.host, cl.admin_port, "SELECT * FROM mysql_servers");
std::string print_master_runtime_mysql_servers_hostgroups;
string_format(t_debug_query, print_master_runtime_mysql_servers_hostgroups, cl.admin_username, cl.admin_password, cl.host, cl.admin_port, "SELECT * FROM runtime_mysql_servers");
std::string print_nomonitor_replica_mysql_servers_hostgroups;
string_format(t_debug_query, print_nomonitor_replica_mysql_servers_hostgroups, "radmin", "radmin", cl.host, R_NOMONITOR_PORT, "SELECT * FROM mysql_servers");
std::string print_nomonitor_replica_runtime_mysql_servers_hostgroups;
string_format(t_debug_query, print_nomonitor_replica_runtime_mysql_servers_hostgroups, "radmin", "radmin", cl.host, R_NOMONITOR_PORT, "SELECT * FROM runtime_mysql_servers");
std::string print_nomonitor_replica_disk_mysql_servers_hostgroups;
string_format(t_debug_query, print_nomonitor_replica_disk_mysql_servers_hostgroups, "radmin", "radmin", cl.host, R_NOMONITOR_PORT, "SELECT * FROM disk.mysql_servers");
std::string print_withmonitor_replica_mysql_servers_hostgroups;
string_format(t_debug_query, print_withmonitor_replica_mysql_servers_hostgroups, "radmin", "radmin", cl.host, R_WITHMONITOR_PORT, "SELECT * FROM mysql_servers");
std::string print_withmonitor_replica_runtime_mysql_servers_hostgroups;
string_format(t_debug_query, print_withmonitor_replica_runtime_mysql_servers_hostgroups, "radmin", "radmin", cl.host, R_WITHMONITOR_PORT, "SELECT * FROM runtime_mysql_servers");
std::string print_withmonitor_replica_disk_mysql_servers_hostgroups;
string_format(t_debug_query, print_withmonitor_replica_disk_mysql_servers_hostgroups, "radmin", "radmin", cl.host, R_WITHMONITOR_PORT, "SELECT * FROM disk.mysql_servers");
std::string variable_val;
// get mysql_servers sync algorithm value
int g_err = get_variable_value(proxy_admin, "admin-cluster_mysql_servers_sync_algorithm", variable_val);
if (g_err) { return EXIT_FAILURE; }
const int cluster_sync_mysql_servers_algorithm = atoi(variable_val.c_str());
// get monitor enabled variable value
g_err = get_variable_value(proxy_admin, "mysql-monitor_enabled", variable_val);
if (g_err) { return EXIT_FAILURE; }
bool monitor_enabled = false;
if (strcasecmp(variable_val.c_str(), "true") == 0 || strcasecmp(variable_val.c_str(), "1") == 0) {
monitor_enabled = true;
}
// get save-to-disk variable value
g_err = get_variable_value(proxy_admin, "admin-cluster_mysql_servers_save_to_disk", variable_val);
if (g_err) { return EXIT_FAILURE; }
bool save_to_disk_value = false;
if (strcasecmp(variable_val.c_str(), "true") == 0 || strcasecmp(variable_val.c_str(), "1") == 0) {
save_to_disk_value = true;
}
// get read_only interval variable value
g_err = get_variable_value(proxy_admin, "mysql-monitor_read_only_interval", variable_val);
if (g_err) { return EXIT_FAILURE; }
const long monitor_read_only_interval = std::stol(variable_val);
// get read_only timeout variable value
g_err = get_variable_value(proxy_admin, "mysql-monitor_read_only_timeout", variable_val);
if (g_err) { return EXIT_FAILURE; }
const long monitor_read_only_timeout = std::stol(variable_val);
diag("Checking mysql_servers_sync status "
"[admin-cluster_mysql_servers_sync_algorithm:'%d', "
"mysql-monitor_enabled:'%s', "
"admin-cluster_mysql_servers_save_to_disk:'%s'"
"]...", cluster_sync_mysql_servers_algorithm, (monitor_enabled ? "true" : "false"), (save_to_disk_value ? "true" : "false"));
std::cout << "MASTER 'MYSQL SERVERS' TABLE BEFORE SYNC:" << std::endl;
system(print_master_mysql_servers_hostgroups.c_str());
std::cout << std::endl;
// Wait till read_only actions have been performed
uint64_t wait = monitor_read_only_interval + monitor_read_only_timeout;
usleep((wait * 1000) * 2);
std::cout << "MASTER 'RUNTIME MYSQL SERVERS' TABLE BEFORE SYNC:" << std::endl;
system(print_master_runtime_mysql_servers_hostgroups.c_str());
std::cout << std::endl;
uint64_t master_mysql_servers_checksum = 0;
uint64_t master_runtime_mysql_servers_checksum = 0;
// fetch master mysql_servers resultset and compute it's hash
{
MYSQL_QUERY(proxy_admin, "SELECT * FROM mysql_servers");
MYSQL_RES* mysql_servers_res = mysql_store_result(proxy_admin);
master_mysql_servers_checksum = mysql_servers_raw_checksum(mysql_servers_res);
mysql_free_result(mysql_servers_res);
}
// fetch master runtime_mysql_servers resultset and compute it's hash
{
MYSQL_QUERY(proxy_admin, "SELECT * FROM runtime_mysql_servers");
MYSQL_RES* mysql_servers_res = mysql_store_result(proxy_admin);
master_runtime_mysql_servers_checksum = mysql_servers_raw_checksum(mysql_servers_res);
mysql_free_result(mysql_servers_res);
}
// This comment is exclusively for this TAP test
// If monitor is enabled, records of runtime_mysql_servers and mysql_servers should not match
if (monitor_enabled == true) {
ok(master_mysql_servers_checksum != master_runtime_mysql_servers_checksum, "'runtime_mysql_servers' and 'mysql_servers' should not match.");
} else {
ok(master_mysql_servers_checksum == master_runtime_mysql_servers_checksum, "'runtime_mysql_servers' and 'mysql_servers' should match.");
}
//
// SYNCH CHECK
bool not_synced_query = false;
if (save_to_disk_value == false) {
if (cluster_sync_mysql_servers_algorithm == 1) {
// Algo: 1 [Sync mysql_servers_v2 and runtime_mysql_servers]
// Replica [WITHMONITOR] mysql_servers for both the replica and master will be identical.
not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_mysql_servers_checksum, "mysql_servers");
std::cout << "REPLICA [WITHMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_withmonitor_replica_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
// Replica [NOMONITOR] mysql_servers for both the replica and master will be identical.
not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_mysql_servers_checksum, "mysql_servers");
std::cout << "REPLICA [NOMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_nomonitor_replica_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
// Replica [WITHMONITOR] runtime_mysql_servers for both the replica and master will be identical.
not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_runtime_mysql_servers_checksum, "runtime_mysql_servers");
std::cout << "REPLICA [WITHMONITOR] 'RUNTIME MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_withmonitor_replica_runtime_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'runtime_mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
// Replica [NOMONITOR] runtime_mysql_servers for both the replica and master will be identical.
not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_runtime_mysql_servers_checksum, "runtime_mysql_servers");
std::cout << "REPLICA [NOMONITOR] 'RUNTIME MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_nomonitor_replica_runtime_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'runtime_mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
} else if (cluster_sync_mysql_servers_algorithm == 2) {
// Algo: 2 [Sync mysql_servers_v2 only]
// Replica [WITHMONITOR] mysql_servers for both the replica and master will be identical.
not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_mysql_servers_checksum, "mysql_servers");
std::cout << "REPLICA [WITHMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_withmonitor_replica_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
// Replica [NOMONITOR] mysql_servers for both the replica and master will be identical.
not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_mysql_servers_checksum, "mysql_servers");
std::cout << "REPLICA [NOMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_nomonitor_replica_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
// Replica [WITHMONITOR] runtime_mysql_servers for both the replica and master will be identical.
// Reason: Replica [WITHMONITOR] has monitoring checks enabled and will generate identical runtime_mysql_servers records.
not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_runtime_mysql_servers_checksum, "runtime_mysql_servers");
std::cout << "REPLICA [WITHMONITOR] 'RUNTIME MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_withmonitor_replica_runtime_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'runtime_mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
// Replica [NOMONITOR] runtime_mysql_servers will be identical to master mysql_servers.
// Reason: Replica [NOMONITOR] has monitoring checks disabled, so runtime_mysql_servers will be identical to mysql_servers records.
not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_mysql_servers_checksum, "runtime_mysql_servers");
std::cout << "REPLICA [NOMONITOR] 'RUNTIME MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_nomonitor_replica_runtime_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'runtime_mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
} else if (cluster_sync_mysql_servers_algorithm == 3) {
// Algo: 3 [If the command line includes the argument "-M", Algorithm 1 will be selected.
// If "-M" is not provided, then Algorithm 2 will be chosen.]
// Replica [WITHMONITOR] mysql_servers for both the replica and master will be identical.
not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_mysql_servers_checksum, "mysql_servers");
std::cout << "REPLICA [WITHMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_withmonitor_replica_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
// Replica [NOMONITOR] mysql_servers for both the replica and master will be identical.
not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_mysql_servers_checksum, "mysql_servers");
std::cout << "REPLICA [NOMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_nomonitor_replica_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
// Replica [WITHMONITOR] runtime_mysql_servers for both the replica and master will be identical.
// Reason: Algorithm 2 will be selected [Sync mysql_servers_v2]. After read_only_action,
// the runtime_mysql_servers for replica becomes identical to master.
not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_runtime_mysql_servers_checksum, "runtime_mysql_servers");
std::cout << "REPLICA [WITHMONITOR] 'RUNTIME MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_withmonitor_replica_runtime_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'runtime_mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
// Replica [NOMONITOR] runtime_mysql_servers for both the replica and master will be identical.
// Reason: Algorithm 1 will be selected [Sync mysql_servers_v2 and runtime_mysql_servers]
not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_runtime_mysql_servers_checksum, "runtime_mysql_servers");
std::cout << "REPLICA [NOMONITOR] 'RUNTIME MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_nomonitor_replica_runtime_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'runtime_mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
}
} else { //save_to_disk_value is true
// If Algorithm 1 is selected, the runtime_mysql_servers data will be saved to disk.
// If Algorithm 2 is selected, the mysql_servers data will be saved to disk.
// However, for Algorithm 3, the data saved to disk will depend on the algorithm that is chosen based on the -M argument.
if (cluster_sync_mysql_servers_algorithm == 1) {
// Replica [WITHMONITOR] disk.mysql_servers and master runtime_mysql_servers will be identical.
not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_runtime_mysql_servers_checksum, "disk.mysql_servers");
std::cout << "REPLICA [WITHMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_withmonitor_replica_disk_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'disk.mysql_servers' should be identical to 'runtime_mysql_servers'.");
std::cout << std::endl;
// Replica [NOMONITOR] disk.mysql_servers and master runtime_mysql_servers will be identical.
not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_runtime_mysql_servers_checksum, "disk.mysql_servers");
std::cout << "REPLICA [NOMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_nomonitor_replica_disk_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'disk.mysql_servers' should be identical to 'runtime_mysql_servers'.");
std::cout << std::endl;
} else if (cluster_sync_mysql_servers_algorithm == 2) {
// Replica [WITHMONITOR] disk.mysql_servers and master mysql_servers will be identical.
not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_mysql_servers_checksum, "disk.mysql_servers");
std::cout << "REPLICA [WITHMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_withmonitor_replica_disk_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'disk.mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
// Replica [NOMONITOR] disk.mysql_servers and master mysql_servers will be identical.
not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_mysql_servers_checksum, "disk.mysql_servers");
std::cout << "REPLICA [NOMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_nomonitor_replica_disk_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'disk.mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
} else if (cluster_sync_mysql_servers_algorithm == 3) {
// Replica [WITHMONITOR] disk.mysql_servers and master mysql_servers will be identical.
not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_mysql_servers_checksum, "disk.mysql_servers");
std::cout << "REPLICA [WITHMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_withmonitor_replica_disk_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'disk.disk.mysql_servers' with NULL comments should be synced.");
std::cout << std::endl;
// Replica [NOMONITOR] disk.mysql_servers and master runtime_mysql_servers will be identical.
not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_runtime_mysql_servers_checksum, "disk.mysql_servers");
std::cout << "REPLICA [NOMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl;
system(print_nomonitor_replica_disk_mysql_servers_hostgroups.c_str());
ok(not_synced_query == false, "'disk.mysql_servers' with NULL comments should be synced.");
}
}
diag("Checking mysql_servers_sync status "
"[admin-cluster_mysql_servers_sync_algorithm:'%d', "
"mysql-monitor_enabled:'%s', "
"admin-cluster_mysql_servers_save_to_disk:'%s'"
"]... Done", cluster_sync_mysql_servers_algorithm, (monitor_enabled ? "true" : "false"), (save_to_disk_value ? "true" : "false"));
return EXIT_SUCCESS;
}
/**
* @brief Assumes that 'proxysql_servers' holds at least the one entry required for this test.
* @details It's assumed that primary ProxySQL is part of a Cluster.
*/
int update_proxysql_servers(const CommandLine& cl, MYSQL* admin) {
const char update_proxysql_servers_t[] {
"UPDATE proxysql_servers SET comment='%s' WHERE hostname='%s' and port=%d"
};
cfmt_t update_servers {
cstr_format(update_proxysql_servers_t, std::to_string(time(NULL)).c_str(), cl.host, cl.admin_port)
};
MYSQL_QUERY_T(admin, update_servers.str.c_str());
MYSQL_QUERY_T(admin, "LOAD PROXYSQL SERVERS TO RUNTIME");
return EXIT_SUCCESS;
}
int setup_config_file(const CommandLine& cl, uint32_t r_port, const std::string& config_filename) {
const std::string& workdir = std::string(cl.workdir);
const std::string& t_fmt_config_file = workdir + "test_cluster_sync_config/test_cluster_sync-t.cnf";
const std::string& fmt_config_file = workdir + "test_cluster_sync_config/test_cluster_sync_" + config_filename + "/test_cluster_sync.cnf";
const std::string& datadir_path = workdir + "test_cluster_sync_config/test_cluster_sync_" + config_filename;
// Prepare the configuration file
config_t cfg {};
config_init(&cfg);
if (!config_read_file(&cfg, t_fmt_config_file.c_str())) {
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - Error reading config file.");
config_destroy(&cfg);
return -1;
}
config_setting_t* r_datadir = config_lookup(&cfg, "datadir");
if (r_datadir == nullptr) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - 'datadir' setting not found.");
return -1;
}
config_setting_t* r_admin_vars = config_lookup(&cfg, "admin_variables");
if (r_admin_vars == nullptr) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - 'admin_variables' setting not found.");
return -1;
}
config_setting_t* r_mysql_ifaces = config_setting_get_member(r_admin_vars, "mysql_ifaces");
if (r_mysql_ifaces == nullptr) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - 'mysql_ifaces' setting not found.");
return -1;
}
int r_ifaces_res = config_setting_set_string(r_mysql_ifaces, std::string { "0.0.0.0:" + std::to_string(r_port) }.c_str());
if (r_ifaces_res == CONFIG_FALSE) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - Error while trying to set the values for 'mysql_ifaces'.");
return -1;
}
config_setting_t* p_servers = config_lookup(&cfg, "proxysql_servers");
if (p_servers == nullptr) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - 'proxysql_servers' setting not found.");
return -1;
}
int r_datadir_res = config_setting_set_string(r_datadir, datadir_path.c_str());
if (r_datadir_res == CONFIG_FALSE) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - Error while trying to set the 'datadir' value.");
return -1;
}
// Get first group settings
config_setting_t* r_pserver_group = config_setting_get_elem(p_servers, 0);
if (r_pserver_group == nullptr) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - 'proxysql_servers' doesn't contains first group.");
return -1;
}
config_setting_t* r_pserver_hostname = config_setting_get_member(r_pserver_group, "hostname");
config_setting_t* r_pserver_port = config_setting_get_member(r_pserver_group, "port");
// Check the group members
if (r_pserver_hostname == nullptr || r_pserver_port == nullptr) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - 'proxysql_servers' doesn't contains the necessary group members.");
return -1;
}
int fhost_res = config_setting_set_string(r_pserver_hostname, cl.host);
int fport_res = config_setting_set_int(r_pserver_port, cl.admin_port);
if (fhost_res == CONFIG_FALSE || fport_res == CONFIG_FALSE) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - Error while trying to set the values from env variables.");
return -1;
}
// Write the new config file
if (config_write_file(&cfg, fmt_config_file.c_str()) == CONFIG_FALSE) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Config file - Error while trying to write the new config file.");
return -1;
}
config_destroy(&cfg);
return 0;
}
int launch_proxysql_replica(const CommandLine& cl, uint32_t r_port, const std::string config_filename, bool monitor_enabled,
const std::atomic<bool>& save_proxy_stderr) {
const std::string& workdir = std::string(cl.workdir);
const std::string& replica_stderr = workdir + "test_cluster_sync_config/test_cluster_sync_" + config_filename + "/cluster_sync_node_stderr.txt";
const std::string& proxysql_db = workdir + "test_cluster_sync_config/test_cluster_sync_" + config_filename + "/proxysql.db";
const std::string& stats_db = workdir + "test_cluster_sync_config/test_cluster_sync_" + config_filename + "/proxysql_stats.db";
const std::string& fmt_config_file = workdir + "test_cluster_sync_config/test_cluster_sync_" + config_filename + "/test_cluster_sync.cnf";
// Setup the config file using the env variables in 'CommandLine'
if (setup_config_file(cl, r_port, config_filename)) {
return EXIT_FAILURE;
}
const std::string& proxy_binary_path = workdir + "../../../src/proxysql";
const std::string& proxy_command = proxy_binary_path + " -f " + (monitor_enabled == false ? "-M" : "") + " -c " + fmt_config_file + " > " + replica_stderr + " 2>&1";
diag("Launching replica ProxySQL [%s] via 'system' with command : `%s`", config_filename.c_str(), proxy_command.c_str());
int exec_res = system(proxy_command.c_str());
ok(exec_res == 0, "proxysql cluster node [%s] should execute and shutdown nicely. 'system' result was: %d", config_filename.c_str(), exec_res);
// In case of error place in log the reason
if (exec_res || save_proxy_stderr.load()) {
if (exec_res) {
diag("LOG: Proxysql cluster node [%s] execution failed, logging stderr into 'test_cluster_sync_node_stderr_%s.txt", config_filename.c_str(), config_filename.c_str());
} else {
diag("LOG: One of the tests failed to pass, logging stderr 'test_cluster_sync_node_stderr_%s.txt", config_filename.c_str());
}
}
remove(proxysql_db.c_str());
remove(stats_db.c_str());
return EXIT_SUCCESS;
}
int get_read_only_value(const std::string& host, uint16_t port, const std::string& username, const std::string& password,
int* read_only_val) {
// check is mysql server has read_only value 0
MYSQL* mysqldb = mysql_init(NULL);
// Initialize connections
if (!mysqldb) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysqldb));
return EXIT_FAILURE;
}
// Connnect to local proxysql
if (!mysql_real_connect(mysqldb, host.c_str(), username.c_str(), password.c_str(), NULL, port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysqldb));
mysql_close(mysqldb);
return EXIT_FAILURE;
}
const int rc_query = mysql_query(mysqldb,"SELECT @@global.read_only read_only");
if (rc_query == 0) {
MYSQL_RES *result = mysql_store_result(mysqldb);
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) {
if (row[0]) {
*read_only_val = static_cast<uint16_t>(std::strtoul(row[0], NULL, 10));
}
}
mysql_free_result(result);
}
mysql_close(mysqldb);
return EXIT_SUCCESS;
}
std::vector<std::vector<std::string>> queries = {
{
"SET mysql-monitor_read_only_interval=200", // setting read_only variables
"SET mysql-monitor_read_only_timeout=100",
"SET mysql-monitor_enabled='true'", // enabling monitor
"LOAD MYSQL VARIABLES TO RUNTIME",
"UPDATE global_variables SET variable_value='1' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'",// setting admin-cluster_mysql_servers_sync_algorithm to 1 -> fetch mysql_servers_v2 and runtime_mysql_servers
"LOAD ADMIN VARIABLES TO RUNTIME"
},
{
"UPDATE global_variables SET variable_value='2' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'",
"LOAD ADMIN VARIABLES TO RUNTIME",
"INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type) VALUES (999,998,'read_only')", // adding dummy data so replica nodes can sync after algorithm change from 1 to 2.
"LOAD MYSQL SERVERS TO RUNTIME"
},
{
"UPDATE global_variables SET variable_value='3' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'",
"LOAD ADMIN VARIABLES TO RUNTIME",
"DELETE FROM mysql_replication_hostgroups WHERE writer_hostgroup=999 AND reader_hostgroup=998 AND check_type='read_only'", // deleting dummy data so replica nodes can sync after algorithm change from 2 to 3.
"LOAD MYSQL SERVERS TO RUNTIME"
},
{
"SET mysql-monitor_enabled='false'",
"LOAD MYSQL VARIABLES TO RUNTIME",
"LOAD MYSQL SERVERS TO RUNTIME", // to regenerate runtime_mysql_servers
"UPDATE global_variables SET variable_value='1' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'",
"LOAD ADMIN VARIABLES TO RUNTIME"
},
{
"UPDATE global_variables SET variable_value='2' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'",
"LOAD ADMIN VARIABLES TO RUNTIME",
"INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type) VALUES (999,998,'read_only')",// adding dummy data so replica nodes can sync after algorithm change from 1 to 2.
"LOAD MYSQL SERVERS TO RUNTIME"
},
{
"UPDATE global_variables SET variable_value='3' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'",
"LOAD ADMIN VARIABLES TO RUNTIME",
"DELETE FROM mysql_replication_hostgroups WHERE writer_hostgroup=999 AND reader_hostgroup=998 AND check_type='read_only'", // deleting dummy data so replica nodes can sync after algorithm change from 2 to 3.
"LOAD MYSQL SERVERS TO RUNTIME"
},
{
// save to disk
"SET mysql-monitor_enabled='true'",
"LOAD MYSQL VARIABLES TO RUNTIME",
"UPDATE global_variables SET variable_value='true' WHERE variable_name='admin-cluster_mysql_servers_save_to_disk'", // setting admin-cluster_mysql_servers_save_to_disk to true
"UPDATE global_variables SET variable_value='1' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'",
"LOAD ADMIN VARIABLES TO RUNTIME",
"INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type) VALUES (997,996,'read_only')", // adding dummy data so replica nodes can sync after algorithm change from 1 to 2.
"LOAD MYSQL SERVERS TO RUNTIME"
},
{
"UPDATE global_variables SET variable_value='2' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'",
"LOAD ADMIN VARIABLES TO RUNTIME",
"DELETE FROM mysql_replication_hostgroups WHERE writer_hostgroup=997 AND reader_hostgroup=996 AND check_type='read_only'", // deleting dummy data so replica nodes can sync after algorithm change from 2 to 3.
"LOAD MYSQL SERVERS TO RUNTIME"
},
{
"UPDATE global_variables SET variable_value='3' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'",
"LOAD ADMIN VARIABLES TO RUNTIME",
"INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type) VALUES (997,996,'read_only')",// adding dummy data so replica nodes can sync after algorithm change from 1 to 2.
"LOAD MYSQL SERVERS TO RUNTIME"
}
};
int main(int, char**) {
CommandLine cl;
std::atomic<bool> save_proxy_stderr(false);
if (cl.getEnv()) {
diag("Failed to get the required environmental variables.");
return EXIT_FAILURE;
}
plan( 1 + 1 // replica instances
+ 1 // confirming mysql server 127.0.0.1:13306 is a writer
+ (6 * 5) // calling check_mysql_servers_sync 7 times, 5 differnt checks in each call
+ (3 * 3)
+ 1 + 1 // shutting down replica instances
);
MYSQL* proxy_admin = mysql_init(NULL);
// Initialize connections
if (!proxy_admin) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy_admin));
return EXIT_FAILURE;
}
// Connnect to local proxysql
if (!mysql_real_connect(proxy_admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy_admin));
return EXIT_FAILURE;
}
const std::string t_update_proxysql_servers {
"INSERT INTO proxysql_servers (hostname, port, weight, comment) VALUES ('%s', %d, 0, 'proxysql')"
};
std::string update_proxysql_servers;
string_format(t_update_proxysql_servers, update_proxysql_servers, cl.host, cl.admin_port);
// 1. Backup the Core nodes from current cluster configuration
MYSQL_QUERY(proxy_admin, "DROP TABLE IF EXISTS proxysql_servers_sync_test_backup_2687");
MYSQL_QUERY(proxy_admin, "CREATE TABLE proxysql_servers_sync_test_backup_2687 AS SELECT * FROM proxysql_servers");
// 2. Remove primary from Core nodes
MYSQL_QUERY(proxy_admin, "DELETE FROM proxysql_servers WHERE hostname=='127.0.0.1' AND PORT==6032");
MYSQL_QUERY(proxy_admin, "LOAD PROXYSQL SERVERS TO RUNTIME");
pair<int,vector<srv_addr_t>> core_nodes { fetch_cluster_nodes(proxy_admin) };
if (core_nodes.first) { return EXIT_FAILURE; }
// 2.1 If core nodes are not reachable, assume no cluster is running; make test gracefully exit
if (core_nodes.second.size()) {
const string host { core_nodes.second[0].host };
const int port { core_nodes.second[0].port };
MYSQL* c_node_admin = mysql_init(NULL);
if (!mysql_real_connect(c_node_admin, host.c_str(), cl.admin_username, cl.admin_password, NULL, port, NULL, 0)) {
int myerrno = mysql_errno(c_node_admin);
if (myerrno == 2002) {
diag("Unable to connect to cluster Core nodes; required environment not met, gracefully exiting...");
plan(0);
return exit_status();
} else {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy_admin));
return EXIT_FAILURE;
}
}
mysql_close(c_node_admin);
}
// 3. Wait for all Core nodes to sync (confirm primary out of core nodes)
std::string check_no_primary_query {};
string_format(
"SELECT CASE COUNT(*) WHEN 0 THEN 1 ELSE 0 END FROM proxysql_servers WHERE hostname=='%s' AND port==%d",
check_no_primary_query, cl.host, cl.admin_port
);
int check_res = check_nodes_sync(cl, core_nodes.second, check_no_primary_query, SYNC_TIMEOUT);
if (check_res != EXIT_SUCCESS) { return EXIT_FAILURE; }
// 4. Remove all current servers from primary instance (only secondary sync matters)
MYSQL_QUERY(proxy_admin, "DELETE FROM proxysql_servers");
MYSQL_QUERY(proxy_admin, update_proxysql_servers.c_str());
MYSQL_QUERY(proxy_admin, "LOAD PROXYSQL SERVERS TO RUNTIME");
// disable admin-cluster_mysql_servers_save_to_disk before executing replicas
MYSQL_QUERY(proxy_admin, "UPDATE global_variables SET variable_value='false' WHERE variable_name='admin-cluster_mysql_servers_save_to_disk'"); // setting admin-cluster_mysql_servers_save_to_disk to false
MYSQL_QUERY(proxy_admin, "LOAD ADMIN VARIABLES TO RUNTIME");
// cleaning old records
MYSQL_QUERY(proxy_admin, "DELETE FROM mysql_servers");
MYSQL_QUERY(proxy_admin, "DELETE FROM mysql_replication_hostgroups");
MYSQL_QUERY(proxy_admin, "LOAD MYSQL SERVERS TO RUNTIME");
// Launch proxysql with cluster config and monitor feature disabled
std::thread proxysql_replica_nomonitor_thd(launch_proxysql_replica, std::ref(cl), R_NOMONITOR_PORT, "nomonitor", false, std::ref(save_proxy_stderr));
// Launch proxysql with cluster config - with -M commandline
std::thread proxysql_replica_withmonitor_thd(launch_proxysql_replica, std::ref(cl), R_WITHMONITOR_PORT, "withmonitor", true, std::ref(save_proxy_stderr));
MYSQL* r_proxysql_nomonitor_admin = NULL;
MYSQL* r_proxysql_withmonitor_admin = NULL;
{
// Waiting for proxysql to be ready
conn_opts_t conn_opts_nomonitor {};
conn_opts_nomonitor.host = cl.host;
conn_opts_nomonitor.user = "radmin";
conn_opts_nomonitor.pass = "radmin";
conn_opts_nomonitor.port = R_NOMONITOR_PORT;
// connect to proxsqyl replica [nomonitor]
r_proxysql_nomonitor_admin = wait_for_proxysql(conn_opts_nomonitor, CONNECT_TIMEOUT);
// Once the thread is spanwed we should always go to cleanup to wait
ok(r_proxysql_nomonitor_admin != nullptr, "New instance of proxysql [nomonitor] with cluster config should be properly spawned.");
if (r_proxysql_nomonitor_admin == nullptr) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(r_proxysql_nomonitor_admin));
goto cleanup;
}
conn_opts_t conn_opts_withmonitor {};
conn_opts_withmonitor.host = cl.host;
conn_opts_withmonitor.user = "radmin";
conn_opts_withmonitor.pass = "radmin";
conn_opts_withmonitor.port = R_WITHMONITOR_PORT;
// connect to proxsqyl replica [nomonitor]
r_proxysql_withmonitor_admin = wait_for_proxysql(conn_opts_withmonitor, CONNECT_TIMEOUT);
// Once the thread is spanwed we should always go to cleanup to wait
ok(r_proxysql_withmonitor_admin != nullptr, "New instance of proxysql [withmonitor] with cluster config should be properly spawned.");
if (r_proxysql_withmonitor_admin == nullptr) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(r_proxysql_withmonitor_admin));
goto cleanup;
}
int read_only_val = -1;
int result = get_read_only_value("127.0.0.1", 13306, "root", "root", &read_only_val);
if (result != EXIT_SUCCESS) {
fprintf(stderr, "File %s, line %d, Error: `%s`\n", __FILE__, __LINE__, "Fetching read_only value from mysql server failed.");
goto cleanup;
}
// For thorough testing of synchronization under all possible scenarios, it is necessary for
// the MySQL server at 127.0.0.1:13306 to function as a writer.
ok(read_only_val == 0, "MySQL Server '127.0.0.1:13306' should function as a writer");
const std::vector<mysql_server_tuple> insert_mysql_servers_values {
std::make_tuple(1, "127.0.0.1", 13306, 12, "ONLINE", 1, 1, 1000, 300, 1, 200, ""), // this server has read_only value 0 (writer)
std::make_tuple(2, "127.0.0.1", 13307, 13, "OFFLINE_SOFT", 2, 1, 500, 300, 1, 200, ""),
std::make_tuple(3, "127.0.0.1", 13308, 14, "OFFLINE_HARD", 2, 1, 500, 300, 1, 200, ""),
std::make_tuple(4, "127.0.0.1", 13309, 15, "SHUNNED", 1, 0, 500, 300, 1, 200, "")
};
const std::vector<replication_hostgroups_tuple> insert_replication_hostgroups_values {
std::make_tuple(0, 1, "read_only") // Here we are assigning the hostgroup to the reader, and read-only actions will creating a new entry in hostgroup 0.
};
// Inserting new records into 'mysql_servers' and 'mysql_replication_hostgroups'.
result = insert_mysql_servers_records(proxy_admin, insert_mysql_servers_values, insert_replication_hostgroups_values);
if (result != EXIT_SUCCESS) {
fprintf(stderr, "File %s, line %d, Error: `%s`\n", __FILE__, __LINE__, "Failed to insert records in mysql_servers table.");
goto cleanup;
}
for (const auto& pre_queries : queries) {
for (const std::string& query : pre_queries) {
MYSQL_QUERY__(proxy_admin, query.c_str());
usleep(1000000);
}
sleep(2);
result = check_mysql_servers_sync(cl, proxy_admin, r_proxysql_withmonitor_admin, r_proxysql_nomonitor_admin);
if (result != EXIT_SUCCESS) {
fprintf(stderr, "File %s, line %d, Error: `%s`\n", __FILE__, __LINE__, "Checking mysql servers sync records failed.");
goto cleanup;
}
}
}
cleanup:
// In case of test failing, save the stderr output from the spawned proxysql instance
if (tests_failed() != 0) {
save_proxy_stderr.store(true);
}
if (r_proxysql_nomonitor_admin) {
int mysql_timeout = 2;
mysql_options(r_proxysql_nomonitor_admin, MYSQL_OPT_CONNECT_TIMEOUT, &mysql_timeout);
mysql_options(r_proxysql_nomonitor_admin, MYSQL_OPT_READ_TIMEOUT, &mysql_timeout);
mysql_options(r_proxysql_nomonitor_admin, MYSQL_OPT_WRITE_TIMEOUT, &mysql_timeout);
mysql_query(r_proxysql_nomonitor_admin, "PROXYSQL SHUTDOWN");
mysql_close(r_proxysql_nomonitor_admin);
}
if (r_proxysql_withmonitor_admin) {
int mysql_timeout = 2;
mysql_options(r_proxysql_withmonitor_admin, MYSQL_OPT_CONNECT_TIMEOUT, &mysql_timeout);
mysql_options(r_proxysql_withmonitor_admin, MYSQL_OPT_READ_TIMEOUT, &mysql_timeout);
mysql_options(r_proxysql_withmonitor_admin, MYSQL_OPT_WRITE_TIMEOUT, &mysql_timeout);
mysql_query(r_proxysql_withmonitor_admin, "PROXYSQL SHUTDOWN");
mysql_close(r_proxysql_withmonitor_admin);
}
proxysql_replica_nomonitor_thd.join();
proxysql_replica_withmonitor_thd.join();
// Recover primary ProxySQL MySQL and ProxySQL servers
diag("RESTORING: Recovering primary configuration...");
{
// Recover previous MySQL servers and generate a newer checksum for primary
MYSQL_QUERY(proxy_admin, "LOAD MYSQL SERVERS FROM DISK");
MYSQL_QUERY(proxy_admin, "LOAD MYSQL SERVERS TO RUNTIME");
// Insert primary into another Core node config and wait for replication
diag("RESTORING: Inserting primary back into Core nodes");
bool recovered_servers_st = false;
std::string insert_query {};
string_format(
"INSERT INTO proxysql_servers (hostname,port,weight,comment) VALUES ('%s',%d,0,'proxysql')",
insert_query, cl.host, cl.admin_port
);
for (const auto& node : core_nodes.second) {
const std::string host { node.host };
const int port = node.port;
MYSQL* c_node_admin = mysql_init(NULL);
diag("RESTORING: Inserting into node '%s:%d'", host.c_str(), port);
if (!mysql_real_connect(c_node_admin, host.c_str(), cl.admin_username, cl.admin_password, NULL, port, NULL, 0)) {
const std::string err_msg {
"Connection to core node failed with '" + std::string { mysql_error(c_node_admin) } + "'. Retrying..."
};
fprintf(stderr, "File %s, line %d, Error: `%s`\n", __FILE__, __LINE__, err_msg.c_str());
mysql_close(c_node_admin);
continue;
}
int my_rc = mysql_query(c_node_admin, insert_query.c_str());
if (my_rc == EXIT_SUCCESS) {
mysql_query(c_node_admin, "LOAD PROXYSQL SERVERS TO RUNTIME");
break;
} else {
const std::string err_msg {
"Insert primary into node failed with: '" + std::string { mysql_error(c_node_admin) } + "'"
};
fprintf(stderr, "File %s, line %d, Error: `%s`\n", __FILE__, __LINE__, err_msg.c_str());
}
}
// Wait for sync after primary insertion into Core node
std::string check_for_primary {};
string_format(
"SELECT COUNT(*) FROM proxysql_servers WHERE hostname=='%s' AND port==%d", check_no_primary_query,
cl.host, cl.admin_port
);
// Wait for the other nodes to sync ProxySQL servers to include Primary
int check_res = check_nodes_sync(cl, core_nodes.second, check_no_primary_query, SYNC_TIMEOUT);
if (check_res != EXIT_SUCCESS) { return EXIT_FAILURE; }
// Recover the old ProxySQL servers from backup in primary
MYSQL_QUERY(proxy_admin, "DELETE FROM proxysql_servers");
MYSQL_QUERY(proxy_admin, "INSERT INTO proxysql_servers SELECT * FROM proxysql_servers_sync_test_backup_2687");
MYSQL_QUERY(proxy_admin, "DROP TABLE proxysql_servers_sync_test_backup_2687");
MYSQL_QUERY(proxy_admin, "LOAD PROXYSQL SERVERS TO RUNTIME");
}
mysql_close(proxy_admin);
return exit_status();
}