/** * @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 #include #include #include #include #include #include #include #include #include #include #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; using replication_hostgroups_tuple = std::tuple; /** * @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& insert_mysql_servers_values, const std::vector& 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& 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(std::strtoul(row[0], NULL, 10)); } } mysql_free_result(result); } mysql_close(mysqldb); return EXIT_SUCCESS; } std::vector> 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 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> 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 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 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(); }