diff --git a/maxscale-system-test/CMakeLists.txt b/maxscale-system-test/CMakeLists.txt index d37424fa4..0e21a6a67 100644 --- a/maxscale-system-test/CMakeLists.txt +++ b/maxscale-system-test/CMakeLists.txt @@ -942,6 +942,9 @@ add_test_executable(mxs2878_monitor_ssl.cpp mxs2878_monitor_ssl mxs2878_monitor_ # MXS-2939: Test that session commands trigger a reconnection add_test_executable(mxs2939_sescmd_reconnect.cpp mxs2939_sescmd_reconnect mxs2939_sescmd_reconnect LABELS REPL_BACKEND readwritesplit) +# MXS-2919: Slaves with broken replication should not be used +add_test_executable(mxs2919_broken_slaves.cpp mxs2919_broken_slaves mxs2919_broken_slaves LABELS REPL_BACKEND readwritesplit) + ############################################ # END: Normal tests # ############################################ diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.mxs2919_broken_slaves b/maxscale-system-test/cnf/maxscale.cnf.template.mxs2919_broken_slaves new file mode 100644 index 000000000..a155b3c26 --- /dev/null +++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs2919_broken_slaves @@ -0,0 +1,26 @@ +[maxscale] +threads=###threads### + +###server### + +[MySQL-Monitor] +type=monitor +module=mysqlmon +servers=###server_line### +user=maxskysql +password=skysql +monitor_interval=1s + +[RW-Split-Router] +type=service +router=readwritesplit +servers=###server_line### +user=maxskysql +password=skysql +max_slave_replication_lag=1 + +[RW-Split-Listener] +type=listener +service=RW-Split-Router +protocol=MySQLClient +port=4006 diff --git a/maxscale-system-test/maxtest/include/maxtest/mariadb_nodes.h b/maxscale-system-test/maxtest/include/maxtest/mariadb_nodes.h index 4da3db711..1f71831a2 100644 --- a/maxscale-system-test/maxtest/include/maxtest/mariadb_nodes.h +++ b/maxscale-system-test/maxtest/include/maxtest/mariadb_nodes.h @@ -242,6 +242,16 @@ public: // Create the default users used by all tests void create_users(int node); + /** + * Blocks `src` from communicating with `dest` + */ + void block_node_from_node(int src, int dest); + + /** + * Unblocks the block added by block_node_from_node + */ + void unblock_node_from_node(int src, int dest); + /** * @param node Index of node to block. * @return The command used for blocking a node. diff --git a/maxscale-system-test/maxtest/src/mariadb_nodes.cc b/maxscale-system-test/maxtest/src/mariadb_nodes.cc index 6203b0446..d8c9c1947 100644 --- a/maxscale-system-test/maxtest/src/mariadb_nodes.cc +++ b/maxscale-system-test/maxtest/src/mariadb_nodes.cc @@ -510,11 +510,31 @@ int Mariadb_nodes::clean_iptables(int node) return ssh_node_f(node, true, "while [ \"$(iptables -n -L INPUT 1|grep '%d')\" != \"\" ]; do iptables -D INPUT 1; done;" - "while [ \"$(ip6tables -n -L INPUT 1|grep '%d')\" != \"\" ]; do ip6tables -D INPUT 1; done;", + "while [ \"$(ip6tables -n -L INPUT 1|grep '%d')\" != \"\" ]; do ip6tables -D INPUT 1; done;" + "while [ \"$(iptables -n -L OUTPUT 1|grep '3306')\" != \"\" ]; do iptables -D OUTPUT 1; done;", port[node], port[node]); } + +void Mariadb_nodes::block_node_from_node(int src, int dest) +{ + std::ostringstream ss; + + ss << "iptables -I OUTPUT 1 -p tcp -d " << IP[dest] << " --dport 3306 -j DROP;"; + + ssh_node_f(src, true, "%s", ss.str().c_str()); +} + +void Mariadb_nodes::unblock_node_from_node(int src, int dest) +{ + std::ostringstream ss; + + ss << "iptables -D OUTPUT -p tcp -d " << IP[dest] << " --dport 3306 -j DROP;"; + + ssh_node_f(src, true, "%s", ss.str().c_str()); +} + std::string Mariadb_nodes::block_command(int node) const { const char FORMAT[] = diff --git a/maxscale-system-test/mxs2919_broken_slaves.cpp b/maxscale-system-test/mxs2919_broken_slaves.cpp new file mode 100644 index 000000000..e671b5647 --- /dev/null +++ b/maxscale-system-test/mxs2919_broken_slaves.cpp @@ -0,0 +1,43 @@ +/** + * MXS-2919: Slaves that aren't replicating should not be used for reads when max_slave_replication_lag is + * used. + */ + +#include "testconnections.h" + +int main(int argc, char** argv) +{ + TestConnections test(argc, argv); + + auto conn = test.maxscales->rwsplit(); + test.expect(conn.connect(), "Connection should work"); + + std::string master_id = conn.field("SELECT @@server_id, @@last_insert_id", 0); + + test.repl->connect(); + + for (int i = 1; i < test.repl->N; i++) + { + test.repl->block_node_from_node(i, 0); + test.try_query(test.repl->nodes[i], "STOP SLAVE;START SLAVE"); + } + + test.repl->disconnect(); + test.maxscales->wait_for_monitor(); + + for (int i = 0; test.ok() && i < 50; i++) + { + auto current_id = conn.field("SELECT @@server_id"); + + test.expect(current_id == master_id, + "The query was not routed to the master: %s%s", + current_id.c_str(), conn.error()); + } + + for (int i = 1; i < test.repl->N; i++) + { + test.repl->unblock_node_from_node(i, 0); + } + + return test.global_result; +}