From c105a71317bccc094edaa177df43fb43b9934dc1 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Thu, 20 Sep 2018 16:51:58 +0300 Subject: [PATCH] MXS-2047 Create test that reveals problem If * BLR replicates from a node in a Galera cluster and * writes are made to all nodes in that cluster, then * if a slave to BLR is stopped when it has received an event originating in a node different than the one BLR is replicating from the subsequent (re)starting of the slave will fail because BLR looks for the last event from a file whose path contains the server id of the node where the event originates, although it should look for it in the file whose path contains the server id of the node from which BLR replicates. --- maxscale-system-test/CMakeLists.txt | 3 + ...axscale.cnf.template.mxs2047_blr_gtid_path | 26 ++ .../mxs2047_blr_gtid_path.cpp | 293 ++++++++++++++++++ 3 files changed, 322 insertions(+) create mode 100644 maxscale-system-test/cnf/maxscale.cnf.template.mxs2047_blr_gtid_path create mode 100644 maxscale-system-test/mxs2047_blr_gtid_path.cpp diff --git a/maxscale-system-test/CMakeLists.txt b/maxscale-system-test/CMakeLists.txt index cec0eb835..dc939a194 100644 --- a/maxscale-system-test/CMakeLists.txt +++ b/maxscale-system-test/CMakeLists.txt @@ -1068,5 +1068,8 @@ add_test_executable(mxs1113_schemarouter_ps.cpp mxs1113_schemarouter_ps mxs1113_ # https://jira.mariadb.org/browse/MXS-2037 add_test_executable(mxs2037_namedserver_wildcards.cpp mxs2037_namedserver_wildcards mxs2037_namedserver_wildcards LABELS namedserverfilter LIGHT REPL_BACKEND) +# MXS-2047: BLR must store full path of events +# https://jira.mariadb.org/browse/MXS-2047 +add_test_executable(mxs2047_blr_gtid_path.cpp mxs2047_blr_gtid_path mxs2047_blr_gtid_path LABELS binlogrouter GALERA_BACKEND) configure_file(templates.h.in ${CMAKE_CURRENT_BINARY_DIR}/templates.h @ONLY) diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.mxs2047_blr_gtid_path b/maxscale-system-test/cnf/maxscale.cnf.template.mxs2047_blr_gtid_path new file mode 100644 index 000000000..5ace4280d --- /dev/null +++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs2047_blr_gtid_path @@ -0,0 +1,26 @@ +[BLR] +type=service +router=binlogrouter +user=repl +password=repl +version_string=5.6.15-log +master_id=5 +server_id=4711 +mariadb10_master_gtid=On + +[BLR-Listener] +type=listener +service=BLR +protocol=MariaDBClient +port=5306 + +[CLI] +type=service +router=cli + +[CLI Listener] +type=listener +service=CLI +protocol=maxscaled +#address=localhost +socket=default diff --git a/maxscale-system-test/mxs2047_blr_gtid_path.cpp b/maxscale-system-test/mxs2047_blr_gtid_path.cpp new file mode 100644 index 000000000..cc579a04f --- /dev/null +++ b/maxscale-system-test/mxs2047_blr_gtid_path.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2022-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include +#include +#include +#include +#include +#include +#include "testconnections.h" + +using namespace std; + +namespace +{ + +// The amount of time slept between various operations that are +// expected to take some time before becoming visible. + +const int REPLICATION_SLEEP = 5; // Seconds + +string get_gtid_current_pos(TestConnections& test, MYSQL* pMysql) +{ + std::vector row = get_row(pMysql, "SELECT @@gtid_current_pos"); + + test.expect(row.size() == 1, "Did not get @@gtid_current_pos"); + + return row.empty() ? "" : row[0]; +} + +// Setup BLR to replicate from galera_000. +bool setup_blr(TestConnections& test, const string& gtid, const char* zHost, int port) +{ + test.tprintf("Connecting to BLR at %s:%d", zHost, port); + + MYSQL* pMysql = open_conn_no_db(port, zHost, "repl", "repl"); + test.expect(pMysql, "Could not open connection to %s.", zHost); + + test.try_query(pMysql, "STOP SLAVE"); + test.try_query(pMysql, "SET @@global.gtid_slave_pos='%s'", gtid.c_str()); + + mxb_assert(test.galera); + Mariadb_nodes& gc = *test.galera; + + stringstream ss; + + ss << "CHANGE MASTER "; + ss << "TO MASTER_HOST='" << gc.IP[0] << "', "; + ss << "MASTER_PORT=" << gc.port[0] << ", "; + ss << "MASTER_USER='repl', MASTER_PASSWORD='repl', MASTER_USE_GTID=Slave_pos"; + + string stmt = ss.str(); + + cout << stmt << endl; + + test.try_query(pMysql, "%s", stmt.c_str()); + test.try_query(pMysql, "START SLAVE"); + + mysql_close(pMysql); + + return test.global_result == 0; +} + +// Setup slave to replicate from BLR. +bool setup_slave(TestConnections& test, + const string& gtid, + MYSQL* pSlave, + const char* zMaxscale_host, + int maxscale_port) +{ + test.try_query(pSlave, "SET GLOBAL server_id=54"); // Remove this when galera/ms server ids are distinct + test.try_query(pSlave, "STOP SLAVE"); + test.try_query(pSlave, "RESET SLAVE"); + test.try_query(pSlave, "DROP TABLE IF EXISTS test.MXS2011"); + test.try_query(pSlave, "SET @@global.gtid_slave_pos='%s'", gtid.c_str()); + + stringstream ss; + + ss << "CHANGE MASTER TO "; + ss << "MASTER_HOST='" << zMaxscale_host << "', "; + ss << "MASTER_PORT=" << maxscale_port << ", "; + ss << "MASTER_USER='repl', MASTER_PASSWORD='repl', MASTER_USE_GTID=Slave_pos"; + + string stmt = ss.str(); + + cout << stmt << endl; + + test.try_query(pSlave, "%s", stmt.c_str()); + test.try_query(pSlave, "START SLAVE"); + + return test.global_result == 0; +} + +bool setup_schema(TestConnections& test, MYSQL* pServer) +{ + test.try_query(pServer, "DROP TABLE IF EXISTS test.MXS2011"); + test.try_query(pServer, "CREATE TABLE test.MXS2011 (i INT)"); + + return test.global_result == 0; +} + +unsigned count = 0; + +void insert(TestConnections& test, MYSQL* pMaster) +{ + stringstream ss; + ss << "INSERT INTO test.MXS2011 VALUES (" << ++count << ")"; + + string stmt = ss.str(); + + cout << stmt.c_str() << endl; + + test.try_query(pMaster, "%s", stmt.c_str()); +} + +void select(TestConnections& test, MYSQL* pSlave) +{ + my_ulonglong nRows; + unsigned long long nResult_sets; + + int rc = execute_query_num_of_rows(pSlave, "SELECT * FROM test.MXS2011", &nRows, &nResult_sets); + test.expect(rc == 0, "Execution of SELECT failed."); + + if (rc == 0) + { + mxb_assert(nResult_sets == 1); + + test.expect(nRows == count, "Expected %d rows, got %d.", count, (int)nRows); + } +} + +bool insert_select(TestConnections& test, MYSQL* pSlave, MYSQL* pMaster) +{ + insert(test, pMaster); + sleep(REPLICATION_SLEEP); // To ensure that the insert reaches the slave. + select(test, pSlave); + + return test.global_result == 0; +} + +bool insert_select(TestConnections& test, MYSQL* pSlave) +{ + Mariadb_nodes& gc = *test.galera; + + for (int i = 0; i < gc.N; ++i) + { + MYSQL* pMaster = gc.nodes[i]; + + insert_select(test, pSlave, pMaster); + } + + return test.global_result == 0; +} + +void reset_galera(TestConnections& test) +{ + Mariadb_nodes& gc = *test.galera; + + for (int i = 0; i < gc.N; ++i) + { + test.try_query(gc.nodes[i], "RESET MASTER"); + } +} + +// Ensure log_slave_updates is on. +void setup_galera(TestConnections& test) +{ + Mariadb_nodes& gc = *test.galera; + + for (int i = 0; i < gc.N; ++i) + { + gc.stash_server_settings(i); + gc.add_server_setting(i, "log_slave_updates=1"); + } +} + +// Restore log_slave_updates as it was. +void restore_galera(TestConnections& test) +{ + Mariadb_nodes& gc = *test.galera; + + for (int i = 0; i < gc.N; ++i) + { + gc.restore_server_settings(i); + } + + int rc = gc.start_replication(); + test.expect(rc == 0, "Could not start Galera cluster."); +} + +// STOP SLAVE; START SLAVE cycle. +void restart_slave(TestConnections& test, MYSQL* pSlave) +{ + Row row; + + auto replication_failed = [] (const std::string& column) + { + return column.find("from master when reading data from binary log: " + "'Requested file name") != string::npos; + }; + + test.try_query(pSlave, "STOP SLAVE"); + + row = get_row(pSlave, "SHOW SLAVE STATUS"); + auto it1 = std::find_if(row.begin(), row.end(), replication_failed); + test.expect(it1 == row.end(), "Replication failed."); + + test.try_query(pSlave, "START SLAVE"); + + sleep(REPLICATION_SLEEP); + + // The START SLAVE above fails if BLR does not handle GTIDs correctly. + row = get_row(pSlave, "SHOW SLAVE STATUS"); + auto it2 = std::find_if(row.begin(), row.end(), replication_failed); + test.expect(it2 == row.end(), "START SLAVE failed."); +} + +} + +int main(int argc, char* argv[]) +{ + mxb::Log log(MXB_LOG_TARGET_STDOUT); + + TestConnections::skip_maxscale_start(false); + TestConnections test(argc, argv); + + test.maxscales->ssh_node(0, "rm -f /var/lib/maxscale/master.ini", true); + test.maxscales->ssh_node(0, "rm -f /var/lib/maxscale/gtid_maps.db", true); + test.maxscales->ssh_node(0, "rm -rf /var/lib/maxscale/0", true); + + test.start_maxscale(0); + + setup_galera(test); + test.galera->start_replication(); // Causes restart. + + Mariadb_nodes& gc = *test.galera; + gc.connect(); + + reset_galera(test); + + string gtid = get_gtid_current_pos(test, gc.nodes[0]); + + cout << "GTID: " << gtid << endl; + + const char* zValue; + + // Env-vars for debugging. + zValue = getenv("MXS2047_BLR_HOST"); + const char* zMaxscale_host = (zValue ? zValue : test.maxscales->IP[0]); + cout << "MaxScale host: " << zMaxscale_host << endl; + + zValue = getenv("MXS2047_BLR_PORT"); + int maxscale_port = (zValue ? atoi(zValue) : test.maxscales->binlog_port[0]); + cout << "MaxScale port: " << maxscale_port << endl; + + if (setup_blr(test, gtid, zMaxscale_host, maxscale_port)) + { + int slave_index = test.repl->N - 1; // We use the last slave. + + Mariadb_nodes& ms = *test.repl; + ms.connect(slave_index); + + MYSQL* pSlave = ms.nodes[slave_index]; + mxb_assert(pSlave); + + if (setup_slave(test, gtid, pSlave, zMaxscale_host, maxscale_port)) + { + if (setup_schema(test, gc.nodes[0])) + { + sleep(REPLICATION_SLEEP); + + if (insert_select(test, pSlave)) + { + restart_slave(test, pSlave); + } + } + } + } + + restore_galera(test); + + return test.global_result; +}