diff --git a/test/tap/tests/test_cluster1-t.cpp b/test/tap/tests/test_cluster1-t.cpp new file mode 100644 index 000000000..fbd5256fc --- /dev/null +++ b/test/tap/tests/test_cluster1-t.cpp @@ -0,0 +1,258 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + + +/* + * this test assumes that this proxysql instance is part of a 10 nodes cluster + * there are 4 core nodes and 6 satellite nodes + * 127.0.0.1:6032 : this proxy (core node 0) + * 127.0.0.1:26001 : core node1 + * 127.0.0.1:26002 : core node2 + * 127.0.0.1:26003 : core node3 + * 127.0.0.1:26004 : satellite node1 + * 127.0.0.1:26005 : satellite node2 + * 127.0.0.1:26006 : satellite node3 + * 127.0.0.1:26007 : satellite node4 + * 127.0.0.1:26008 : satellite node5 + * 127.0.0.1:26009 : satellite node6 +*/ + +int run_q(MYSQL *mysql, const char *q) { + MYSQL_QUERY(mysql,q); + return 0; +} + +void get_time(std::string& s) { + time_t __timer; + char __buffer[30]; + struct tm __tm_info; + time(&__timer); + localtime_r(&__timer, &__tm_info); + strftime(__buffer, 25, "%Y-%m-%d %H:%M:%S", &__tm_info); + s = std::string(__buffer); +} + + + +// we make the very simple assumptions that all proxies are running +// on 127.0.0.1 , therefore we only specify the ports +const std::vector cluster_ports = { + 6032, + 26001, + 26002, + 26003, + 26004, + 26005, + 26006, + 26007, + 26008, + 26009 +}; + +// these simply queries update a variable to make sure that a resync is triggered +const char * update_admin_variables_1 = "UPDATE global_variables SET variable_value=variable_value+1 WHERE variable_name='admin-refresh_interval'"; +const char * update_mysql_variables_1 = "UPDATE global_variables SET variable_value=variable_value+1 WHERE variable_name='mysql-monitor_connect_interval'"; +const char * update_mysql_query_rules_1 = "UPDATE mysql_query_rules SET comment = IFNULL(comment,'') || 'a'"; +const char * update_mysql_users_1 = "UPDATE mysql_users SET max_connections = max_connections + 1"; +const char * update_mysql_servers_1 = "UPDATE mysql_servers SET max_connections = max_connections + 1"; +std::vector conns; + + +int dumping_checksums_return_uniq(MYSQL_RES *res, std::set& checksums) { + MYSQL_ROW row; + while ((row = mysql_fetch_row(res))) { + diag("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]); + std::string chk = row[5]; + checksums.insert(chk); + } + return checksums.size(); +} + + +int get_checksum(MYSQL *mysql, const std::string& name, std::string& value) { + std::string query = "SELECT checksum FROM runtime_checksums_values WHERE name='" + name + "'"; + MYSQL_QUERY(mysql,query.c_str()); + MYSQL_RES * res = mysql_store_result(mysql); + int rr = mysql_num_rows(res); + MYSQL_ROW row; + while ((row = mysql_fetch_row(res))) { + value = std::string(row[0]); + } + ok(rr == 1 && value.length() > 0, "Checksum for %s = %s" , name.c_str(), value.c_str()); + mysql_free_result(res); + if (rr == 1 && value.length() > 0) return 0; + return 1; +} + +int module_in_sync(MYSQL *mysql, const std::string& name, const std::string& value, int num_retries, int& i) { + std::string query = "SELECT hostname, port, name, version, epoch, checksum, changed_at, updated_at, diff_check FROM stats_proxysql_servers_checksums WHERE name='" + name + "'"; + int rc = 0; + while (i< num_retries && rc != 1) { + std::set checksums; + MYSQL_QUERY(mysql,query.c_str()); + MYSQL_RES * res = mysql_store_result(mysql); + std::string s; + get_time(s); + diag("%s: Dumping %s", s.c_str(), query.c_str()); + int rc = dumping_checksums_return_uniq(res, checksums); + mysql_free_result(res); + if (rc == 1) { + std::set::iterator it = checksums.begin(); + if (*it == value) { + return 0; + } + } + sleep(1); + i++; + } + return 1; +} + +int create_connections(CommandLine& cl) { + for (int i = 0; i < cluster_ports.size() ; i++) { + MYSQL * mysql = mysql_init(NULL); + if (!mysql) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql)); + return exit_status(); + } + + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); + if (!mysql_real_connect(mysql, cl.host, cl.admin_username, cl.admin_password, NULL, cluster_ports[i], NULL, CLIENT_SSL|CLIENT_COMPRESS)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql)); + return exit_status(); + } + conns.push_back(mysql); + } + return 0; +} + +int trigger_sync_and_check(MYSQL *mysql, std::string modname, const char *update_query, const char *load_query) { + int rc; + std::string chk1; + std::string chk2; + get_checksum(mysql, modname , chk1); + MYSQL_QUERY(mysql, update_query); + MYSQL_QUERY(mysql, load_query); + get_checksum(mysql, modname, chk2); + ok(chk1 != chk2 , "%s checksums. Before: %s , after: %s", modname.c_str(), chk1.c_str(), chk2.c_str()); + int retries = 0; + rc = module_in_sync(mysql, modname, chk2, 30, retries); + ok (rc == 0, "Module %s %sin sync after %d seconds" , modname.c_str() , rc == 0 ? "" : "NOT " , retries); + for (int i = 4 ; i modules = { + "admin_variables", + "ldap_variables", + "mysql_variables", + "mysql_query_rules", + "mysql_servers", + "mysql_users", + "proxysql_servers", + }; + + std::string chk1; + std::string chk2; + get_checksum(proxysql_admin, "admin_variables", chk1); + // we set all the admin-cluster_xxxx_save_to_disk to false to prevent that resync affects our testing proxy + for (std::vector::iterator it = modules.begin() ; it != modules.end() ; it++) { + std::string q = "SET admin-cluster_" + *it + "_save_to_disk='false'"; + MYSQL_QUERY(proxysql_admin, q.c_str()); + } + MYSQL_QUERY(proxysql_admin, update_admin_variables_1); + MYSQL_QUERY(proxysql_admin, "LOAD ADMIN VARIABLES TO RUNTIME"); + get_checksum(proxysql_admin, "admin_variables", chk2); + ok(chk1 != chk2 , "admin_variables checksums. Before: %s , after: %s", chk1.c_str(), chk2.c_str()); + + get_checksum(proxysql_admin, "mysql_variables", chk1); + MYSQL_QUERY(proxysql_admin, "SET mysql-have_ssl='true'"); + MYSQL_QUERY(proxysql_admin, "SET mysql-have_compress='true'"); + MYSQL_QUERY(proxysql_admin, update_mysql_variables_1); + MYSQL_QUERY(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME"); + get_checksum(proxysql_admin, "mysql_variables", chk2); + ok(chk1 != chk2 , "mysql_variables checksums. Before: %s , after: %s", chk1.c_str(), chk2.c_str()); + + + + MYSQL_RES* proxy_res; + int rc = 0; + rc = create_connections(cl); + if (rc != 0) { + return exit_status(); + } + ok(conns.size() == cluster_ports.size() , "Known nodes: %lu . Connected nodes: %lu", cluster_ports.size(), conns.size()); + + int retries = 0; + rc = module_in_sync(proxysql_admin, "mysql_variables", chk2, 30, retries); + ok (rc == 0, "Module mysql_variables %sin sync after %d seconds" , rc == 0 ? "" : "NOT " , retries); + + + // the workflow here is simple: + // for each proxy: + // get the checksum of the module + // update the module + // get the new checksum + // wait for all the other core nodes to sync + for (int i = 0; i < 4; i++) { + diag("Running changes on server node %d", i); + trigger_sync_and_check(conns[i], "admin_variables", update_admin_variables_1, "LOAD ADMIN VARIABLES TO RUNTIME"); + } + for (int i = 0; i < 4; i++) { + diag("Running changes on server node %d", i); + trigger_sync_and_check(conns[i], "mysql_variables", update_mysql_variables_1, "LOAD MYSQL VARIABLES TO RUNTIME"); + } + for (int i = 0; i < 4; i++) { + diag("Running changes on server node %d", i); + trigger_sync_and_check(conns[i], "mysql_query_rules", update_mysql_query_rules_1, "LOAD MYSQL QUERY RULES TO RUNTIME"); + } + for (int i = 0; i < 4; i++) { + diag("Running changes on server node %d", i); + trigger_sync_and_check(conns[i], "mysql_users", update_mysql_users_1, "LOAD MYSQL USERS TO RUNTIME"); + } + for (int i = 0; i < 4; i++) { + diag("Running changes on server node %d", i); + trigger_sync_and_check(conns[i], "mysql_servers", update_mysql_servers_1, "LOAD MYSQL SERVERS TO RUNTIME"); + } + + return exit_status(); +}