diff --git a/maxscale-system-test/.gitignore b/maxscale-system-test/.gitignore index c67b0e42f..318b47f99 100644 --- a/maxscale-system-test/.gitignore +++ b/maxscale-system-test/.gitignore @@ -112,6 +112,7 @@ mysqlmon_failover_manual2_3 mysqlmon_failover_manual2_2 mysqlmon_failover_rejoin_old_slave mysqlmon_failover_rolling_master +mysqlmon_failover_rolling_restart_slaves mxs1045 mxs1071_maxrows mxs1110_16mb diff --git a/maxscale-system-test/CMakeLists.txt b/maxscale-system-test/CMakeLists.txt index bc82b8ec3..365a01f91 100644 --- a/maxscale-system-test/CMakeLists.txt +++ b/maxscale-system-test/CMakeLists.txt @@ -278,6 +278,9 @@ add_test_executable(mysqlmon_failover_rolling_master.cpp mysqlmon_failover_rolli # MySQL Monitor rejoin old slave add_test_executable(mysqlmon_failover_rejoin_old_slave.cpp mysqlmon_failover_rejoin_old_slave mysqlmon_failover_rejoin_old_slave LABELS mysqlmon REPL_BACKEND) +# MySQL Monitor rolling restart slaves +add_test_executable(mysqlmon_failover_rolling_restart_slaves.cpp mysqlmon_failover_rolling_restart_slaves mysqlmon_failover_rolling_restart_slaves LABELS mysqlmon REPL_BACKEND) + # Test monitor state change events when manually clearing server bits add_test_executable(false_monitor_state_change.cpp false_monitor_state_change replication LABELS mysqlmon REPL_BACKEND) diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.mysqlmon_failover_rolling_restart_slaves b/maxscale-system-test/cnf/maxscale.cnf.template.mysqlmon_failover_rolling_restart_slaves new file mode 100644 index 000000000..c9da3d2ad --- /dev/null +++ b/maxscale-system-test/cnf/maxscale.cnf.template.mysqlmon_failover_rolling_restart_slaves @@ -0,0 +1,92 @@ +[maxscale] +threads=###threads### + +[MySQL-Monitor] +type=monitor +module=mysqlmon +servers= server1, server2, server3, server4 +user=maxskysql +passwd= skysql +monitor_interval=1000 +allow_cluster_recovery=true +detect_standalone_master=true +auto_failover=true +auto_rejoin=true +replication_user=repl +replication_password=repl +backend_connect_timeout=1 + +[RW-Split-Router] +type=service +router= readwritesplit +servers=server1, server2, server3, server4 +user=maxskysql +passwd=skysql + +[Read-Connection-Router-Slave] +type=service +router=readconnroute +router_options= slave +servers=server1, server2, server3, server4 +user=maxskysql +passwd=skysql + +[Read-Connection-Router-Master] +type=service +router=readconnroute +router_options=master +servers=server1, server2, server3, server4 +user=maxskysql +passwd=skysql + +[RW-Split-Listener] +type=listener +service=RW-Split-Router +protocol=MySQLClient +port=4006 + +[Read-Connection-Listener-Slave] +type=listener +service=Read-Connection-Router-Slave +protocol=MySQLClient +port=4009 + +[Read-Connection-Listener-Master] +type=listener +service=Read-Connection-Router-Master +protocol=MySQLClient +port=4008 + +[CLI] +type=service +router=cli + +[CLI Listener] +type=listener +service=CLI +protocol=maxscaled +socket=default + +[server1] +type=server +address=###node_server_IP_1### +port=###node_server_port_1### +protocol=MySQLBackend + +[server2] +type=server +address=###node_server_IP_2### +port=###node_server_port_2### +protocol=MySQLBackend + +[server3] +type=server +address=###node_server_IP_3### +port=###node_server_port_3### +protocol=MySQLBackend + +[server4] +type=server +address=###node_server_IP_4### +port=###node_server_port_4### +protocol=MySQLBackend diff --git a/maxscale-system-test/mysqlmon_failover_rolling_restart_slaves.cpp b/maxscale-system-test/mysqlmon_failover_rolling_restart_slaves.cpp new file mode 100644 index 000000000..8cfe437d1 --- /dev/null +++ b/maxscale-system-test/mysqlmon_failover_rolling_restart_slaves.cpp @@ -0,0 +1,209 @@ +/* + * 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: 2020-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 "testconnections.h" + +using std::cerr; +using std::cout; +using std::endl; +using std::flush; +using std::string; +using std::stringstream; + +namespace +{ + +void sleep(int s) +{ + cout << "Sleeping " << s << " times 1 second" << flush; + do + { + ::sleep(1); + cout << "." << flush; + --s; + } + while (s > 0); + + cout << endl; +} + +} + +namespace +{ + +void create_table(TestConnections& test) +{ + MYSQL* pConn = test.maxscales->conn_rwsplit[0]; + + test.try_query(pConn, "DROP TABLE IF EXISTS test.t1"); + test.try_query(pConn, "CREATE TABLE test.t1(id INT)"); +} + +static int i_start = 0; +static int n_rows = 20; +static int i_end = 0; + +void insert_data(TestConnections& test) +{ + MYSQL* pConn = test.maxscales->conn_rwsplit[0]; + + test.try_query(pConn, "BEGIN"); + + i_end = i_start + n_rows; + + for (int i = i_start; i < i_end; ++i) + { + stringstream ss; + ss << "INSERT INTO test.t1 VALUES (" << i << ")"; + test.try_query(pConn, ss.str().c_str()); + } + + test.try_query(pConn, "COMMIT"); + + i_start = i_end; +} + +void expect(TestConnections& test, const char* zServer, const StringSet& expected) +{ + StringSet found = test.get_server_status(zServer); + + std::ostream_iterator oi(cout, ", "); + + cout << zServer + << ", expected states: "; + std::copy(expected.begin(), expected.end(), oi); + cout << endl; + + cout << zServer + << ", found states : "; + std::copy(found.begin(), found.end(), oi); + cout << endl; + + if (found != expected) + { + cout << "ERROR, found states are not the same as the expected ones." << endl; + ++test.global_result; + } + + cout << endl; +} + +void expect(TestConnections& test, const char* zServer, const char* zState) +{ + StringSet s; + s.insert(zState); + + expect(test, zServer, s); +} + +void expect(TestConnections& test, const string& server, const char* zState) +{ + expect(test, server.c_str(), zState); +} + +void expect(TestConnections& test, const char* zServer, const char* zState1, const char* zState2) +{ + StringSet s; + s.insert(zState1); + s.insert(zState2); + + expect(test, zServer, s); +} + +void expect(TestConnections& test, const string& server, const char* zState1, const char* zState2) +{ + expect(test, server.c_str(), zState1, zState2); +} + +string server_name(int i) +{ + stringstream ss; + ss << "server" << (i + 1); + return ss.str(); +} + +void check_server_status(TestConnections& test, int N, int down = -1) +{ + expect(test, "server1", "Master", "Running"); + + for (int i = 1; i < N; ++i) + { + string slave = server_name(i); + if (i == down) + { + expect(test, slave, "Down"); + } + else + { + expect(test, slave, "Slave", "Running"); + } + } +} + +void run(TestConnections& test) +{ + sleep(5); + + int N = test.repl->N; + cout << "Nodes: " << N << endl; + + check_server_status(test, N); + + cout << "\nConnecting to MaxScale." << endl; + test.maxscales->connect_maxscale(0); + + cout << "\nCreating table." << endl; + create_table(test); + + cout << "\nInserting data." << endl; + insert_data(test); + + cout << "\nSyncing slaves." << endl; + test.repl->sync_slaves(); + + for (int i = 1; i < N; ++i) + { + string slave = server_name(i); + + cout << "\nStopping slave " << slave << endl; + test.repl->stop_node(i); + + sleep(5); + + check_server_status(test, N, i); + + cout << "\nStarting slave " << slave << endl; + test.repl->start_node(i, ""); + + sleep(5); + + check_server_status(test, N); + } +} + +} + +int main(int argc, char* argv[]) +{ + TestConnections test(argc, argv); + + run(test); + + return test.global_result; +} +