diff --git a/deps/Makefile b/deps/Makefile index 0010fb24ff..e3e973c96d 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -248,6 +248,8 @@ endif cd mariadb-client-library/mariadb_client && patch libmariadb/mariadb_dyncol.c < ../mariadb_dyncol.c-multiplication-overflow.patch cd mariadb-client-library/mariadb_client && patch libmariadb/ma_array.c < ../ma_array.c-multiplication-overflow.patch cd mariadb-client-library/mariadb_client && patch zlib/zutil.c < ../zutil.c-multiplication-overflow.patch + cd mariadb-client-library/mariadb_client && patch libmariadb/mariadb_rpl.c < ../mariadb_rpl.c.patch + cd mariadb-client-library/mariadb_client && patch include/mariadb_rpl.h < ../mariadb_rpl.h.patch cd mariadb-client-library/mariadb_client && CC=${CC} CXX=${CXX} ${MAKE} mariadbclient # cd mariadb-client-library/mariadb_client/include && make my_config.h diff --git a/deps/mariadb-client-library/mariadb_rpl.c.patch b/deps/mariadb-client-library/mariadb_rpl.c.patch new file mode 100644 index 0000000000..cbdd85c744 --- /dev/null +++ b/deps/mariadb-client-library/mariadb_rpl.c.patch @@ -0,0 +1,16 @@ +@@ -386,6 +386,15 @@ + memcpy(rpl_event->event.rows.row_data, ev, rpl_event->event.rows.row_data_size); + } + break; ++ case PREVIOUS_GTIDS_LOG_EVENT: ++ case ANONYMOUS_GTID_LOG_EVENT: ++ case WRITE_ROWS_EVENT: ++ case UPDATE_ROWS_EVENT: ++ case DELETE_ROWS_EVENT: ++ case GTID_LOG_EVENT: ++ case HEARTBEAT_LOG_EVENT_V2: ++ case ROWS_QUERY_LOG_EVENT: ++ break; + default: + free(rpl_event); + return NULL; diff --git a/deps/mariadb-client-library/mariadb_rpl.h.patch b/deps/mariadb-client-library/mariadb_rpl.h.patch new file mode 100644 index 0000000000..039536ef51 --- /dev/null +++ b/deps/mariadb-client-library/mariadb_rpl.h.patch @@ -0,0 +1,9 @@ +@@ -94,6 +94,8 @@ + VIEW_CHANGE_EVENT= 37, + XA_PREPARE_LOG_EVENT= 38, + ++ HEARTBEAT_LOG_EVENT_V2 = 41, ++ + /* + Add new events here - right above this comment! + Existing events (except ENUM_END_EVENT) should never change their numbers diff --git a/include/MySQL_Data_Stream.h b/include/MySQL_Data_Stream.h index 8cedfdea1d..7f24206605 100644 --- a/include/MySQL_Data_Stream.h +++ b/include/MySQL_Data_Stream.h @@ -212,6 +212,7 @@ class MySQL_Data_Stream myconn->statuses.myconnpoll_get++; mc->myds=this; encrypted = false; // this is the default + // PMC-10005 // we handle encryption for backend // // we have a similar code in MySQL_Connection diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 8bf647455f..032e38e96a 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -3997,16 +3997,36 @@ int MySQL_Session::get_pkts_from_client(bool& wrong_pass, PtrSize_t& pkt) { handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_SEND_LONG_DATA(pkt); break; case _MYSQL_COM_BINLOG_DUMP: - case _MYSQL_COM_REGISTER_SLAVE: case _MYSQL_COM_BINLOG_DUMP_GTID: + case _MYSQL_COM_REGISTER_SLAVE: // In this switch we handle commands that download binlog events from MySQL - // servers. For this commands a lot of the features provided by ProxySQL + // servers. For these commands a lot of the features provided by ProxySQL // aren't useful, like multiplexing, query parsing, etc. For this reason, - // ProxySQL enable fast_forward when it receives these commands.  - proxy_info( - "COM_REGISTER_SLAVE, COM_BINLOG_DUMP or COM_BINLOG_DUMP_GTID received. " - "Changing session fast foward to true\n" - ); + // ProxySQL enables fast_forward when it receives these commands.  + { + // we use a switch to write the command in the info message + std::string q = "Received command "; + switch ((enum_mysql_command)c) { + case _MYSQL_COM_BINLOG_DUMP: + q += "MYSQL_COM_BINLOG_DUMP"; + break; + case _MYSQL_COM_BINLOG_DUMP_GTID: + q += "MYSQL_COM_BINLOG_DUMP_GTID"; + break; + case _MYSQL_COM_REGISTER_SLAVE: + q += "MYSQL_COM_REGISTER_SLAVE"; + break; + default: + assert(0); + break; + }; + // we add the client details in the info message + if (client_myds && client_myds->addr.addr) { + q += " from client " + std::string(client_myds->addr.addr) + ":" + std::to_string(client_myds->addr.port); + } + q += " . Changing session fast_forward to true"; + proxy_info("%s\n", q.c_str()); + } session_fast_forward = true; if (client_myds->PSarrayIN->len) { @@ -4047,6 +4067,33 @@ int MySQL_Session::get_pkts_from_client(bool& wrong_pass, PtrSize_t& pkt) { // by 'mariadb' library. Otherwise 'MySQL_Thread' will threat this // 'MySQL_Data_Stream' as library handled. mybe->server_myds->DSS = STATE_READY; + // myds needs to have encrypted value set correctly + { + MySQL_Data_Stream * myds = mybe->server_myds; + MySQL_Connection * myconn = myds->myconn; + assert(myconn != NULL); + // PMC-10005 + // if backend connection uses SSL we will set + // encrypted = true and we will start using the SSL structure + // directly from P_MARIADB_TLS structure. + MYSQL *mysql = myconn->mysql; + if (mysql && myconn->ret_mysql) { + if (mysql->options.use_ssl == 1) { + P_MARIADB_TLS * matls = (P_MARIADB_TLS *)mysql->net.pvio->ctls; + if (matls != NULL) { + myds->encrypted = true; + myds->ssl = (SSL *)matls->ssl; + myds->rbio_ssl = BIO_new(BIO_s_mem()); + myds->wbio_ssl = BIO_new(BIO_s_mem()); + SSL_set_bio(myds->ssl, myds->rbio_ssl, myds->wbio_ssl); + } else { + // if mysql->options.use_ssl == 1 but matls == NULL + // it means that ProxySQL tried to use SSL to connect to the backend + // but the backend didn't support SSL + } + } + } + } set_status(FAST_FORWARD); // we can set status to FAST_FORWARD } diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index fcd06e045b..bede6d49be 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -1115,6 +1115,7 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { break; case ASYNC_CONNECT_SUCCESSFUL: if (mysql && ret_mysql) { + // PMC-10005 // we handle encryption for backend // // we have a similar code in MySQL_Data_Stream::attach_connection() diff --git a/test/tap/tests/test_backend_conn_ping-t.cpp b/test/tap/tests/test_backend_conn_ping-t.cpp index a6052aee69..f1faf8005e 100644 --- a/test/tap/tests/test_backend_conn_ping-t.cpp +++ b/test/tap/tests/test_backend_conn_ping-t.cpp @@ -555,6 +555,13 @@ int main(int, char**) { diag("Restoring previous 'MySQL' servers infra config..."); + { + // do some cleanup + string query = "SET mysql-free_connections_pct=5"; + diag("%s", query.c_str()); + MYSQL_QUERY(proxy_admin, query.c_str()); + MYSQL_QUERY(proxy_admin, "LOAD MYSQL VARIABLES TO RUNTIME"); + } { for (const auto& server_old_config : servers_old_configs) { const mysql_res_row& res_row = server_old_config.first; @@ -568,6 +575,7 @@ int main(int, char**) { } } + sleep(2); // wait for the cleanup to happen mysql_close(proxy_admin); return exit_status(); diff --git a/test/tap/tests/test_binlog_fast_forward-t.cpp b/test/tap/tests/test_binlog_fast_forward-t.cpp new file mode 100644 index 0000000000..42f70f8ad8 --- /dev/null +++ b/test/tap/tests/test_binlog_fast_forward-t.cpp @@ -0,0 +1,161 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" +#include "mariadb_rpl.h" + +MYSQL * mysqladmin = NULL; +CommandLine cl; + +#define NHB 10 // the number of heartbeat to wait + +int run_queries_sets(std::vector& queries, MYSQL *my, const std::string& message_prefix) { + for (std::vector::iterator it = queries.begin(); it != queries.end(); it++) { + std::string q = *it; + diag("%s: %s", message_prefix.c_str(), q.c_str()); + MYSQL_QUERY(my, q.c_str()); + } + return 0; +} + +std::vector adminQ_set1 = { + "SET mysql-have_ssl='false'", + "LOAD MYSQL VARIABLES TO RUNTIME", + "UPDATE mysql_servers SET use_ssl=1", + "LOAD MYSQL SERVERS TO RUNTIME", +}; + + +int pull_replication(MYSQL *mysql, int server_id) { + MARIADB_RPL_EVENT *event= NULL; + MARIADB_RPL *rpl= mariadb_rpl_init(mysql); + rpl->server_id= server_id; + rpl->start_position= 4; + rpl->flags= MARIADB_RPL_BINLOG_SEND_ANNOTATE_ROWS; + + if (mariadb_rpl_open(rpl)) + return exit_status(); + + int num_heartbeats = 0; + int num_events = 0; + + diag("Pulling all binlog events, it may take some time, be patient..."); + /* + * we iterate through all the events, + * and then wait NHB heartbeats + */ + while(num_heartbeats < NHB && (event= mariadb_rpl_fetch(rpl, event))) + { + num_events++; + bool print_diag = false; + if (event->event_type == HEARTBEAT_LOG_EVENT_V2 || event->event_type == HEARTBEAT_LOG_EVENT) { + num_heartbeats++; + print_diag = true; + } + if (print_diag == false) { + // we try to not flood the log + if (num_events < 10) { + print_diag = true; + } else if (num_events < 100) { + if (num_events % 10 == 0) { + print_diag = true; + } + } else if (num_events < 1000) { + if (num_events % 100 == 0) { + print_diag = true; + } + } else { + if (num_events % 1000 == 0) { + print_diag = true; + } + } + } + if (print_diag == true) + diag("%s: server_id %d , event: %d , received events: %d , received heartbeats: %d", tap_curtime().c_str(), server_id, event->event_type, num_events, num_heartbeats); + } + // we expects NHB heartbeats + ok(num_heartbeats == NHB , "For server_id %d received %d heartbeats", server_id, num_heartbeats); + mariadb_free_rpl_event(event); + mariadb_rpl_close(rpl); + if (num_heartbeats != NHB) { + return 1; + } + return 0; +} + + +std::vector repl_queries_set1 = { // only 1 set, maybe more later + "SET NAMES latin1", + "SET NAMES utf8", + "SET @master_binlog_checksum = 'NONE'", + "SET @master_heartbeat_period=2000000000", // 2 seconds heartbeat +}; + +int setup_replication(int server_id, bool frontend_ssl, bool backend_ssl, std::vector& mysql_queries) { + diag("Running %s using server_id %d , frontend_ssl = %s , backend_ssl = %s", __func__ , server_id, (frontend_ssl ? "TRUE" : "FALSE") , (backend_ssl ? "TRUE" : "FALSE")); + MYSQL * mysql = mysql_init(NULL); + + std::vector admin_queries = {}; + admin_queries.push_back(std::string("SET mysql-have_ssl='") + std::string(frontend_ssl ? "true" : "false") + "'"); + admin_queries.push_back("LOAD MYSQL VARIABLES TO RUNTIME"); + admin_queries.push_back(std::string("UPDATE mysql_servers SET use_ssl=") + std::string(backend_ssl ? "1" : "0")); + admin_queries.push_back("LOAD MYSQL SERVERS TO RUNTIME"); + + if (!mysql) + return exit_status(); + if (frontend_ssl) { + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); + } + if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + //if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, 3306, NULL, 0)) { + fprintf(stderr, "Failed to connect to database: Error: %s\n", mysql_error(mysql)); + return exit_status(); + } + if (run_queries_sets(admin_queries, mysqladmin, "Running on Admin")) + return exit_status(); + if (run_queries_sets(mysql_queries, mysql, "Running on MySQL")) + return exit_status(); + int rc = pull_replication(mysql, server_id); + ok(rc==0,"Result with server_id %d , frontend_ssl = %s , backend_ssl = %s , rc = %d", server_id, (frontend_ssl ? "TRUE" : "FALSE") , (backend_ssl ? "TRUE" : "FALSE") , rc); + if (rc != 0) + return exit_status(); + mysql_close(mysql); + return 0; +} + +int main(int argc, char** argv) { + + if(cl.getEnv()) + return exit_status(); + + plan(8); // each test has 2 OK + + mysqladmin = mysql_init(NULL); + if (!mysqladmin) + return exit_status(); + + if (!mysql_real_connect(mysqladmin, 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(mysqladmin)); + return exit_status(); + } + // we now test various combination + setup_replication(11, false, false, repl_queries_set1); + setup_replication(12, true, false, repl_queries_set1); + setup_replication(13, false, true, repl_queries_set1); + setup_replication(14, true, true, repl_queries_set1); + + mysql_close(mysqladmin); + + return exit_status(); +} + diff --git a/test/tap/tests/test_com_register_slave_enables_fast_forward-t.cpp b/test/tap/tests/test_com_register_slave_enables_fast_forward-t.cpp index c847c4f705..33f3055547 100644 --- a/test/tap/tests/test_com_register_slave_enables_fast_forward-t.cpp +++ b/test/tap/tests/test_com_register_slave_enables_fast_forward-t.cpp @@ -7,13 +7,18 @@ * listen binlog events. It listen two times, one after sending a query that do * not disable multiplexing and the other after sending a query that disables * multiplexing. + * + * The repository for test_binlog_reader-t is: + * https://github.com/ProxySQL/proxysql_binlog_test */ #include + #include "tap.h" int main(int argc, char** argv) { + plan(1); const std::string test_deps_path = getenv("TEST_DEPS"); const int test_binlog_reader_res = system((test_deps_path + "/test_binlog_reader-t").c_str());