mirror of https://github.com/sysown/proxysql
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.
1022 lines
46 KiB
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();
|
|
}
|