diff --git a/.gitignore b/.gitignore index 3929f2c98..539f686cf 100644 --- a/.gitignore +++ b/.gitignore @@ -194,3 +194,8 @@ test/tap/tests/reg_test_3504-change_user_libmysql_helper test/tap/tests/generate_set_session_csv test/tap/tests/set_testing-240.csv local_testing_datadir/ + +#files generated during CI run +proxysql-save.cfg +test/tap/tests/test_cluster_sync_config/cluster_sync_node_stderr.txt +test/tap/tests/test_cluster_sync_config/proxysql*.pem diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index 5f27ddff9..438e5643b 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -35,11 +35,18 @@ struct bio_st { typedef int CRYPTO_REF_COUNT; -// in libssl 1.1.1 +/** + * @brief This is the 'bio_st' struct definition from libssl 3.0.0. NOTE: This is an internal struct from + * OpenSSL library, currently it's used for performing checks on the reads/writes performed on the BIO objects. + * It's extremely important to keep this struct up to date with each OpenSSL dependency update. + */ struct bio_st { + OSSL_LIB_CTX *libctx; const BIO_METHOD *method; /* bio, mode, argp, argi, argl, ret */ +#ifndef OPENSSL_NO_DEPRECATED_3_0 BIO_callback_fn callback; +#endif BIO_callback_fn_ex callback_ex; char *cb_arg; /* first argument for the callback */ int init; diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index f348b0e36..fa5cd3d6f 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -785,3 +785,53 @@ int create_extra_users( return EXIT_SUCCESS; } + +std::string tap_curtime() { + time_t __timer; + char lut[30]; + struct tm __tm_info; + time(&__timer); + localtime_r(&__timer, &__tm_info); + strftime(lut, 25, "%Y-%m-%d %H:%M:%S", &__tm_info); + std::string s = std::string(lut); + return s; + +int get_proxysql_cpu_usage(const CommandLine& cl, uint32_t intv, uint32_t& cpu_usage) { + // check if proxysql process is consuming higher cpu than it should + MYSQL* proxysql_admin = mysql_init(NULL); + if (!mysql_real_connect(proxysql_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(proxysql_admin)); + return -1; + } + + // recover admin variables + std::string set_stats_query { "SET admin-stats_system_cpu=" + std::to_string(intv) }; + MYSQL_QUERY(proxysql_admin, set_stats_query.c_str()); + MYSQL_QUERY(proxysql_admin, "LOAD ADMIN VARIABLES TO RUNTIME"); + + // sleep during the required interval + safe threshold + sleep(intv + 2); + + MYSQL_QUERY(proxysql_admin, "SELECT * FROM system_cpu ORDER BY timestamp DESC LIMIT 1"); + MYSQL_RES* admin_res = mysql_store_result(proxysql_admin); + MYSQL_ROW row = mysql_fetch_row(admin_res); + + double s_clk = (1.0 / sysconf(_SC_CLK_TCK)) * 1000; + int utime_ms = atoi(row[1]) * s_clk; + int stime_ms = atoi(row[2]) * s_clk; + int t_ms = utime_ms + stime_ms; + + // return the cpu usage + cpu_usage = t_ms; + + // free the result + mysql_free_result(admin_res); + + // recover admin variables + MYSQL_QUERY(proxysql_admin, "SET admin-stats_system_cpu=60"); + MYSQL_QUERY(proxysql_admin, "LOAD ADMIN VARIABLES TO RUNTIME"); + + mysql_close(proxysql_admin); + + return 0; +} diff --git a/test/tap/tap/utils.h b/test/tap/tap/utils.h index 9c774ebd3..f83df178b 100644 --- a/test/tap/tap/utils.h +++ b/test/tap/tap/utils.h @@ -10,6 +10,8 @@ #include +#include "command_line.h" + #define MYSQL_QUERY(mysql, query) \ do { \ if (mysql_query(mysql, query)) { \ @@ -227,4 +229,15 @@ int create_extra_users( MYSQL* proxysql_admin, MYSQL* mysql_server, const std::vector& users_config ); +std::string tap_curtime(); +/** + * @brief Returns ProxySQL cpu usage in ms. + * @param intv The interval in which the CPU usage of ProxySQL is going + * to be measured. + * @param cpu_usage Output parameter with the cpu usage by ProxySQL in + * 'ms' in the specified interval. + * @return 0 if success, -1 in case of error. + */ +int get_proxysql_cpu_usage(const CommandLine& cl, uint32_t intv, uint32_t& cpu_usage); + #endif // #define UTILS_H diff --git a/test/tap/tests/reg_test_3273_ssl_con-t.cpp b/test/tap/tests/reg_test_3273_ssl_con-t.cpp index 6c7014fc3..f3c78cec4 100644 --- a/test/tap/tests/reg_test_3273_ssl_con-t.cpp +++ b/test/tap/tests/reg_test_3273_ssl_con-t.cpp @@ -1,5 +1,5 @@ /** - * @file test_simple_ssl_con-t.cpp + * @file reg_test_3273_ssl_con-t.cpp * @brief This test tries to induce a particular timing condition to replicate issue #70138. * For testing the issue against admin, supply to the binary "admin" as parameter, otherwise * the connection will be created as a regular client connection. @@ -64,49 +64,8 @@ static int wait_for_mysql(MYSQL *mysql, int status) { } } - -/** - * @brief Returns ProxySQL cpu usage in ms. - * @param intv The interval in which the CPU usage of ProxySQL is going - * to be measured. - * @param cpu_usage Output parameter with the cpu usage by ProxySQL in - * 'ms' in the specified interval. - * @return 0 if success, -1 in case of error. - */ -int get_proxysql_cpu_usage(const CommandLine& cl, int intv, int* cpu_usage) { - // check if proxysql process is consuming higher cpu than it should - MYSQL* proxysql_admin = mysql_init(NULL); - if (!mysql_real_connect(proxysql_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(proxysql_admin)); - return -1; - } - - // recover admin variables - std::string set_stats_query { "SET admin-stats_system_cpu=" + std::to_string(intv) }; - MYSQL_QUERY(proxysql_admin, set_stats_query.c_str()); - MYSQL_QUERY(proxysql_admin, "LOAD ADMIN VARIABLES TO RUNTIME"); - - // sleep during the required interval + safe threshold - sleep(intv + 2); - - MYSQL_QUERY(proxysql_admin, "SELECT * FROM system_cpu ORDER BY timestamp DESC LIMIT 1"); - MYSQL_RES* admin_res = mysql_store_result(proxysql_admin); - MYSQL_ROW row = mysql_fetch_row(admin_res); - - double s_clk = 1.0 / sysconf(_SC_CLK_TCK); - int utime_ms = atoi(row[1]) / s_clk; - int stime_ms = atoi(row[2]) / s_clk; - int t_ms = utime_ms + stime_ms; - - // return the cpu usage - *cpu_usage = t_ms; - - // recover admin variables - MYSQL_QUERY(proxysql_admin, "SET admin-stats_system_cpu=60"); - MYSQL_QUERY(proxysql_admin, "LOAD ADMIN VARIABLES TO RUNTIME"); - - return 0; -} +const uint32_t REPORT_INTV_SEC = 5; +const double MAX_ALLOWED_CPU_USAGE = 0.15; int main(int argc, char** argv) { CommandLine cl; @@ -125,8 +84,8 @@ int main(int argc, char** argv) { } // get ProxySQL idle cpu usage - int idle_cpu_ms = 0; - int idle_err = get_proxysql_cpu_usage(cl, 5, &idle_cpu_ms); + uint32_t idle_cpu_ms = 0; + int idle_err = get_proxysql_cpu_usage(cl, REPORT_INTV_SEC, idle_cpu_ms); if (idle_err) { fprintf(stdout, "File %s, line %d, Error: '%s'\n", __FILE__, __LINE__, "Unable to get 'idle_cpu' usage."); return idle_err; @@ -155,8 +114,7 @@ int main(int argc, char** argv) { my_socket sockt = mysql_get_socket(proxysql); int state = 0; - while (status) - { + while (status) { status = wait_for_mysql(proxysql, status); if (state == 1) { std::thread closer {[sockt]() -> void { @@ -187,20 +145,20 @@ int main(int argc, char** argv) { return exit_status(); } - int final_cpu_ms = 0; - int final_err = get_proxysql_cpu_usage(cl, 5, &final_cpu_ms); + uint32_t final_cpu_ms = 0; + int final_err = get_proxysql_cpu_usage(cl, REPORT_INTV_SEC, final_cpu_ms); if (final_err) { fprintf(stdout, "File %s, line %d, Error: '%s'\n", __FILE__, __LINE__, "Unable to get 'idle_cpu' usage."); return idle_err; } - // proxysql spent more than one time of CPU in the last 5 seconds when it should be - // idle; something is wrong + // compute the '%' of CPU used during the last interval + uint32_t cpu_usage_ms = final_cpu_ms - idle_cpu_ms; + double cpu_usage_pct = cpu_usage_ms / (REPORT_INTV_SEC * 1000.0); + ok( - final_cpu_ms < (idle_cpu_ms*3), - "ProxySQL shouldn't be taking so much CPU time, idle:'%d', final:'%d'", - idle_cpu_ms, - final_cpu_ms + cpu_usage_pct < MAX_ALLOWED_CPU_USAGE, "ProxySQL CPU usage should be below expected: (Exp: %%%lf, Act: %%%lf)", + MAX_ALLOWED_CPU_USAGE, cpu_usage_pct ); return exit_status(); diff --git a/test/tap/tests/reg_test_3765_ssl_pollout-t.cpp b/test/tap/tests/reg_test_3765_ssl_pollout-t.cpp new file mode 100644 index 000000000..54b01416c --- /dev/null +++ b/test/tap/tests/reg_test_3765_ssl_pollout-t.cpp @@ -0,0 +1,169 @@ +/** + * @file reg_test_3765_ssl_pollout-t.cpp + * @brief This test opens multiple connections against ProxySQL with different client flags and checks that + * CPU usage by ProxySQL didn't increase significantly. Tested connections types are: normal, SSL, and + * compression. + * @details The goal of the test is to detect regressions or incompatibilities in the way ProxySQL polling + * operations interacts with OpenSSL library. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +using std::string; +using std::vector; + +struct conn_opts_t { + string host; + string user; + string pass; + int port; + uint64_t client_flags; +}; + +/** + * @brief TODO: Refactor this into utils, also used in another PR. + */ +int create_connections(const conn_opts_t& conn_opts, uint32_t cons_num, std::vector& proxy_conns) { + std::vector result {}; + + for (uint32_t i = 0; i < cons_num; i++) { + const char* host = conn_opts.host.c_str(); + const char* user = conn_opts.user.c_str(); + const char* pass = conn_opts.pass.c_str(); + const int port = conn_opts.port; + + MYSQL* proxysql = mysql_init(NULL); + + if (conn_opts.client_flags & CLIENT_SSL) { + mysql_ssl_set(proxysql, NULL, NULL, NULL, NULL, NULL); + } + + if (!mysql_real_connect(proxysql, host, user, pass, NULL, port, NULL, conn_opts.client_flags)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return EXIT_FAILURE; + } else { + result.push_back(proxysql); + } + } + + proxy_conns = result; + return EXIT_SUCCESS; +} + +const uint32_t ADMIN_CONN_NUM = 100; +const uint32_t MYSQL_CONN_NUM = 100; +const uint32_t REPORT_INTV_SEC = 5; +const double MAX_ALLOWED_CPU_USAGE = 0.15; + +int get_idle_conns_cpu_usage(CommandLine& cl, uint64_t mode, uint32_t& idle_cpu_ms, uint32_t& final_cpu_ms) { + // get ProxySQL idle cpu usage + int idle_err = get_proxysql_cpu_usage(cl, REPORT_INTV_SEC, idle_cpu_ms); + if (idle_err) { + fprintf(stdout, "File %s, line %d, Error: '%s'\n", __FILE__, __LINE__, "Unable to get 'idle_cpu' usage."); + return idle_err; + } + + conn_opts_t proxy_conns_opts { "127.0.0.1", cl.username, cl.password, cl.port, mode }; + conn_opts_t admin_conns_opts { "127.0.0.1", cl.admin_username, cl.admin_password, cl.admin_port, mode }; + + // Create 'N' admin and mysql connections without SSL + vector v_admin_conns {}; + int admin_conns_res = create_connections(admin_conns_opts, ADMIN_CONN_NUM, v_admin_conns); + if (admin_conns_res != EXIT_SUCCESS) { + return EXIT_FAILURE; + } + + vector v_proxy_conns {}; + int mysql_conns_res = create_connections(proxy_conns_opts, MYSQL_CONN_NUM, v_proxy_conns); + if (admin_conns_res != EXIT_SUCCESS) { + return EXIT_FAILURE; + } + + int final_err = get_proxysql_cpu_usage(cl, REPORT_INTV_SEC, final_cpu_ms); + if (final_err) { + fprintf(stdout, "File %s, line %d, Error: '%s'\n", __FILE__, __LINE__, "Unable to get 'idle_cpu' usage."); + return idle_err; + } + + std::for_each(v_admin_conns.begin(), v_admin_conns.end(), [](MYSQL* conn) -> void { mysql_close(conn); }); + std::for_each(v_proxy_conns.begin(), v_proxy_conns.end(), [](MYSQL* conn) -> void { mysql_close(conn); }); + + return EXIT_SUCCESS; +} + +int main(int argc, char** argv) { + CommandLine cl; + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return -1; + } + + uint32_t idle_cpu_ms = 0; + uint32_t final_cpu_ms = 0; + + MYSQL* proxysql_admin = mysql_init(NULL); + if (!mysql_real_connect(proxysql_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(proxysql_admin)); + return EXIT_FAILURE; + } + + MYSQL_QUERY(proxysql_admin, "SET mysql-have_ssl=1"); + MYSQL_QUERY(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME"); + mysql_close(proxysql_admin); + + diag("Testing regular connections..."); + int cpu_usage_res = get_idle_conns_cpu_usage(cl, 0, idle_cpu_ms, final_cpu_ms); + if (cpu_usage_res != EXIT_SUCCESS) { return EXIT_FAILURE; } + + // compute the '%' of CPU used during the last interval + uint32_t cpu_usage_ms = final_cpu_ms - idle_cpu_ms; + double cpu_usage_pct = cpu_usage_ms / (REPORT_INTV_SEC * 1000.0); + + ok( + cpu_usage_pct < MAX_ALLOWED_CPU_USAGE, "ProxySQL CPU usage should be below expected: (Exp: %%%lf, Act: %%%lf)", + MAX_ALLOWED_CPU_USAGE, cpu_usage_pct + ); + + diag("Testing SSL connections..."); + cpu_usage_res = get_idle_conns_cpu_usage(cl, CLIENT_SSL, idle_cpu_ms, final_cpu_ms); + if (cpu_usage_res != EXIT_SUCCESS) { return EXIT_FAILURE; } + + // compute the '%' of CPU used during the last interval + cpu_usage_ms = final_cpu_ms - idle_cpu_ms; + cpu_usage_pct = cpu_usage_ms / (REPORT_INTV_SEC * 1000.0); + + ok( + cpu_usage_pct < MAX_ALLOWED_CPU_USAGE, "ProxySQL CPU usage should be below expected: (Exp: %%%lf, Act: %%%lf)", + MAX_ALLOWED_CPU_USAGE, cpu_usage_pct + ); + + diag("Testing SSL and compressed connections..."); + cpu_usage_res = get_idle_conns_cpu_usage(cl, CLIENT_SSL|CLIENT_COMPRESS, idle_cpu_ms, final_cpu_ms); + if (cpu_usage_res != EXIT_SUCCESS) { return EXIT_FAILURE; } + + // compute the '%' of CPU used during the last interval + cpu_usage_ms = final_cpu_ms - idle_cpu_ms; + cpu_usage_pct = cpu_usage_ms / (REPORT_INTV_SEC * 1000.0); + + ok( + cpu_usage_pct < MAX_ALLOWED_CPU_USAGE, "ProxySQL CPU usage should be below expected: (Exp: %%%lf, Act: %%%lf)", + MAX_ALLOWED_CPU_USAGE, cpu_usage_pct + ); + + return exit_status(); +} diff --git a/test/tap/tests/test_unshun_algorithm-t.cpp b/test/tap/tests/test_unshun_algorithm-t.cpp index b5924b10a..cb8163167 100644 --- a/test/tap/tests/test_unshun_algorithm-t.cpp +++ b/test/tap/tests/test_unshun_algorithm-t.cpp @@ -40,6 +40,7 @@ int shunn_server(MYSQL* proxysql_admin, uint32_t i, uint32_t j) { std::string t_simulator_error_query { "PROXYSQL_SIMULATOR mysql_error %d 127.0.0.1:330%d 1234" }; std::string simulator_error_q_i {}; string_format(t_simulator_error_query, simulator_error_q_i, i, j); + diag("%s: running query: %s", tap_curtime().c_str(), simulator_error_q_i.c_str()); MYSQL_QUERY(proxysql_admin, simulator_error_q_i.c_str()); return EXIT_SUCCESS; @@ -63,18 +64,21 @@ int wakup_target_server(MYSQL* proxysql_mysql, uint32_t i) { string_format(t_simple_do_query, simple_do_query, i); mysql_query(proxysql_mysql, simple_do_query.c_str()); + diag("%s: running query: %s", tap_curtime().c_str(), simple_do_query.c_str()); sleep(SHUN_RECOVERY_TIME * 2); mysql_query(proxysql_mysql, simple_do_query.c_str()); + diag("%s: running query: %s", tap_curtime().c_str(), simple_do_query.c_str()); return EXIT_SUCCESS; } int server_status_checker(MYSQL* admin, const string& f_st, const string& n_st, uint32_t i) { std::string t_server_status_query { - "SELECT status FROM runtime_mysql_servers WHERE port=330%d order by hostgroup_id" + "SELECT status,hostgroup_id FROM runtime_mysql_servers WHERE port=330%d order by hostgroup_id" }; std::string server_status_query {}; string_format(t_server_status_query, server_status_query, i); + diag("%s: running query: %s", tap_curtime().c_str(), server_status_query.c_str()); MYSQL_QUERY(admin, server_status_query.c_str()); MYSQL_RES* status_res = mysql_store_result(admin); @@ -89,7 +93,8 @@ int server_status_checker(MYSQL* admin, const string& f_st, const string& n_st, while (( row = mysql_fetch_row(status_res) )) { std::string status { row[0] }; - diag("Status found for server '127.0.0.1:330%d' was '%s'", i, status.c_str()); + std::string hgid { row[1] }; + diag("Status found for server '%s:127.0.0.1:330%d' was '%s'", hgid.c_str(), i, status.c_str()); if (row_num == 0) { if (status != f_st) { unexp_row_value = true;