From b4eab27686e48007745f19db03e375a8938eda56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Mon, 6 May 2024 15:10:15 +0200 Subject: [PATCH] Add new TAP test 'mysql_reconnect' Tests reconnect with and without SSL for 'libmariadb' and 'libmysql'. --- test/tap/tap/utils.cpp | 21 ++ test/tap/tap/utils.h | 7 + test/tap/tests/Makefile | 8 + test/tap/tests/mysql_reconnect.cpp | 200 ++++++++++++++++++ .../eof_mixed_flags_queries-t.cpp | 21 -- 5 files changed, 236 insertions(+), 21 deletions(-) create mode 100644 test/tap/tests/mysql_reconnect.cpp diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index e8633536f..94c7a6ff8 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -1033,6 +1033,27 @@ cleanup: return res; } +vector> get_all_bin_vec(size_t tg_size) { + vector> all_bin_strs {}; + vector bin_vec(tg_size, 0); + + for (size_t i = 0; i < tg_size; i++) { + if (i == 0) { + bin_vec[i] = 0; + for (const vector p : get_permutations(bin_vec)) { + all_bin_strs.push_back(p); + } + } + + bin_vec[i] = 1; + for (const vector p : get_permutations(bin_vec)) { + all_bin_strs.push_back(p); + } + } + + return all_bin_strs; +} + string to_string(const conn_cnf_t& cnf) { return string { string { "{" } diff --git a/test/tap/tap/utils.h b/test/tap/tap/utils.h index 17518c4f8..6f45e5572 100644 --- a/test/tap/tap/utils.h +++ b/test/tap/tap/utils.h @@ -424,6 +424,13 @@ std::vector> get_permutations(const std::vector& elem_set) { return result; } +/** + * @brief Generates permutations of binary vectors of the specified size. + * @param tg_size The target size of the binary vectors. + * @return The generated permutations. + */ +std::vector> get_all_bin_vec(size_t tg_size); + /** * @brief Struct holding options on how to performs connections for 'EOF' tests. */ diff --git a/test/tap/tests/Makefile b/test/tap/tests/Makefile index 1a8387f1c..a368a5d49 100644 --- a/test/tap/tests/Makefile +++ b/test/tap/tests/Makefile @@ -191,6 +191,8 @@ tests: tests-cpp \ setparser_test \ reg_test_3504-change_user_libmariadb_helper \ reg_test_3504-change_user_libmysql_helper \ + mysql_reconnect_libmariadb-t \ + mysql_reconnect_libmysql-t \ setparser_test2 setparser_test2-t \ setparser_test3 setparser_test3-t \ set_testing-240.csv \ @@ -289,6 +291,12 @@ reg_test_3504-change_user_libmariadb_helper: reg_test_3504-change_user_helper.cp reg_test_3504-change_user_libmysql_helper: reg_test_3504-change_user_helper.cpp $(TAP_LDIR)/libtap.so $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -Wl,-Bstatic -lmysqlclient -ltap_mysql57 $(CUSTOMARGS) -o $@ +mysql_reconnect_libmariadb-t: mysql_reconnect.cpp $(TAP_LDIR)/libtap.so + $(CXX) -DDISABLE_WARNING_COUNT_LOGGING $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(STATIC_LIBS) -o $@ + +mysql_reconnect_libmysql-t: mysql_reconnect.cpp $(TAP_LDIR)/libtap_mysql8.a + $(CXX) -DLIBMYSQL_HELPER8 -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL8_IDIR) -I$(TEST_MYSQL8_EDIR) -L$(TEST_MYSQL8_LDIR) -lmysqlclient -ltap_mysql8 -lresolv $(CUSTOMARGS) -o $@ + test_clickhouse_server_libmysql-t: test_clickhouse_server-t.cpp $(TAP_LDIR)/libtap.so $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient -ltap_mysql57 $(CUSTOMARGS) -o $@ diff --git a/test/tap/tests/mysql_reconnect.cpp b/test/tap/tests/mysql_reconnect.cpp new file mode 100644 index 000000000..8962d009d --- /dev/null +++ b/test/tap/tests/mysql_reconnect.cpp @@ -0,0 +1,200 @@ +/** + * @file mysql_reconnect.cpp + * @brief Check that reconnect works against ProxySQL with/without SSL enabled. + * @details The test requires to be compiled against libmariadb and libmysql. This allows to perform a + * regression test against libmysql regarding reconnect and SSL session tickets. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef LIBMYSQL_HELPER8 +#include +#else +#include "mysql.h" +#endif + +#include "utils.h" +#include "tap.h" +#include "command_line.h" + +using std::string; +using std::vector; + +struct _conn_cnf_t { + bool ssl; + bool eof; +}; + +int test_reconnect(const CommandLine& cl, const _conn_cnf_t& cnf) { + MYSQL* proxy = mysql_init(NULL); + + bool reconnect = 1; + int cflags = 0; + + if (cnf.ssl) { +#ifdef LIBMYSQL_HELPER8 + enum mysql_ssl_mode ssl_mode = SSL_MODE_REQUIRED; + mysql_options(proxy, MYSQL_OPT_SSL_MODE, &ssl_mode); +#else + mysql_ssl_set(proxy, NULL, NULL, NULL, NULL, NULL); + cflags |= CLIENT_SSL; +#endif + } + + if (cnf.eof) { + proxy->options.client_flag |= CLIENT_DEPRECATE_EOF; + } + + mysql_options(proxy, MYSQL_OPT_RECONNECT, &reconnect); + cflags |= CLIENT_REMEMBER_OPTIONS; + + const string TG_BACKEND { get_env_str("TG_BACKEND", "PROXYSQL") }; + + const char* user = cl.username; + const char* pass = cl.password; + const char* host = cl.host; + int port = cl.port; + + if (TG_BACKEND == "MYSQL") { + port = cl.mysql_port; + } + + diag( + "Creating initial conn against ProxySQL host:'%s', port:'%d', user:'%s', pass:'%s'", + cl.host, cl.port, cl.username, cl.password + ); + + if (!mysql_real_connect(proxy, host, user, pass, NULL, port, NULL, cflags)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy)); + return EXIT_FAILURE; + } + +#ifdef LIBMYSQL_HELPER8 + void* ssl_session_data = nullptr; + + if (cnf.ssl) { + ssl_session_data = mysql_get_ssl_session_data(proxy, 0, nullptr); + if (ssl_session_data) { + mysql_options(proxy, MYSQL_OPT_SSL_SESSION_DATA, ssl_session_data); + } + } +#endif + + const char* admin_user = cl.admin_username; + const char* admin_pass = cl.admin_password; + const char* admin_host = cl.admin_host; + int admin_port = cl.admin_port; + + if (TG_BACKEND == "MYSQL") { + admin_user = cl.mysql_username; + admin_pass = cl.mysql_password; + admin_port = cl.mysql_port; + } + + MYSQL* admin = mysql_init(NULL); + + diag( + "Creating Admin conn against ProxySQL host:'%s', port:'%d', user:'%s', pass:'%s'", + admin_host, admin_port, admin_user, admin_pass + ); + if (!mysql_real_connect(admin, admin_host, admin_user, admin_pass, NULL, admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); + return EXIT_FAILURE; + } + + pid_t pid = getpid(); + const string s_pid { std::to_string(pid) }; + + std::thread query_thread([proxy, s_pid] () { + const string query { "/* client_pid=" + s_pid + " */ SELECT SLEEP(20)" }; + int rc = mysql_query(proxy, query.c_str()); + + ok(rc != 0, "Query should exit with error rc:%d, err:'%s'", rc, mysql_error(proxy)); + + rc = mysql_query(proxy, "DO 1"); + if (rc) { + diag("Simple query failed after reconnect query:'%s', err:'%s'", "DO 1", mysql_error(proxy)); + } + + ok(rc == 0, "Second query should succeed (reconnect) rc:%d, err:'%s'", rc, mysql_error(proxy)); + }); + + const string cond_query { + TG_BACKEND == "PROXYSQL" ? + "SELECT IIF(" + "(SELECT COUNT(*) FROM stats_mysql_processlist WHERE" + " info LIKE '%client_pid=" + s_pid + "%')=1, 'TRUE', 'FALSE')" : + "SELECT IF(" + "(SELECT COUNT(*) FROM information_schema.processlist WHERE" + " info LIKE '%client_pid=" + s_pid + "%' AND state='User sleep')=1, 'TRUE', 'FALSE')" + }; + + int wres = wait_for_cond(admin, cond_query.c_str(), 60); + + const string ext_query { + TG_BACKEND == "PROXYSQL" ? + "SELECT SessionID FROM stats_mysql_processlist WHERE info LIKE '%client_pid=" + s_pid + "%'" : + "SELECT ID FROM information_schema.processlist WHERE info LIKE '%client_pid=" + s_pid + "%'" + " AND state='User sleep'" + }; + + ext_val_t ext_sess_id = mysql_query_ext_val(admin, ext_query, int64_t(0)); + + if (ext_sess_id.err != EXIT_SUCCESS) { + const string err { get_ext_val_err(admin, ext_sess_id) }; + diag("Failed getting 'SessionID' query:`%s`, err:`%s`", ext_query.c_str(), err.c_str()); + goto cleanup; + } + + { + const string kill_sess_query { "KILL CONNECTION " + std::to_string(ext_sess_id.val) }; + mysql_query(admin, kill_sess_query.c_str()); + } + +cleanup: + + { + query_thread.join(); + + mysql_close(admin); +#ifdef LIBMYSQL_HELPER8 + if (ssl_session_data) { + mysql_free_ssl_session_data(proxy, ssl_session_data); + } +#endif + mysql_close(proxy); + } + + return EXIT_SUCCESS; +} + + +int main(int argc, char** argv) { + CommandLine cl; + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return EXIT_FAILURE; + } + + const auto& bin_vecs { get_all_bin_vec(2) }; + plan(bin_vecs.size() * 2); + + for (const vector vec : bin_vecs) { + _conn_cnf_t conf { vec[0], vec[1] }; + diag("Testing reconnect with config ssl:%d, eof:%d", conf.ssl, conf.eof); + + int rc = test_reconnect(cl, conf); + if (rc) { + diag("Reconnect failed, aborting further testing... rc:%d, ssl:%d, eof:%d", rc, conf.ssl, conf.eof); + break; + } + } + + return exit_status(); +} diff --git a/test/tap/tests_with_deps/deprecate_eof_support/eof_mixed_flags_queries-t.cpp b/test/tap/tests_with_deps/deprecate_eof_support/eof_mixed_flags_queries-t.cpp index f6ce4b329..556d77ba5 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/eof_mixed_flags_queries-t.cpp +++ b/test/tap/tests_with_deps/deprecate_eof_support/eof_mixed_flags_queries-t.cpp @@ -28,27 +28,6 @@ using std::string; using std::vector; -vector> get_all_bin_vec(size_t tg_size) { - vector> all_bin_strs {}; - vector bin_vec(tg_size, 0); - - for (size_t i = 0; i < tg_size; i++) { - if (i == 0) { - bin_vec[i] = 0; - for (const vector p : get_permutations(bin_vec)) { - all_bin_strs.push_back(p); - } - } - - bin_vec[i] = 1; - for (const vector p : get_permutations(bin_vec)) { - all_bin_strs.push_back(p); - } - } - - return all_bin_strs; -} - vector gen_all_configs(const string& ff_user) { vector> all_bin_vec { get_all_bin_vec(5) }; std::sort(all_bin_vec.begin(), all_bin_vec.end());