From 721fabc922eb4fbbc64a9ed7bf991af4f2b187bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Thu, 18 Nov 2021 20:17:14 +0100 Subject: [PATCH] TAP test to benchmark fast routing Adding a TAP test to benchmark 2000 HGs and fast routing. It uses sqlite3 server as a backend. It compare a single HG vs 2000 HGs. It benchmarks with and without Query Cache. --- test/tap/tests/sqlite_3_server_test.h | 91 +++++++ test/tap/tests/test_sqlite3_server-t.cpp | 90 +------ ...test_sqlite3_server_and_fast_routing-t.cpp | 238 ++++++++++++++++++ 3 files changed, 330 insertions(+), 89 deletions(-) create mode 100644 test/tap/tests/sqlite_3_server_test.h create mode 100644 test/tap/tests/test_sqlite3_server_and_fast_routing-t.cpp diff --git a/test/tap/tests/sqlite_3_server_test.h b/test/tap/tests/sqlite_3_server_test.h new file mode 100644 index 000000000..a31510466 --- /dev/null +++ b/test/tap/tests/sqlite_3_server_test.h @@ -0,0 +1,91 @@ +/** + * @brief Extract the current 'sqliteserver-mysql_ifaces' from ProxySQL config. + * @param proxysql_admin An already opened connection to ProxySQL Admin. + * @return EXIT_SUCCESS, or one of the following error codes: + * - EINVAL if supplied 'proxysql_admin' is NULL. + * - '-1' in case of ProxySQL returns an 'NULL' row for the query selecting + * the variable 'sqliteserver-read_only'. + * - EXIT_FAILURE in case other operation failed. + */ +int get_sqlite3_ifaces(MYSQL* proxysql_admin, std::string& sqlite3_ifaces) { + if (proxysql_admin == NULL) { + return EINVAL; + } + + int res = EXIT_FAILURE; + + MYSQL_QUERY( + proxysql_admin, + "SELECT * FROM global_variables WHERE Variable_name='sqliteserver-mysql_ifaces'" + ); + + MYSQL_RES* admin_res = mysql_store_result(proxysql_admin); + if (!admin_res) { + diag("'mysql_store_result' at line %d failed: %s", __LINE__, mysql_error(proxysql_admin)); + goto cleanup; + } + + { + MYSQL_ROW row = mysql_fetch_row(admin_res); + if (!row || row[0] == nullptr || row[1] == nullptr) { + diag("'mysql_fetch_row' at line %d returned 'NULL'", __LINE__); + res = -1; + goto cleanup; + } + + std::string _sqlite3_ifaces { row[1] }; + sqlite3_ifaces = _sqlite3_ifaces; + res = EXIT_SUCCESS; + } + +cleanup: + + return res; +} + +int extract_sqlite3_host_port(MYSQL* proxysql_admin, std::pair& host_port) { + if (proxysql_admin == nullptr) { return EINVAL; } + int res = EXIT_SUCCESS; + + std::string sqlite3_ifaces {}; + int ifaces_err = get_sqlite3_ifaces(proxysql_admin, sqlite3_ifaces); + + // ProxySQL is likely to have been launched without "--sqlite3-server" flag + if (ifaces_err == -1) { + diag("ProxySQL was launched without '--sqlite3-server' flag"); + res = EXIT_FAILURE; + return res; + } + + // Extract the correct port to connect to SQLite server + std::string::size_type colon_pos = sqlite3_ifaces.find(":"); + if (colon_pos == std::string::npos) { + diag("ProxySQL returned a malformed 'sqliteserver-mysql_ifaces': %s", sqlite3_ifaces.c_str()); + res = EXIT_FAILURE; + return res; + } + + std::string sqlite3_host { sqlite3_ifaces.substr(0, colon_pos) }; + std::string sqlite3_port { sqlite3_ifaces.substr(colon_pos + 1) }; + + // Check that port has valid conversion + char* end_pos = nullptr; + int i_sqlite3_port = std::strtol(sqlite3_port.c_str(), &end_pos, 10); + + if (errno == ERANGE || (end_pos != &sqlite3_port.back() + 1)) { + diag( + "ProxySQL returned a invalid port number within 'sqliteserver-mysql_ifaces': %s", + sqlite3_ifaces.c_str() + ); + res = EXIT_FAILURE; + return res; + } + + if (res == EXIT_SUCCESS) { + host_port = { sqlite3_host, i_sqlite3_port }; + } + + return res; +} + + diff --git a/test/tap/tests/test_sqlite3_server-t.cpp b/test/tap/tests/test_sqlite3_server-t.cpp index 860e86091..49832c3e7 100644 --- a/test/tap/tests/test_sqlite3_server-t.cpp +++ b/test/tap/tests/test_sqlite3_server-t.cpp @@ -41,50 +41,7 @@ using query_spec = std::tuple; const int sqlite3_port = 0; -/** - * @brief Extract the current 'sqliteserver-mysql_ifaces' from ProxySQL config. - * @param proxysql_admin An already opened connection to ProxySQL Admin. - * @return EXIT_SUCCESS, or one of the following error codes: - * - EINVAL if supplied 'proxysql_admin' is NULL. - * - '-1' in case of ProxySQL returns an 'NULL' row for the query selecting - * the variable 'sqliteserver-read_only'. - * - EXIT_FAILURE in case other operation failed. - */ -int get_sqlite3_ifaces(MYSQL* proxysql_admin, std::string& sqlite3_ifaces) { - if (proxysql_admin == NULL) { - return EINVAL; - } - - int res = EXIT_FAILURE; - - MYSQL_QUERY( - proxysql_admin, - "SELECT * FROM global_variables WHERE Variable_name='sqliteserver-mysql_ifaces'" - ); - - MYSQL_RES* admin_res = mysql_store_result(proxysql_admin); - if (!admin_res) { - diag("'mysql_store_result' at line %d failed: %s", __LINE__, mysql_error(proxysql_admin)); - goto cleanup; - } - - { - MYSQL_ROW row = mysql_fetch_row(admin_res); - if (!row || row[0] == nullptr || row[1] == nullptr) { - diag("'mysql_fetch_row' at line %d returned 'NULL'", __LINE__); - res = -1; - goto cleanup; - } - - std::string _sqlite3_ifaces { row[1] }; - sqlite3_ifaces = _sqlite3_ifaces; - res = EXIT_SUCCESS; - } - -cleanup: - - return res; -} +#include "sqlite_3_server_test.h" void fetch_and_discard_results(MYSQL_RES* result, bool verbose=false) { MYSQL_ROW row = nullptr; @@ -153,51 +110,6 @@ void execute_and_check_queries(MYSQL* proxysql_sqlite3, const std::vector& host_port) { - if (proxysql_admin == nullptr) { return EINVAL; } - int res = EXIT_SUCCESS; - - std::string sqlite3_ifaces {}; - int ifaces_err = get_sqlite3_ifaces(proxysql_admin, sqlite3_ifaces); - - // ProxySQL is likely to have been launched without "--sqlite3-server" flag - if (ifaces_err == -1) { - diag("ProxySQL was launched without '--sqlite3-server' flag"); - res = EXIT_FAILURE; - return res; - } - - // Extract the correct port to connect to SQLite server - std::string::size_type colon_pos = sqlite3_ifaces.find(":"); - if (colon_pos == std::string::npos) { - diag("ProxySQL returned a malformed 'sqliteserver-mysql_ifaces': %s", sqlite3_ifaces.c_str()); - res = EXIT_FAILURE; - return res; - } - - std::string sqlite3_host { sqlite3_ifaces.substr(0, colon_pos) }; - std::string sqlite3_port { sqlite3_ifaces.substr(colon_pos + 1) }; - - // Check that port has valid conversion - char* end_pos = nullptr; - int i_sqlite3_port = std::strtol(sqlite3_port.c_str(), &end_pos, 10); - - if (errno == ERANGE || (end_pos != &sqlite3_port.back() + 1)) { - diag( - "ProxySQL returned a invalid port number within 'sqliteserver-mysql_ifaces': %s", - sqlite3_ifaces.c_str() - ); - res = EXIT_FAILURE; - return res; - } - - if (res == EXIT_SUCCESS) { - host_port = { sqlite3_host, i_sqlite3_port }; - } - - return res; -} - /** * @brief List of the pairs holding a series of queries that should be * successfully performed against ProxySQL SQLite3 server. diff --git a/test/tap/tests/test_sqlite3_server_and_fast_routing-t.cpp b/test/tap/tests/test_sqlite3_server_and_fast_routing-t.cpp new file mode 100644 index 000000000..d90b2de03 --- /dev/null +++ b/test/tap/tests/test_sqlite3_server_and_fast_routing-t.cpp @@ -0,0 +1,238 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +using query_spec = std::tuple; + +const int sqlite3_port = 0; + +#include "sqlite_3_server_test.h" + +inline unsigned long long monotonic_time() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (((unsigned long long) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000); +} + + +int main(int argc, char** argv) { + CommandLine cl; + + std::string s; + MYSQL * proxysql_mysql = mysql_init(NULL); + std::pair host_port {}; + int host_port_err; + + MYSQL* proxysql_admin = mysql_init(NULL); + + double nofr = 0; + double fr = 0; + + plan(1); + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + goto cleanup; + } + + // Connect to ProxySQL Admin and check current SQLite3 configuration + 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) + ); + goto cleanup; + } + +// { + host_port_err = extract_sqlite3_host_port(proxysql_admin, host_port); + if (host_port_err) { + diag("Failed to get and parse 'sqliteserver-mysql_ifaces' at line '%d'", __LINE__); + goto cleanup; + } + s = "DELETE FROM mysql_servers WHERE hostgroup_id BETWEEN 1001 AND 3000"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + s = "DELETE FROM mysql_query_rules_fast_routing WHERE destination_hostgroup BETWEEN 1001 AND 3000"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + for (unsigned int i=1001; i<3000; i+=2) { + std::string s = "INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES "; + s += "(" + std::to_string(i) + ",'" + host_port.first + "'," + std::to_string(host_port.second) + ")"; + s += ","; + s += "(" + std::to_string(i+1) + ",'" + host_port.first + "'," + std::to_string(host_port.second) + ")"; + MYSQL_QUERY(proxysql_admin, s.c_str()); + s = "INSERT INTO mysql_query_rules_fast_routing (username, schemaname, flagIN, destination_hostgroup, comment) VALUES "; + s += "('" + std::string(cl.username) + "', 'randomschemaname" + std::to_string(i) + "', 0, " + std::to_string(i) + ", 'writer" + std::to_string(i) + "')"; + s += ","; + s += "('" + std::string(cl.username) + "', 'randomschemaname" + std::to_string(i) + "', 1, " + std::to_string(i+1) + ", 'reader" + std::to_string(i+1) + "')"; + MYSQL_QUERY(proxysql_admin, s.c_str()); + } + diag("Completed inserting 2000 rows in mysql_servers and mysql_query_rules_fast_routing"); + s = "DELETE FROM mysql_query_rules"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + s = "INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply) VALUES (1,1,'^SELECT 1$', 1001, 1)"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + s = "LOAD MYSQL SERVERS TO RUNTIME"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + s = "LOAD MYSQL QUERY RULES TO RUNTIME"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + + if (!mysql_real_connect(proxysql_mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + fprintf( + stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, + mysql_error(proxysql_mysql) + ); + goto cleanup; + } + { + unsigned long long begin; + for (int i=0; i<10001; i++) { + if (i==1) + begin = monotonic_time(); + int rc; + rc = mysql_query(proxysql_mysql, "SELECT 1"); + if (rc != 0) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_mysql)); + goto cleanup; + } + MYSQL_RES* result = mysql_store_result(proxysql_mysql); + mysql_free_result(result); + } + unsigned long long end = monotonic_time(); + nofr += (end - begin); + std::cerr << double( end - begin ) / 1000 << " millisecs.\n" ; + } + + s = "DELETE FROM mysql_query_rules"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + s = "INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, cache_ttl, apply) VALUES (1,1,'^SELECT 1$', 1001, 600000, 1)"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + s = "LOAD MYSQL QUERY RULES TO RUNTIME"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + + { + unsigned long long begin; + for (int i=0; i<10001; i++) { + if (i==1) + begin = monotonic_time(); + int rc; + rc = mysql_query(proxysql_mysql, "SELECT 1"); + if (rc != 0) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_mysql)); + goto cleanup; + } + MYSQL_RES* result = mysql_store_result(proxysql_mysql); + mysql_free_result(result); + } + unsigned long long end = monotonic_time(); + std::cerr << double( end - begin ) / 1000 << " millisecs.\n" ; + nofr += (end - begin); + } + + s = "DELETE FROM mysql_query_rules"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + s = "INSERT INTO mysql_query_rules (rule_id, active, match_pattern, flagOUT) VALUES (1,1,'^SELECT 1$', 0)"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + s = "INSERT INTO mysql_query_rules (rule_id, active, match_pattern, flagOUT) VALUES (2,1,'^SELECT 2$', 1)"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + s = "LOAD MYSQL QUERY RULES TO RUNTIME"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + mysql_select_db(proxysql_mysql, "randomschemaname2085"); + { + unsigned long long begin; + for (int i=0; i<5001; i++) { + if (i==1) + begin = monotonic_time(); + int rc; + rc = mysql_query(proxysql_mysql, "SELECT 1"); + if (rc != 0) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_mysql)); + goto cleanup; + } + MYSQL_RES* result = mysql_store_result(proxysql_mysql); + mysql_free_result(result); + rc = mysql_query(proxysql_mysql, "SELECT 2"); + if (rc != 0) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_mysql)); + goto cleanup; + } + result = mysql_store_result(proxysql_mysql); + mysql_free_result(result); + } + unsigned long long end = monotonic_time(); + std::cerr << double( end - begin ) / 1000 << " millisecs.\n" ; + fr += (end - begin); + } + s = "DELETE FROM mysql_query_rules"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + s = "INSERT INTO mysql_query_rules (rule_id, active, match_pattern, flagOUT, cache_ttl) VALUES (1,1,'^SELECT 1$', 0, 600000)"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + s = "INSERT INTO mysql_query_rules (rule_id, active, match_pattern, flagOUT, cache_ttl) VALUES (2,1,'^SELECT 2$', 1, 600000)"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + s = "LOAD MYSQL QUERY RULES TO RUNTIME"; + diag("Executing: %s", s.c_str()); + MYSQL_QUERY(proxysql_admin, s.c_str()); + { + unsigned long long begin; + for (int i=0; i<5001; i++) { + if (i==1) + begin = monotonic_time(); + int rc; + rc = mysql_query(proxysql_mysql, "SELECT 1"); + if (rc != 0) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_mysql)); + goto cleanup; + } + MYSQL_RES* result = mysql_store_result(proxysql_mysql); + mysql_free_result(result); + rc = mysql_query(proxysql_mysql, "SELECT 2"); + if (rc != 0) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_mysql)); + goto cleanup; + } + result = mysql_store_result(proxysql_mysql); + mysql_free_result(result); + } + unsigned long long end = monotonic_time(); + std::cerr << double( end - begin ) / 1000 << " millisecs.\n" ; + fr += (end - begin); + } + ok (fr < (nofr * 2) , "Times for: Single HG = %dms , multi HG = %dms", (int)(nofr/1000), (int)(fr/1000)); +cleanup: + + mysql_close(proxysql_admin); + mysql_close(proxysql_mysql); + + return exit_status(); +}