diff --git a/Documentation/Changelog.md b/Documentation/Changelog.md index 4bb672149..a225cd41a 100644 --- a/Documentation/Changelog.md +++ b/Documentation/Changelog.md @@ -49,6 +49,7 @@ For more details, please refer to: * MaxScale now supports IPv6 For more details, please refer to: +* [MariaDB MaxScale 2.1.12 Release Notes](Release-Notes/MaxScale-2.1.12-Release-Notes.md) * [MariaDB MaxScale 2.1.11 Release Notes](Release-Notes/MaxScale-2.1.11-Release-Notes.md) * [MariaDB MaxScale 2.1.10 Release Notes](Release-Notes/MaxScale-2.1.10-Release-Notes.md) * [MariaDB MaxScale 2.1.9 Release Notes](Release-Notes/MaxScale-2.1.9-Release-Notes.md) diff --git a/Documentation/Monitors/MySQL-Monitor.md b/Documentation/Monitors/MySQL-Monitor.md index 47a6ddd82..74e6adbfa 100644 --- a/Documentation/Monitors/MySQL-Monitor.md +++ b/Documentation/Monitors/MySQL-Monitor.md @@ -120,6 +120,23 @@ This functionality is similar to the [Multi-Master Monitor](MM-Monitor.md) functionality. The only difference is that the MySQL monitor will also detect traditional Master-Slave topologies. +### `ignore_external_masters` + +Ignore any servers that are not monitored by this monitor but are a part of the +replication topology. This option was added in MaxScale 2.1.12 and is disabled +by default. + +MaxScale detects if a master server replicates from an external server. When +this is detected, the server is assigned the `Slave` and `Slave of External +Server` labels and will be treated as a slave server. Most of the time this +topology is used when MaxScale is used for read scale-out without master +servers, a Galera cluster with read replicas being a prime example of this +setup. Sometimes this is not the desired behavior and the external master server +should be ignored. Most of the time this is due to multi-source replication. + +When this option is enabled, all servers that have the `Master, Slave, Slave of +External Server, Running` labels will instead get the `Master, Running` labels. + ### `detect_standalone_master` Detect standalone master servers. This feature takes a boolean parameter and is diff --git a/Documentation/Release-Notes/MaxScale-2.1.12-Release-Notes.md b/Documentation/Release-Notes/MaxScale-2.1.12-Release-Notes.md index 850f3cdc8..3c91b9b71 100644 --- a/Documentation/Release-Notes/MaxScale-2.1.12-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-2.1.12-Release-Notes.md @@ -36,6 +36,13 @@ where only parameters are used with the binlogrouter. [Here is a list of bugs fixed in MaxScale 2.1.12.](https://jira.mariadb.org/issues/?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%202.1.12) +* [MXS-1555](https://jira.mariadb.org/browse/MXS-1555) Protocol command tracking doesn't work with readwritesplit +* [MXS-1553](https://jira.mariadb.org/browse/MXS-1553) GaleraMon ignores server's SSL configuration +* [MXS-1540](https://jira.mariadb.org/browse/MXS-1540) Race conditions in Galeramon server parameter handling +* [MXS-1536](https://jira.mariadb.org/browse/MXS-1536) Fatal: MaxScale 2.1.10 received fatal signal 11. Attempting backtrace. Commit ID: 96c3f0dda3b5a9640c4995f46ac8efec77686269 System name: Linux Release string: NAME=CentOS Linux +* [MXS-1529](https://jira.mariadb.org/browse/MXS-1529) OOM: mxs_realloc can be repeated this way +* [MXS-1509](https://jira.mariadb.org/browse/MXS-1509) Show correct server state for multisource replication + ## Packaging RPM and Debian packages are provided for the Linux distributions supported by diff --git a/Documentation/Routers/Binlogrouter.md b/Documentation/Routers/Binlogrouter.md index b54acb9fa..cc6ac9d78 100644 --- a/Documentation/Routers/Binlogrouter.md +++ b/Documentation/Routers/Binlogrouter.md @@ -362,7 +362,6 @@ follows. [Replication] type=service router=binlogrouter -servers=masterdb user=maxscale passwd=maxpwd server_id=3 diff --git a/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md b/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md index f89c716b1..5aaef9552 100644 --- a/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md +++ b/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md @@ -372,6 +372,11 @@ Examples with SSL options: Binlog Router Plugin is compatible with MariaDB 5.5, 10.0, 10.1 and 10.2 as well as MySQL 5.6 and 5.7. +Note: When using MariaDB 10.2 or MySQL 5.7 the `send_slave_heartbeat` option +must be set to On as the slave servers request the hearbeat to MaxScale. +As an alternative use `CHANGE MASTER TO MASTER_HEARTBEAT_PERIOD=0` in +the slave server in order to disable the heartbeat request. + ## Enabling MariaDB 10 compatibility MariaDB 10 has different slave registration phase so an extra option is required: @@ -396,6 +401,11 @@ with MySQL 5.7 slaves the `send_slave_heartbeat` option must be set to on. Binlog Router currently does not work for MySQL 5.5 due to missing *@@global.binlog_checksum* variable. +## MariaDB Limitations +Starting from version 10.2 there are new replication events related +to binlog event compression: these new events are not supported yet. +Be sure that `log_bin_compress` is not set in any MariaDB 10.2 server. + # MariaDB MaxScale Replication Diagnostics The binlog router module of MariaDB MaxScale produces diagnostic output that can diff --git a/Documentation/Upgrading/Upgrading-To-MaxScale-2.1.md b/Documentation/Upgrading/Upgrading-To-MaxScale-2.1.md index 91ae243db..5c1955a8b 100644 --- a/Documentation/Upgrading/Upgrading-To-MaxScale-2.1.md +++ b/Documentation/Upgrading/Upgrading-To-MaxScale-2.1.md @@ -7,6 +7,7 @@ For more information about MariaDB MaxScale 2.1, please refer to the [ChangeLog](../Changelog.md). For a complete list of changes in MaxScale 2.1, refer to the +* [MaxScale 2.1.12 Release Notes](../Release-Notes/MaxScale-2.1.12-Release-Notes.md) * [MaxScale 2.1.11 Release Notes](../Release-Notes/MaxScale-2.1.11-Release-Notes.md) * [MaxScale 2.1.10 Release Notes](../Release-Notes/MaxScale-2.1.10-Release-Notes.md) * [MaxScale 2.1.9 Release Notes](../Release-Notes/MaxScale-2.1.9-Release-Notes.md). diff --git a/VERSION21.cmake b/VERSION21.cmake index 42ce2974e..e1964f9f7 100644 --- a/VERSION21.cmake +++ b/VERSION21.cmake @@ -5,7 +5,7 @@ set(MAXSCALE_VERSION_MAJOR "2" CACHE STRING "Major version") set(MAXSCALE_VERSION_MINOR "1" CACHE STRING "Minor version") -set(MAXSCALE_VERSION_PATCH "11" CACHE STRING "Patch version") +set(MAXSCALE_VERSION_PATCH "12" CACHE STRING "Patch version") # This should only be incremented if a package is rebuilt set(MAXSCALE_BUILD_NUMBER 1 CACHE STRING "Release number") diff --git a/include/maxscale/server.h b/include/maxscale/server.h index 19f86c126..e1c82b42c 100644 --- a/include/maxscale/server.h +++ b/include/maxscale/server.h @@ -138,6 +138,10 @@ typedef struct server int last_event; /**< The last event that occurred on this server */ bool active_event; /**< Event observed when MaxScale was active */ int64_t triggered_at; /**< Time when the last event was triggered */ + struct + { + bool ssl_not_enabled; /**< SSL not used for an SSL enabled server */ + } log_warning; /**< Whether a specific warning was logged */ #if defined(SS_DEBUG) skygw_chk_t server_chk_tail; #endif @@ -234,6 +238,10 @@ enum (((server)->status & (SERVER_RUNNING|SERVER_MASTER|SERVER_SLAVE|SERVER_MAINT)) == \ (SERVER_RUNNING|SERVER_MASTER|SERVER_SLAVE)) +#define SERVER_IS_SLAVE_OF_EXTERNAL_MASTER(s) (((s)->status & \ + (SERVER_RUNNING|SERVER_SLAVE_OF_EXTERNAL_MASTER)) == \ + (SERVER_RUNNING|SERVER_SLAVE_OF_EXTERNAL_MASTER)) + /** * @brief Allocate a new server * diff --git a/maxscale-system-test/bulk_insert.cpp b/maxscale-system-test/bulk_insert.cpp index 2b00333db..fe83132da 100644 --- a/maxscale-system-test/bulk_insert.cpp +++ b/maxscale-system-test/bulk_insert.cpp @@ -205,7 +205,6 @@ int main(int argc, char** argv) test.maxscales->connect_maxscale(0); test.repl->connect(); - test.tprintf("Testing column-wise binding with a direct connection"); test.add_result(bind_by_column(test.repl->nodes[0]), "Bulk inserts with a direct connection should work"); test.tprintf("Testing column-wise binding with readwritesplit"); diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.mxs1509 b/maxscale-system-test/cnf/maxscale.cnf.template.mxs1509 new file mode 100644 index 000000000..c839b0dad --- /dev/null +++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs1509 @@ -0,0 +1,45 @@ +[maxscale] +threads=###threads### + +[MySQL Monitor] +type=monitor +module=mysqlmon +servers=server1,server2 +user=maxskysql +passwd=skysql +monitor_interval=1000 + +[RW Split Router] +type=service +router=readwritesplit +servers=server1,server2 +user=maxskysql +passwd=skysql + +[RW Split Listener] +type=listener +service=RW Split Router +protocol=MySQLClient +port=4006 + +[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 diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog index c85f33b95..81985d120 100755 --- a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog +++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog @@ -7,9 +7,9 @@ type=service router=binlogrouter user=skysql passwd=skysql -version_string=5.6.15-log +#version_string=5.6.15-log #router_options=server-id=3,user=repl,password=repl,master-id=1 -router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,mariadb10-compatibility=1 +router_options=server-id=9993,send_slave_heartbeat=On,user=repl,password=repl,longburst=500,heartbeat=10,binlogdir=/var/lib/maxscale/Binlog_Service,mariadb10-compatibility=1 [Binlog Listener] diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog.in b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog.in index c85f33b95..81985d120 100755 --- a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog.in +++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog.in @@ -7,9 +7,9 @@ type=service router=binlogrouter user=skysql passwd=skysql -version_string=5.6.15-log +#version_string=5.6.15-log #router_options=server-id=3,user=repl,password=repl,master-id=1 -router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,mariadb10-compatibility=1 +router_options=server-id=9993,send_slave_heartbeat=On,user=repl,password=repl,longburst=500,heartbeat=10,binlogdir=/var/lib/maxscale/Binlog_Service,mariadb10-compatibility=1 [Binlog Listener] diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog1 b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog1 index 473832a71..ee369a8f3 100755 --- a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog1 +++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog1 @@ -7,9 +7,9 @@ type=service router=binlogrouter user=skysql passwd=skysql -version_string=5.6.15-log +#version_string=5.6.15-log #router_options=server-id=3,user=repl,password=repl,master-id=1 -router_options=server-id=3,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,mariadb10-compatibility=1 +router_options=server-id=9993,send_slave_heartbeat=On,longburst=500,heartbeat=10,binlogdir=/var/lib/maxscale/Binlog_Service,mariadb10-compatibility=1 diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog2 b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog2 index 2d41e8396..0d03663a3 100755 --- a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog2 +++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog2 @@ -8,9 +8,9 @@ type=service router=binlogrouter user=skysql passwd=skysql -version_string=5.6.15-log +#version_string=5.6.15-log #router_options=server-id=3,user=repl,password=repl,master-id=1 -router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,transaction_safety=1,mariadb10-compatibility=1 +router_options=server-id=9993,send_slave_heartbeat=On,user=repl,password=repl,longburst=500,heartbeat=10,binlogdir=/var/lib/maxscale/Binlog_Service,transaction_safety=1,mariadb10-compatibility=1 diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync index 4fb918415..76a8f6910 100755 --- a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync +++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync @@ -8,9 +8,9 @@ router=binlogrouter #servers=master user=skysql passwd=skysql -version_string=5.6.15-log +#version_string=5.6.15-log #router_options=server-id=3,user=repl,password=repl,master-id=1 -router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,semisync=1,transaction_safety=1,mariadb10-compatibility=1 +router_options=server-id=9993,send_slave_heartbeat=On,user=repl,password=repl,longburst=500,heartbeat=10,binlogdir=/var/lib/maxscale/Binlog_Service,semisync=1,transaction_safety=1,mariadb10-compatibility=1 diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss0 b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss0 index 13a459c20..4f55f4ec9 100755 --- a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss0 +++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss0 @@ -8,9 +8,9 @@ router=binlogrouter #servers=master user=skysql passwd=skysql -version_string=5.6.15-log +#version_string=5.6.15-log #router_options=server-id=3,user=repl,password=repl,master-id=1 -router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,semisync=0,transaction_safety=0,mariadb10-compatibility=1 +router_options=server-id=9993,send_slave_heartbeat=On,user=repl,password=repl,longburst=500,heartbeat=10,binlogdir=/var/lib/maxscale/Binlog_Service,semisync=0,transaction_safety=0,mariadb10-compatibility=1 diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss1 b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss1 index 93098385d..8c0267ddc 100755 --- a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss1 +++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss1 @@ -8,9 +8,9 @@ router=binlogrouter #servers=master user=skysql passwd=skysql -version_string=5.6.15-log +#version_string=5.6.15-log #router_options=server-id=3,user=repl,password=repl,master-id=1 -router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,semisync=1,transaction_safety=0,mariadb10-compatibility=1 +router_options=server-id=9993,send_slave_heartbeat=On,user=repl,password=repl,longburst=500,heartbeat=10,binlogdir=/var/lib/maxscale/Binlog_Service,semisync=1,transaction_safety=0,mariadb10-compatibility=1 diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs1_ss0 b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs1_ss0 index 8e78d9126..20f30f984 100755 --- a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs1_ss0 +++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs1_ss0 @@ -8,9 +8,9 @@ router=binlogrouter #servers=master user=skysql passwd=skysql -version_string=5.6.15-log +#version_string=5.6.15-log #router_options=server-id=3,user=repl,password=repl,master-id=1 -router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,semisync=0,transaction_safety=1,mariadb10-compatibility=1 +router_options=server-id=9993,send_slave_heartbeat=On,user=repl,password=repl,longburst=500,heartbeat=10,binlogdir=/var/lib/maxscale/Binlog_Service,semisync=0,transaction_safety=1,mariadb10-compatibility=1 diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_tx_safe b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_tx_safe index 33957920a..6a1078fc9 100755 --- a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_tx_safe +++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_tx_safe @@ -8,9 +8,9 @@ router=binlogrouter #servers=master user=skysql passwd=skysql -version_string=5.6.15-log +#version_string=5.6.15-log #router_options=server-id=3,user=repl,password=repl,master-id=1 -router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,transaction_safety=1,mariadb10-compatibility=1 +router_options=server-id=9993,send_slave_heartbeat=On,user=repl,password=repl,longburst=500,heartbeat=10,binlogdir=/var/lib/maxscale/Binlog_Service,transaction_safety=1,mariadb10-compatibility=1 diff --git a/maxscale-system-test/crash_out_of_files.cpp b/maxscale-system-test/crash_out_of_files.cpp index 88a175946..ea214d165 100644 --- a/maxscale-system-test/crash_out_of_files.cpp +++ b/maxscale-system-test/crash_out_of_files.cpp @@ -44,7 +44,7 @@ int main(int argc, char *argv[]) Test->repl->close_connections(); Test->stop_timeout(); - Test->check_log_err(0, (char *) "refresh rate limit exceeded", false); + Test->check_log_err(0, (char *) "Refresh rate limit exceeded", false); Test->check_maxscale_alive(0); int rval = Test->global_result; delete Test; diff --git a/maxscale-system-test/mariadb_nodes.cpp b/maxscale-system-test/mariadb_nodes.cpp index 44b61040d..79718de32 100644 --- a/maxscale-system-test/mariadb_nodes.cpp +++ b/maxscale-system-test/mariadb_nodes.cpp @@ -293,10 +293,9 @@ int Mariadb_nodes::stop_nodes() { printf("Stopping node %d\n", i); fflush(stdout); - local_result += execute_query(nodes[i], (char *) "stop slave;"); - fflush(stdout); + local_result += execute_query(nodes[i], "stop slave;"); local_result += stop_node(i); - fflush(stdout); + local_result += ssh_node_f(i, true, "rm -f /var/lib/mysql/*master*.info"); } return local_result; } @@ -345,11 +344,15 @@ int Mariadb_nodes::start_replication() // Start all nodes for (int i = 0; i < N; i++) { - local_result += start_node(i, (char *) ""); - sprintf(str, - "mysql -u root %s -e \"STOP SLAVE; RESET SLAVE; RESET SLAVE ALL; RESET MASTER; SET GLOBAL read_only=OFF;\"", - socket_cmd[i]); - ssh_node(i, str, true); + local_result += start_node(i, (char*)""); + ssh_node_f(i, true, + "mysql --force -u root %s -e \"STOP SLAVE; STOP ALL SLAVES; RESET SLAVE; RESET SLAVE ALL; RESET MASTER; SET GLOBAL read_only=OFF;\"", + socket_cmd[i]); + ssh_node_f(i, true, "sudo rm -f /etc/my.cnf.d/kerb.cnf"); + ssh_node_f(i, true, + "for i in `mysql -ss --force -u root %s -e \"SHOW DATABASES\"|grep -iv 'mysql\\|information_schema\\|performance_schema'`; " + "do mysql --force -u root %s -e \"DROP DATABASE $i\";" + "done", socket_cmd[i], socket_cmd[i]); } sprintf(str, "%s/create_user.sh", test_dir); @@ -361,10 +364,9 @@ int Mariadb_nodes::start_replication() ssh_node(0, str, false); // Create a database dump from the master and distribute it to the slaves - sprintf(str, - "mysqldump --all-databases --add-drop-database --flush-privileges --master-data=1 --gtid %s > /tmp/master_backup.sql", - socket_cmd[0]); - ssh_node(0, str, true); + ssh_node_f(0, true, "mysql --force -u root %s -e \"CREATE DATABASE test\"; " + "mysqldump --all-databases --add-drop-database --flush-privileges --master-data=1 --gtid %s > /tmp/master_backup.sql", + socket_cmd[0], socket_cmd[0]); sprintf(str, "%s/master_backup.sql", test_dir); copy_from_node_legacy("/tmp/master_backup.sql", str, 0); @@ -374,16 +376,11 @@ int Mariadb_nodes::start_replication() printf("Starting node %d\n", i); fflush(stdout); copy_to_node_legacy(str, "/tmp/master_backup.sql", i); - sprintf(dtr, - "mysql -u root %s < /tmp/master_backup.sql", - socket_cmd[i]); - ssh_node(i, dtr, true); - char query[512]; - - sprintf(query, "mysql -u root %s -e \"CHANGE MASTER TO MASTER_HOST=\\\"%s\\\", MASTER_PORT=%d, " - "MASTER_USER=\\\"repl\\\", MASTER_PASSWORD=\\\"repl\\\";" - "START SLAVE;\"", socket_cmd[i], IP_private[0], port[0]); - ssh_node(i, query, true); + ssh_node_f(i, true, "mysql --force -u root %s < /tmp/master_backup.sql", + socket_cmd[i]); + ssh_node_f(i, true, "mysql --force -u root %s -e \"CHANGE MASTER TO MASTER_HOST=\\\"%s\\\", MASTER_PORT=%d, " + "MASTER_USER=\\\"repl\\\", MASTER_PASSWORD=\\\"repl\\\";" + "START SLAVE;\"", socket_cmd[i], IP_private[0], port[0]); } return local_result; @@ -405,7 +402,7 @@ int Galera_nodes::start_galera() ssh_node(0, "echo [mysqld] > cluster_address.cnf", false); ssh_node(0, "echo wsrep_cluster_address=gcomm:// >> cluster_address.cnf", false); ssh_node(0, "cp cluster_address.cnf /etc/my.cnf.d/", true); - local_result += start_node(0, (char *) " --wsrep-cluster-address=gcomm://"); + local_result += start_node(0, (char*)" --wsrep-cluster-address=gcomm://"); sprintf(str, "%s/create_user_galera.sh", test_dir); copy_to_node_legacy(str, "~/", 0); @@ -614,6 +611,28 @@ static bool bad_slave_thread_status(MYSQL *conn, const char *field, int node) return rval; } +static bool multi_source_replication(MYSQL *conn, int node) +{ + bool rval = true; + MYSQL_RES *res; + + if (mysql_query(conn, "SHOW ALL SLAVES STATUS") == 0 && + (res = mysql_store_result(conn))) + { + if (mysql_num_rows(res) == 1) + { + rval = false; + } + else + { + printf("Node %d: More than one configured slave\n", node); + fflush(stdout); + } + } + + return rval; +} + int Mariadb_nodes::check_replication() { int master = 0; @@ -643,7 +662,8 @@ int Mariadb_nodes::check_replication() } } else if (bad_slave_thread_status(nodes[i], "Slave_IO_Running", i) || - bad_slave_thread_status(nodes[i], "Slave_SQL_Running", i)) + bad_slave_thread_status(nodes[i], "Slave_SQL_Running", i) || + multi_source_replication(nodes[i], i)) { res = 1; } @@ -1050,7 +1070,7 @@ static void wait_until_pos(MYSQL *mysql, int filenum, int pos) { MYSQL_ROW row = mysql_fetch_row(res); - if (row && row[6] && row[21]) + if (row && row[5] && strchr(row[5], '.') && row[21]) { char *file_suffix = strchr(row[5], '.') + 1; slave_filenum = atoi(file_suffix); diff --git a/maxscale-system-test/mdbci/add_core_cnf.sh b/maxscale-system-test/mdbci/add_core_cnf.sh new file mode 100755 index 000000000..aa5db1828 --- /dev/null +++ b/maxscale-system-test/mdbci/add_core_cnf.sh @@ -0,0 +1,23 @@ +set -x +chmod 777 /tmp/ +echo 2 > /proc/sys/fs/suid_dumpable +sed -i "s/start() {/start() { \n export DAEMON_COREFILE_LIMIT='unlimited'; ulimit -c unlimited; /" /etc/init.d/maxscale +sed -i "s/log_daemon_msg \"Starting MaxScale\"/export DAEMON_COREFILE_LIMIT='unlimited'; ulimit -c unlimited; log_daemon_msg \"Starting MaxScale\" /" /etc/init.d/maxscale +echo /tmp/core-%e-%s-%u-%g-%p-%t > /proc/sys/kernel/core_pattern + +echo "kernel.core_pattern = /tmp/core-%e-sig%s-user%u-group%g-pid%p-time%t" >> /etc/sysctl.d/core.conf +echo "kernel.core_uses_pid = 1" >> /etc/sysctl.d/core.conf +echo "fs.suid_dumpable = 2" >> /etc/sysctl.d/core.conf + +echo "DefaultLimitCORE=infinity" >> /etc/systemd/system.conf + +echo "* hard core unlimited" >> /etc/security/limits.d/core.conf +echo "* soft core unlimited" >> /etc/security/limits.d/core.conf +echo "* soft nofile 65536" >> /etc/security/limits.d/core.conf +echo "* hard nofile 65536" >> /etc/security/limits.d/core.conf + + +echo "fs.file-max = 65536" >> /etc/sysctl.conf + +systemctl daemon-reexec +sysctl -p diff --git a/maxscale-system-test/mdbci/backend/create_repl_user.sql b/maxscale-system-test/mdbci/backend/create_repl_user.sql new file mode 100644 index 000000000..98431e94e --- /dev/null +++ b/maxscale-system-test/mdbci/backend/create_repl_user.sql @@ -0,0 +1,4 @@ +#create user repl@'%' identified by 'repl'; +grant replication slave on *.* to repl@'%' identified by 'repl'; + +FLUSH PRIVILEGES; diff --git a/maxscale-system-test/mdbci/backend/create_skysql_user.sql b/maxscale-system-test/mdbci/backend/create_skysql_user.sql new file mode 100644 index 000000000..668190d4f --- /dev/null +++ b/maxscale-system-test/mdbci/backend/create_skysql_user.sql @@ -0,0 +1,18 @@ +create user skysql@'%' identified by 'skysql'; +create user skysql@'localhost' identified by 'skysql'; +GRANT ALL PRIVILEGES ON *.* TO skysql@'%' WITH GRANT OPTION; +GRANT ALL PRIVILEGES ON *.* TO skysql@'localhost' WITH GRANT OPTION; + +create user maxuser@'%' identified by 'maxpwd'; +create user maxuser@'localhost' identified by 'maxpwd'; +GRANT ALL PRIVILEGES ON *.* TO maxuser@'%' WITH GRANT OPTION; +GRANT ALL PRIVILEGES ON *.* TO maxuser@'localhost' WITH GRANT OPTION; + +create user maxskysql@'%' identified by 'skysql'; +create user maxskysql@'localhost' identified by 'skysql'; +GRANT ALL PRIVILEGES ON *.* TO maxskysql@'%' WITH GRANT OPTION; +GRANT ALL PRIVILEGES ON *.* TO maxskysql@'localhost' WITH GRANT OPTION; + + +FLUSH PRIVILEGES; +CREATE DATABASE IF NOT EXISTS test; diff --git a/maxscale-system-test/mdbci/backend/galera/setup_galera.sh b/maxscale-system-test/mdbci/backend/galera/setup_galera.sh new file mode 100755 index 000000000..c7e2e182d --- /dev/null +++ b/maxscale-system-test/mdbci/backend/galera/setup_galera.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +N=$galera_N + +x=`expr $N - 1` +for i in $(seq 0 $x) +do + num=`printf "%03d" $i` + sshkey_var=galera_"$num"_keyfile + user_var=galera_"$num"_whoami + IP_var=galera_"$num"_network + + sshkey=${!sshkey_var} + user=${!user_var} + IP=${!IP_var} + + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo mysql_install_db; sudo chown -R mysql:mysql /var/lib/mysql" +done diff --git a/maxscale-system-test/mdbci/backend/setup_repl.sh b/maxscale-system-test/mdbci/backend/setup_repl.sh new file mode 100755 index 000000000..ffa2f2251 --- /dev/null +++ b/maxscale-system-test/mdbci/backend/setup_repl.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +set -x + +x=`expr $node_N - 1` +for i in $(seq 0 $x) +do + num=`printf "%03d" $i` + sshkey_var=node_"$num"_keyfile + user_var=node_"$num"_whoami + IP_var=node_"$num"_network + start_cmd_var=node_"$num"_start_db_command + stop_cmd_var=node_"$num"_stop_db_command + + sshkey=${!sshkey_var} + user=${!user_var} + IP=${!IP_var} + start_cmd=${!start_cmd_var} + stop_cmd=${!stop_cmd_var} + + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo $stop_cmd" + sleep 5 + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP 'sudo sed -i "s/bind-address/#bind-address/g" /etc/mysql/my.cnf' + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP 'sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/usr.sbin.mysqld; sudo service apparmor restart' + + mysql_version=`ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP 'mysql --version'` + echo $mysql_version | grep "5\." + if [ $? == 0 ] ; then + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo sed -i \"s/binlog_row_image=full//\" /etc/my.cnf.d/*.cnf" + fi + + echo $mysql_version | grep "5\.7" + if [ $? == 0 ] ; then +# ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo sed -i \"s/## x001/validate-password=OFF/\" /etc/my.cnf.d/*.cnf" + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo mysqld --initialize; sudo chown -R mysql:mysql /var/lib/mysql" + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo $start_cmd" + + mysql_root_password=`ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo cat /var/log/mysqld.log | grep \"temporary password\" | sed -n -e 's/^.*: //p'"` + + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo mysqladmin -uroot -p'$mysql_root_password' password '$mysql_root_password'" + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "echo \"UNINSTALL PLUGIN validate_password\" | sudo mysql -uroot -p'$mysql_root_password' " + + + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo $stop_cmd" +# ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo sed -i \"s/## x001/validate-password=OFF/\" /etc/my.cnf.d/*.cnf" + + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo $start_cmd" + +# mysql_root_password=`ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo cat /var/log/mysqld.log | grep \"temporary password\" | sed -n -e 's/^.*: //p'"` + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "echo \"show plugins\" | sudo mysql -uroot -p'$mysql_root_password' " + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo mysqladmin -uroot -p'$mysql_root_password' password ''" +# ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo $start_cmd" + else + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo mysql_install_db; sudo chown -R mysql:mysql /var/lib/mysql" + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo $start_cmd" + fi + sleep 15 + scp -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no ${script_dir}/create_*_user.sql $user@$IP://home/$user/ + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo mysql < /home/$user/create_repl_user.sql" + ssh -i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user@$IP "sudo mysql < /home/$user/create_skysql_user.sql" +done diff --git a/maxscale-system-test/mdbci/cnf/galera_server1.cnf b/maxscale-system-test/mdbci/cnf/galera_server1.cnf new file mode 100644 index 000000000..03b37157b --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/galera_server1.cnf @@ -0,0 +1,130 @@ +[mysqld] +expire_logs_days=7 +user=mysql +server_id=1 +wsrep_on=ON + +# Row binary log format is required by Galera +binlog_format=ROW + +log-bin + +# InnoDB is currently the only storage engine supported by Galera +default-storage-engine=innodb +innodb_file_per_table + +# To avoid issues with 'bulk mode inserts' using autoincrement fields +innodb_autoinc_lock_mode=2 + +# Required to prevent deadlocks on parallel transaction execution +innodb_locks_unsafe_for_binlog=1 + +# Query Cache is not supported by Galera wsrep replication +query_cache_size=0 +query_cache_type=0 + +# INITIAL SETUP +# In some systems bind-address defaults to 127.0.0.1, and with mysqldump SST +# it will have (most likely) disastrous consequences on donor node +bind-address=###NODE-ADDRESS### + +## +## WSREP options +## + +# INITIAL SETUP +# For the initial setup, wsrep should be disabled +wsrep_provider=none +# After initial setup, parameter should have full path to wsrep provider library +wsrep_provider=###GALERA-LIB-PATH### + +# Provider specific configuration options +wsrep_provider_options = "evs.keepalive_period = PT3S; evs.inactive_check_period = PT10S; evs.suspect_timeout = PT30S; evs.inactive_timeout = PT1M; evs.install_timeout = PT1M" + +# Logical cluster name. Should be the same for all nodes in the same cluster. +wsrep_cluster_name=skycluster + +# INITIAL SETUP +# Group communication system handle: for the first node to be launched, the value should be "gcomm://", indicating creation of a new cluster; +# for the other nodes joining the cluster, the value should be "gcomm://xxx.xxx.xxx.xxx:4567", where xxx.xxx.xxx.xxx should be the ip of a node +# already on the cluster (usually the first one) +# DEPRECATED +# wsrep_cluster_address=gcomm:// + +# Human-readable node name (non-unique). Hostname by default. +#wsrep_node_name=###NODE-NAME### +wsrep_node_name=galera000 + +# INITIAL SETUP +# Base replication [:port] of the node. +# The values supplied will be used as defaults for state transfer receiving, +# listening ports and so on. Default: address of the first network interface. +wsrep_node_address=###NODE-ADDRESS### + +# INITIAL SETUP +# Address for incoming client connections. Autodetect by default. +wsrep_node_incoming_address=###NODE-ADDRESS### + +# Number of threads that will process writesets from other nodes +wsrep_slave_threads=1 + +# Generate fake primary keys for non-PK tables (required for multi-master +# and parallel applying operation) +wsrep_certify_nonPK=1 + +# Debug level logging (1 = enabled) +wsrep_debug=1 + +# Convert locking sessions into transactions +wsrep_convert_LOCK_to_trx=0 + +# Number of retries for deadlocked autocommits +wsrep_retry_autocommit=1 + +# Change auto_increment_increment and auto_increment_offset automatically +wsrep_auto_increment_control=1 + +# Retry autoinc insert, when the insert failed for "duplicate key error" +wsrep_drupal_282555_workaround=0 + +# Enable "strictly synchronous" semantics for read operations +wsrep_causal_reads=1 + +# Command to call when node status or cluster membership changes. +# Will be passed all or some of the following options: +# --status - new status of this node +# --uuid - UUID of the cluster +# --primary - whether the component is primary or not ("yes"/"no") +# --members - comma-separated list of members +# --index - index of this node in the list +wsrep_notify_cmd= + +## +## WSREP State Transfer options +## + +# State Snapshot Transfer method +#wsrep_sst_method=mysqldump +#wsrep_sst_method=xtrabackup +wsrep_sst_method=rsync + +# INITIAL SETUP +# Address which donor should send State Snapshot to. +# Should be the address of the CURRENT node. DON'T SET IT TO DONOR ADDRESS!!! +# (SST method dependent. Defaults to the first IP of the first interface) +wsrep_sst_receive_address=###NODE-ADDRESS### + +# INITIAL SETUP +# SST authentication string. This will be used to send SST to joining nodes. +# Depends on SST method. For mysqldump method it is root: +#wsrep_sst_auth=###REP-USERNAME###:###REP-PASSWORD### +wsrep_sst_auth=repl:repl + +# Desired SST donor name. +#wsrep_sst_donor= + +# Reject client queries when donating SST (false) +#wsrep_sst_donor_rejects_queries=0 + +# Protocol version to use +# wsrep_protocol_version= diff --git a/maxscale-system-test/mdbci/cnf/galera_server2.cnf b/maxscale-system-test/mdbci/cnf/galera_server2.cnf new file mode 100644 index 000000000..79454a443 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/galera_server2.cnf @@ -0,0 +1,130 @@ +[mysqld] +expire_logs_days=7 +user=mysql +server_id=2 +wsrep_on=ON + +# Row binary log format is required by Galera +binlog_format=ROW + +log-bin + +# InnoDB is currently the only storage engine supported by Galera +default-storage-engine=innodb +innodb_file_per_table + +# To avoid issues with 'bulk mode inserts' using autoincrement fields +innodb_autoinc_lock_mode=2 + +# Required to prevent deadlocks on parallel transaction execution +innodb_locks_unsafe_for_binlog=1 + +# Query Cache is not supported by Galera wsrep replication +query_cache_size=0 +query_cache_type=0 + +# INITIAL SETUP +# In some systems bind-address defaults to 127.0.0.1, and with mysqldump SST +# it will have (most likely) disastrous consequences on donor node +bind-address=###NODE-ADDRESS### + +## +## WSREP options +## + +# INITIAL SETUP +# For the initial setup, wsrep should be disabled +wsrep_provider=none +# After initial setup, parameter should have full path to wsrep provider library +wsrep_provider=###GALERA-LIB-PATH### + +# Provider specific configuration options +wsrep_provider_options = "evs.keepalive_period = PT3S; evs.inactive_check_period = PT10S; evs.suspect_timeout = PT30S; evs.inactive_timeout = PT1M; evs.install_timeout = PT1M" + +# Logical cluster name. Should be the same for all nodes in the same cluster. +wsrep_cluster_name=skycluster + +# INITIAL SETUP +# Group communication system handle: for the first node to be launched, the value should be "gcomm://", indicating creation of a new cluster; +# for the other nodes joining the cluster, the value should be "gcomm://xxx.xxx.xxx.xxx:4567", where xxx.xxx.xxx.xxx should be the ip of a node +# already on the cluster (usually the first one) +# DEPRECATED +# wsrep_cluster_address=gcomm:// + +# Human-readable node name (non-unique). Hostname by default. +#wsrep_node_name=###NODE-NAME### +wsrep_node_name=galera001 + +# INITIAL SETUP +# Base replication [:port] of the node. +# The values supplied will be used as defaults for state transfer receiving, +# listening ports and so on. Default: address of the first network interface. +wsrep_node_address=###NODE-ADDRESS### + +# INITIAL SETUP +# Address for incoming client connections. Autodetect by default. +wsrep_node_incoming_address=###NODE-ADDRESS### + +# Number of threads that will process writesets from other nodes +wsrep_slave_threads=1 + +# Generate fake primary keys for non-PK tables (required for multi-master +# and parallel applying operation) +wsrep_certify_nonPK=1 + +# Debug level logging (1 = enabled) +wsrep_debug=1 + +# Convert locking sessions into transactions +wsrep_convert_LOCK_to_trx=0 + +# Number of retries for deadlocked autocommits +wsrep_retry_autocommit=1 + +# Change auto_increment_increment and auto_increment_offset automatically +wsrep_auto_increment_control=1 + +# Retry autoinc insert, when the insert failed for "duplicate key error" +wsrep_drupal_282555_workaround=0 + +# Enable "strictly synchronous" semantics for read operations +wsrep_causal_reads=1 + +# Command to call when node status or cluster membership changes. +# Will be passed all or some of the following options: +# --status - new status of this node +# --uuid - UUID of the cluster +# --primary - whether the component is primary or not ("yes"/"no") +# --members - comma-separated list of members +# --index - index of this node in the list +wsrep_notify_cmd= + +## +## WSREP State Transfer options +## + +# State Snapshot Transfer method +#wsrep_sst_method=mysqldump +#wsrep_sst_method=xtrabackup +wsrep_sst_method=rsync + +# INITIAL SETUP +# Address which donor should send State Snapshot to. +# Should be the address of the CURRENT node. DON'T SET IT TO DONOR ADDRESS!!! +# (SST method dependent. Defaults to the first IP of the first interface) +wsrep_sst_receive_address=###NODE-ADDRESS### + +# INITIAL SETUP +# SST authentication string. This will be used to send SST to joining nodes. +# Depends on SST method. For mysqldump method it is root: +#wsrep_sst_auth=###REP-USERNAME###:###REP-PASSWORD### +wsrep_sst_auth=repl:repl + +# Desired SST donor name. +#wsrep_sst_donor= + +# Reject client queries when donating SST (false) +#wsrep_sst_donor_rejects_queries=0 + +# Protocol version to use +# wsrep_protocol_version= diff --git a/maxscale-system-test/mdbci/cnf/galera_server3.cnf b/maxscale-system-test/mdbci/cnf/galera_server3.cnf new file mode 100644 index 000000000..9f77a5687 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/galera_server3.cnf @@ -0,0 +1,130 @@ +[mysqld] +expire_logs_days=7 +user=mysql +server_id=3 +wsrep_on=ON + +# Row binary log format is required by Galera +binlog_format=ROW + +log-bin + +# InnoDB is currently the only storage engine supported by Galera +default-storage-engine=innodb +innodb_file_per_table + +# To avoid issues with 'bulk mode inserts' using autoincrement fields +innodb_autoinc_lock_mode=2 + +# Required to prevent deadlocks on parallel transaction execution +innodb_locks_unsafe_for_binlog=1 + +# Query Cache is not supported by Galera wsrep replication +query_cache_size=0 +query_cache_type=0 + +# INITIAL SETUP +# In some systems bind-address defaults to 127.0.0.1, and with mysqldump SST +# it will have (most likely) disastrous consequences on donor node +bind-address=###NODE-ADDRESS### + +## +## WSREP options +## + +# INITIAL SETUP +# For the initial setup, wsrep should be disabled +wsrep_provider=none +# After initial setup, parameter should have full path to wsrep provider library +wsrep_provider=###GALERA-LIB-PATH### + +# Provider specific configuration options +wsrep_provider_options = "evs.keepalive_period = PT3S; evs.inactive_check_period = PT10S; evs.suspect_timeout = PT30S; evs.inactive_timeout = PT1M; evs.install_timeout = PT1M" + +# Logical cluster name. Should be the same for all nodes in the same cluster. +wsrep_cluster_name=skycluster + +# INITIAL SETUP +# Group communication system handle: for the first node to be launched, the value should be "gcomm://", indicating creation of a new cluster; +# for the other nodes joining the cluster, the value should be "gcomm://xxx.xxx.xxx.xxx:4567", where xxx.xxx.xxx.xxx should be the ip of a node +# already on the cluster (usually the first one) +# DEPRECATED +# wsrep_cluster_address=gcomm:// + +# Human-readable node name (non-unique). Hostname by default. +#wsrep_node_name=###NODE-NAME### +wsrep_node_name=galera002 + +# INITIAL SETUP +# Base replication [:port] of the node. +# The values supplied will be used as defaults for state transfer receiving, +# listening ports and so on. Default: address of the first network interface. +wsrep_node_address=###NODE-ADDRESS### + +# INITIAL SETUP +# Address for incoming client connections. Autodetect by default. +wsrep_node_incoming_address=###NODE-ADDRESS### + +# Number of threads that will process writesets from other nodes +wsrep_slave_threads=1 + +# Generate fake primary keys for non-PK tables (required for multi-master +# and parallel applying operation) +wsrep_certify_nonPK=1 + +# Debug level logging (1 = enabled) +wsrep_debug=1 + +# Convert locking sessions into transactions +wsrep_convert_LOCK_to_trx=0 + +# Number of retries for deadlocked autocommits +wsrep_retry_autocommit=1 + +# Change auto_increment_increment and auto_increment_offset automatically +wsrep_auto_increment_control=1 + +# Retry autoinc insert, when the insert failed for "duplicate key error" +wsrep_drupal_282555_workaround=0 + +# Enable "strictly synchronous" semantics for read operations +wsrep_causal_reads=1 + +# Command to call when node status or cluster membership changes. +# Will be passed all or some of the following options: +# --status - new status of this node +# --uuid - UUID of the cluster +# --primary - whether the component is primary or not ("yes"/"no") +# --members - comma-separated list of members +# --index - index of this node in the list +wsrep_notify_cmd= + +## +## WSREP State Transfer options +## + +# State Snapshot Transfer method +#wsrep_sst_method=mysqldump +#wsrep_sst_method=xtrabackup +wsrep_sst_method=rsync + +# INITIAL SETUP +# Address which donor should send State Snapshot to. +# Should be the address of the CURRENT node. DON'T SET IT TO DONOR ADDRESS!!! +# (SST method dependent. Defaults to the first IP of the first interface) +wsrep_sst_receive_address=###NODE-ADDRESS### + +# INITIAL SETUP +# SST authentication string. This will be used to send SST to joining nodes. +# Depends on SST method. For mysqldump method it is root: +#wsrep_sst_auth=###REP-USERNAME###:###REP-PASSWORD### +wsrep_sst_auth=repl:repl + +# Desired SST donor name. +#wsrep_sst_donor= + +# Reject client queries when donating SST (false) +#wsrep_sst_donor_rejects_queries=0 + +# Protocol version to use +# wsrep_protocol_version= diff --git a/maxscale-system-test/mdbci/cnf/galera_server4.cnf b/maxscale-system-test/mdbci/cnf/galera_server4.cnf new file mode 100644 index 000000000..a589a690e --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/galera_server4.cnf @@ -0,0 +1,130 @@ +[mysqld] +expire_logs_days=7 +user=mysql +server_id=4 +wsrep_on=ON + +# Row binary log format is required by Galera +binlog_format=ROW + +log-bin + +# InnoDB is currently the only storage engine supported by Galera +default-storage-engine=innodb +innodb_file_per_table + +# To avoid issues with 'bulk mode inserts' using autoincrement fields +innodb_autoinc_lock_mode=2 + +# Required to prevent deadlocks on parallel transaction execution +innodb_locks_unsafe_for_binlog=1 + +# Query Cache is not supported by Galera wsrep replication +query_cache_size=0 +query_cache_type=0 + +# INITIAL SETUP +# In some systems bind-address defaults to 127.0.0.1, and with mysqldump SST +# it will have (most likely) disastrous consequences on donor node +bind-address=###NODE-ADDRESS### + +## +## WSREP options +## + +# INITIAL SETUP +# For the initial setup, wsrep should be disabled +wsrep_provider=none +# After initial setup, parameter should have full path to wsrep provider library +wsrep_provider=###GALERA-LIB-PATH### + +# Provider specific configuration options +wsrep_provider_options = "evs.keepalive_period = PT3S; evs.inactive_check_period = PT10S; evs.suspect_timeout = PT30S; evs.inactive_timeout = PT1M; evs.install_timeout = PT1M" + +# Logical cluster name. Should be the same for all nodes in the same cluster. +wsrep_cluster_name=skycluster + +# INITIAL SETUP +# Group communication system handle: for the first node to be launched, the value should be "gcomm://", indicating creation of a new cluster; +# for the other nodes joining the cluster, the value should be "gcomm://xxx.xxx.xxx.xxx:4567", where xxx.xxx.xxx.xxx should be the ip of a node +# already on the cluster (usually the first one) +# DEPRECATED +# wsrep_cluster_address=gcomm:// + +# Human-readable node name (non-unique). Hostname by default. +#wsrep_node_name=###NODE-NAME### +wsrep_node_name=galera003 + +# INITIAL SETUP +# Base replication [:port] of the node. +# The values supplied will be used as defaults for state transfer receiving, +# listening ports and so on. Default: address of the first network interface. +wsrep_node_address=###NODE-ADDRESS### + +# INITIAL SETUP +# Address for incoming client connections. Autodetect by default. +wsrep_node_incoming_address=###NODE-ADDRESS### + +# Number of threads that will process writesets from other nodes +wsrep_slave_threads=1 + +# Generate fake primary keys for non-PK tables (required for multi-master +# and parallel applying operation) +wsrep_certify_nonPK=1 + +# Debug level logging (1 = enabled) +wsrep_debug=1 + +# Convert locking sessions into transactions +wsrep_convert_LOCK_to_trx=0 + +# Number of retries for deadlocked autocommits +wsrep_retry_autocommit=1 + +# Change auto_increment_increment and auto_increment_offset automatically +wsrep_auto_increment_control=1 + +# Retry autoinc insert, when the insert failed for "duplicate key error" +wsrep_drupal_282555_workaround=0 + +# Enable "strictly synchronous" semantics for read operations +wsrep_causal_reads=1 + +# Command to call when node status or cluster membership changes. +# Will be passed all or some of the following options: +# --status - new status of this node +# --uuid - UUID of the cluster +# --primary - whether the component is primary or not ("yes"/"no") +# --members - comma-separated list of members +# --index - index of this node in the list +wsrep_notify_cmd= + +## +## WSREP State Transfer options +## + +# State Snapshot Transfer method +#wsrep_sst_method=mysqldump +#wsrep_sst_method=xtrabackup +wsrep_sst_method=rsync + +# INITIAL SETUP +# Address which donor should send State Snapshot to. +# Should be the address of the CURRENT node. DON'T SET IT TO DONOR ADDRESS!!! +# (SST method dependent. Defaults to the first IP of the first interface) +wsrep_sst_receive_address=###NODE-ADDRESS### + +# INITIAL SETUP +# SST authentication string. This will be used to send SST to joining nodes. +# Depends on SST method. For mysqldump method it is root: +#wsrep_sst_auth=###REP-USERNAME###:###REP-PASSWORD### +wsrep_sst_auth=repl:repl + +# Desired SST donor name. +#wsrep_sst_donor= + +# Reject client queries when donating SST (false) +#wsrep_sst_donor_rejects_queries=0 + +# Protocol version to use +# wsrep_protocol_version= diff --git a/maxscale-system-test/mdbci/cnf/mysql56/galera_server1.cnf b/maxscale-system-test/mdbci/cnf/mysql56/galera_server1.cnf new file mode 100644 index 000000000..0e646cb3b --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/galera_server1.cnf @@ -0,0 +1,135 @@ +[mysqld] +expire_logs_days=7 +user=mysql +server_id=1 + +# Row binary log format is required by Galera +binlog_format=ROW + +log-bin + +# InnoDB is currently the only storage engine supported by Galera +default-storage-engine=innodb +innodb_file_per_table + +# To avoid issues with 'bulk mode inserts' using autoincrement fields +innodb_autoinc_lock_mode=2 + +# Required to prevent deadlocks on parallel transaction execution +innodb_locks_unsafe_for_binlog=1 + +# Query Cache is not supported by Galera wsrep replication +query_cache_size=0 +query_cache_type=0 + +# INITIAL SETUP +# In some systems bind-address defaults to 127.0.0.1, and with mysqldump SST +# it will have (most likely) disastrous consequences on donor node +bind-address=###NODE-ADDRESS### + +## +## WSREP options +## + +# INITIAL SETUP +# For the initial setup, wsrep should be disabled +wsrep_provider=none +# After initial setup, parameter should have full path to wsrep provider library +wsrep_provider=###GALERA-LIB-PATH### + +# Provider specific configuration options +wsrep_provider_options = "evs.keepalive_period = PT3S; evs.inactive_check_period = PT10S; evs.suspect_timeout = PT30S; evs.inactive_timeout = PT1M; evs.install_timeout = PT1M" + +# Logical cluster name. Should be the same for all nodes in the same cluster. +wsrep_cluster_name=skycluster + +# INITIAL SETUP +# Group communication system handle: for the first node to be launched, the value should be "gcomm://", indicating creation of a new cluster; +# for the other nodes joining the cluster, the value should be "gcomm://xxx.xxx.xxx.xxx:4567", where xxx.xxx.xxx.xxx should be the ip of a node +# already on the cluster (usually the first one) +# DEPRECATED +# wsrep_cluster_address=gcomm:// + +# Human-readable node name (non-unique). Hostname by default. +#wsrep_node_name=###NODE-NAME### +wsrep_node_name=galera000 + +# INITIAL SETUP +# Base replication [:port] of the node. +# The values supplied will be used as defaults for state transfer receiving, +# listening ports and so on. Default: address of the first network interface. +wsrep_node_address=###NODE-ADDRESS### + +# INITIAL SETUP +# Address for incoming client connections. Autodetect by default. +wsrep_node_incoming_address=###NODE-ADDRESS### + +# Number of threads that will process writesets from other nodes +wsrep_slave_threads=1 + +# Generate fake primary keys for non-PK tables (required for multi-master +# and parallel applying operation) +wsrep_certify_nonPK=1 + +# Maximum number of rows in write set +wsrep_max_ws_rows=131072 + +# Maximum size of write set +wsrep_max_ws_size=1073741824 + +# Debug level logging (1 = enabled) +wsrep_debug=1 + +# Convert locking sessions into transactions +wsrep_convert_LOCK_to_trx=0 + +# Number of retries for deadlocked autocommits +wsrep_retry_autocommit=1 + +# Change auto_increment_increment and auto_increment_offset automatically +wsrep_auto_increment_control=1 + +# Retry autoinc insert, when the insert failed for "duplicate key error" +wsrep_drupal_282555_workaround=0 + +# Enable "strictly synchronous" semantics for read operations +wsrep_causal_reads=0 + +# Command to call when node status or cluster membership changes. +# Will be passed all or some of the following options: +# --status - new status of this node +# --uuid - UUID of the cluster +# --primary - whether the component is primary or not ("yes"/"no") +# --members - comma-separated list of members +# --index - index of this node in the list +wsrep_notify_cmd= + +## +## WSREP State Transfer options +## + +# State Snapshot Transfer method +#wsrep_sst_method=mysqldump +#wsrep_sst_method=xtrabackup +wsrep_sst_method=rsync + +# INITIAL SETUP +# Address which donor should send State Snapshot to. +# Should be the address of the CURRENT node. DON'T SET IT TO DONOR ADDRESS!!! +# (SST method dependent. Defaults to the first IP of the first interface) +wsrep_sst_receive_address=###NODE-ADDRESS### + +# INITIAL SETUP +# SST authentication string. This will be used to send SST to joining nodes. +# Depends on SST method. For mysqldump method it is root: +#wsrep_sst_auth=###REP-USERNAME###:###REP-PASSWORD### +wsrep_sst_auth=repl:repl + +# Desired SST donor name. +#wsrep_sst_donor= + +# Reject client queries when donating SST (false) +#wsrep_sst_donor_rejects_queries=0 + +# Protocol version to use +# wsrep_protocol_version= diff --git a/maxscale-system-test/mdbci/cnf/mysql56/galera_server2.cnf b/maxscale-system-test/mdbci/cnf/mysql56/galera_server2.cnf new file mode 100644 index 000000000..c459d80ad --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/galera_server2.cnf @@ -0,0 +1,135 @@ +[mysqld] +expire_logs_days=7 +user=mysql +server_id=2 + +# Row binary log format is required by Galera +binlog_format=ROW + +log-bin + +# InnoDB is currently the only storage engine supported by Galera +default-storage-engine=innodb +innodb_file_per_table + +# To avoid issues with 'bulk mode inserts' using autoincrement fields +innodb_autoinc_lock_mode=2 + +# Required to prevent deadlocks on parallel transaction execution +innodb_locks_unsafe_for_binlog=1 + +# Query Cache is not supported by Galera wsrep replication +query_cache_size=0 +query_cache_type=0 + +# INITIAL SETUP +# In some systems bind-address defaults to 127.0.0.1, and with mysqldump SST +# it will have (most likely) disastrous consequences on donor node +bind-address=###NODE-ADDRESS### + +## +## WSREP options +## + +# INITIAL SETUP +# For the initial setup, wsrep should be disabled +wsrep_provider=none +# After initial setup, parameter should have full path to wsrep provider library +wsrep_provider=###GALERA-LIB-PATH### + +# Provider specific configuration options +wsrep_provider_options = "evs.keepalive_period = PT3S; evs.inactive_check_period = PT10S; evs.suspect_timeout = PT30S; evs.inactive_timeout = PT1M; evs.install_timeout = PT1M" + +# Logical cluster name. Should be the same for all nodes in the same cluster. +wsrep_cluster_name=skycluster + +# INITIAL SETUP +# Group communication system handle: for the first node to be launched, the value should be "gcomm://", indicating creation of a new cluster; +# for the other nodes joining the cluster, the value should be "gcomm://xxx.xxx.xxx.xxx:4567", where xxx.xxx.xxx.xxx should be the ip of a node +# already on the cluster (usually the first one) +# DEPRECATED +# wsrep_cluster_address=gcomm:// + +# Human-readable node name (non-unique). Hostname by default. +#wsrep_node_name=###NODE-NAME### +wsrep_node_name=galera001 + +# INITIAL SETUP +# Base replication [:port] of the node. +# The values supplied will be used as defaults for state transfer receiving, +# listening ports and so on. Default: address of the first network interface. +wsrep_node_address=###NODE-ADDRESS### + +# INITIAL SETUP +# Address for incoming client connections. Autodetect by default. +wsrep_node_incoming_address=###NODE-ADDRESS### + +# Number of threads that will process writesets from other nodes +wsrep_slave_threads=1 + +# Generate fake primary keys for non-PK tables (required for multi-master +# and parallel applying operation) +wsrep_certify_nonPK=1 + +# Maximum number of rows in write set +wsrep_max_ws_rows=131072 + +# Maximum size of write set +wsrep_max_ws_size=1073741824 + +# Debug level logging (1 = enabled) +wsrep_debug=1 + +# Convert locking sessions into transactions +wsrep_convert_LOCK_to_trx=0 + +# Number of retries for deadlocked autocommits +wsrep_retry_autocommit=1 + +# Change auto_increment_increment and auto_increment_offset automatically +wsrep_auto_increment_control=1 + +# Retry autoinc insert, when the insert failed for "duplicate key error" +wsrep_drupal_282555_workaround=0 + +# Enable "strictly synchronous" semantics for read operations +wsrep_causal_reads=0 + +# Command to call when node status or cluster membership changes. +# Will be passed all or some of the following options: +# --status - new status of this node +# --uuid - UUID of the cluster +# --primary - whether the component is primary or not ("yes"/"no") +# --members - comma-separated list of members +# --index - index of this node in the list +wsrep_notify_cmd= + +## +## WSREP State Transfer options +## + +# State Snapshot Transfer method +#wsrep_sst_method=mysqldump +#wsrep_sst_method=xtrabackup +wsrep_sst_method=rsync + +# INITIAL SETUP +# Address which donor should send State Snapshot to. +# Should be the address of the CURRENT node. DON'T SET IT TO DONOR ADDRESS!!! +# (SST method dependent. Defaults to the first IP of the first interface) +wsrep_sst_receive_address=###NODE-ADDRESS### + +# INITIAL SETUP +# SST authentication string. This will be used to send SST to joining nodes. +# Depends on SST method. For mysqldump method it is root: +#wsrep_sst_auth=###REP-USERNAME###:###REP-PASSWORD### +wsrep_sst_auth=repl:repl + +# Desired SST donor name. +#wsrep_sst_donor= + +# Reject client queries when donating SST (false) +#wsrep_sst_donor_rejects_queries=0 + +# Protocol version to use +# wsrep_protocol_version= diff --git a/maxscale-system-test/mdbci/cnf/mysql56/galera_server3.cnf b/maxscale-system-test/mdbci/cnf/mysql56/galera_server3.cnf new file mode 100644 index 000000000..0bdaafd42 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/galera_server3.cnf @@ -0,0 +1,135 @@ +[mysqld] +expire_logs_days=7 +user=mysql +server_id=3 + +# Row binary log format is required by Galera +binlog_format=ROW + +log-bin + +# InnoDB is currently the only storage engine supported by Galera +default-storage-engine=innodb +innodb_file_per_table + +# To avoid issues with 'bulk mode inserts' using autoincrement fields +innodb_autoinc_lock_mode=2 + +# Required to prevent deadlocks on parallel transaction execution +innodb_locks_unsafe_for_binlog=1 + +# Query Cache is not supported by Galera wsrep replication +query_cache_size=0 +query_cache_type=0 + +# INITIAL SETUP +# In some systems bind-address defaults to 127.0.0.1, and with mysqldump SST +# it will have (most likely) disastrous consequences on donor node +bind-address=###NODE-ADDRESS### + +## +## WSREP options +## + +# INITIAL SETUP +# For the initial setup, wsrep should be disabled +wsrep_provider=none +# After initial setup, parameter should have full path to wsrep provider library +wsrep_provider=###GALERA-LIB-PATH### + +# Provider specific configuration options +wsrep_provider_options = "evs.keepalive_period = PT3S; evs.inactive_check_period = PT10S; evs.suspect_timeout = PT30S; evs.inactive_timeout = PT1M; evs.install_timeout = PT1M" + +# Logical cluster name. Should be the same for all nodes in the same cluster. +wsrep_cluster_name=skycluster + +# INITIAL SETUP +# Group communication system handle: for the first node to be launched, the value should be "gcomm://", indicating creation of a new cluster; +# for the other nodes joining the cluster, the value should be "gcomm://xxx.xxx.xxx.xxx:4567", where xxx.xxx.xxx.xxx should be the ip of a node +# already on the cluster (usually the first one) +# DEPRECATED +# wsrep_cluster_address=gcomm:// + +# Human-readable node name (non-unique). Hostname by default. +#wsrep_node_name=###NODE-NAME### +wsrep_node_name=galera002 + +# INITIAL SETUP +# Base replication [:port] of the node. +# The values supplied will be used as defaults for state transfer receiving, +# listening ports and so on. Default: address of the first network interface. +wsrep_node_address=###NODE-ADDRESS### + +# INITIAL SETUP +# Address for incoming client connections. Autodetect by default. +wsrep_node_incoming_address=###NODE-ADDRESS### + +# Number of threads that will process writesets from other nodes +wsrep_slave_threads=1 + +# Generate fake primary keys for non-PK tables (required for multi-master +# and parallel applying operation) +wsrep_certify_nonPK=1 + +# Maximum number of rows in write set +wsrep_max_ws_rows=131072 + +# Maximum size of write set +wsrep_max_ws_size=1073741824 + +# Debug level logging (1 = enabled) +wsrep_debug=1 + +# Convert locking sessions into transactions +wsrep_convert_LOCK_to_trx=0 + +# Number of retries for deadlocked autocommits +wsrep_retry_autocommit=1 + +# Change auto_increment_increment and auto_increment_offset automatically +wsrep_auto_increment_control=1 + +# Retry autoinc insert, when the insert failed for "duplicate key error" +wsrep_drupal_282555_workaround=0 + +# Enable "strictly synchronous" semantics for read operations +wsrep_causal_reads=0 + +# Command to call when node status or cluster membership changes. +# Will be passed all or some of the following options: +# --status - new status of this node +# --uuid - UUID of the cluster +# --primary - whether the component is primary or not ("yes"/"no") +# --members - comma-separated list of members +# --index - index of this node in the list +wsrep_notify_cmd= + +## +## WSREP State Transfer options +## + +# State Snapshot Transfer method +#wsrep_sst_method=mysqldump +#wsrep_sst_method=xtrabackup +wsrep_sst_method=rsync + +# INITIAL SETUP +# Address which donor should send State Snapshot to. +# Should be the address of the CURRENT node. DON'T SET IT TO DONOR ADDRESS!!! +# (SST method dependent. Defaults to the first IP of the first interface) +wsrep_sst_receive_address=###NODE-ADDRESS### + +# INITIAL SETUP +# SST authentication string. This will be used to send SST to joining nodes. +# Depends on SST method. For mysqldump method it is root: +#wsrep_sst_auth=###REP-USERNAME###:###REP-PASSWORD### +wsrep_sst_auth=repl:repl + +# Desired SST donor name. +#wsrep_sst_donor= + +# Reject client queries when donating SST (false) +#wsrep_sst_donor_rejects_queries=0 + +# Protocol version to use +# wsrep_protocol_version= diff --git a/maxscale-system-test/mdbci/cnf/mysql56/galera_server4.cnf b/maxscale-system-test/mdbci/cnf/mysql56/galera_server4.cnf new file mode 100644 index 000000000..78d510da9 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/galera_server4.cnf @@ -0,0 +1,135 @@ +[mysqld] +expire_logs_days=7 +user=mysql +server_id=4 + +# Row binary log format is required by Galera +binlog_format=ROW + +log-bin + +# InnoDB is currently the only storage engine supported by Galera +default-storage-engine=innodb +innodb_file_per_table + +# To avoid issues with 'bulk mode inserts' using autoincrement fields +innodb_autoinc_lock_mode=2 + +# Required to prevent deadlocks on parallel transaction execution +innodb_locks_unsafe_for_binlog=1 + +# Query Cache is not supported by Galera wsrep replication +query_cache_size=0 +query_cache_type=0 + +# INITIAL SETUP +# In some systems bind-address defaults to 127.0.0.1, and with mysqldump SST +# it will have (most likely) disastrous consequences on donor node +bind-address=###NODE-ADDRESS### + +## +## WSREP options +## + +# INITIAL SETUP +# For the initial setup, wsrep should be disabled +wsrep_provider=none +# After initial setup, parameter should have full path to wsrep provider library +wsrep_provider=###GALERA-LIB-PATH### + +# Provider specific configuration options +wsrep_provider_options = "evs.keepalive_period = PT3S; evs.inactive_check_period = PT10S; evs.suspect_timeout = PT30S; evs.inactive_timeout = PT1M; evs.install_timeout = PT1M" + +# Logical cluster name. Should be the same for all nodes in the same cluster. +wsrep_cluster_name=skycluster + +# INITIAL SETUP +# Group communication system handle: for the first node to be launched, the value should be "gcomm://", indicating creation of a new cluster; +# for the other nodes joining the cluster, the value should be "gcomm://xxx.xxx.xxx.xxx:4567", where xxx.xxx.xxx.xxx should be the ip of a node +# already on the cluster (usually the first one) +# DEPRECATED +# wsrep_cluster_address=gcomm:// + +# Human-readable node name (non-unique). Hostname by default. +#wsrep_node_name=###NODE-NAME### +wsrep_node_name=galera003 + +# INITIAL SETUP +# Base replication [:port] of the node. +# The values supplied will be used as defaults for state transfer receiving, +# listening ports and so on. Default: address of the first network interface. +wsrep_node_address=###NODE-ADDRESS### + +# INITIAL SETUP +# Address for incoming client connections. Autodetect by default. +wsrep_node_incoming_address=###NODE-ADDRESS### + +# Number of threads that will process writesets from other nodes +wsrep_slave_threads=1 + +# Generate fake primary keys for non-PK tables (required for multi-master +# and parallel applying operation) +wsrep_certify_nonPK=1 + +# Maximum number of rows in write set +wsrep_max_ws_rows=131072 + +# Maximum size of write set +wsrep_max_ws_size=1073741824 + +# Debug level logging (1 = enabled) +wsrep_debug=1 + +# Convert locking sessions into transactions +wsrep_convert_LOCK_to_trx=0 + +# Number of retries for deadlocked autocommits +wsrep_retry_autocommit=1 + +# Change auto_increment_increment and auto_increment_offset automatically +wsrep_auto_increment_control=1 + +# Retry autoinc insert, when the insert failed for "duplicate key error" +wsrep_drupal_282555_workaround=0 + +# Enable "strictly synchronous" semantics for read operations +wsrep_causal_reads=0 + +# Command to call when node status or cluster membership changes. +# Will be passed all or some of the following options: +# --status - new status of this node +# --uuid - UUID of the cluster +# --primary - whether the component is primary or not ("yes"/"no") +# --members - comma-separated list of members +# --index - index of this node in the list +wsrep_notify_cmd= + +## +## WSREP State Transfer options +## + +# State Snapshot Transfer method +#wsrep_sst_method=mysqldump +#wsrep_sst_method=xtrabackup +wsrep_sst_method=rsync + +# INITIAL SETUP +# Address which donor should send State Snapshot to. +# Should be the address of the CURRENT node. DON'T SET IT TO DONOR ADDRESS!!! +# (SST method dependent. Defaults to the first IP of the first interface) +wsrep_sst_receive_address=###NODE-ADDRESS### + +# INITIAL SETUP +# SST authentication string. This will be used to send SST to joining nodes. +# Depends on SST method. For mysqldump method it is root: +#wsrep_sst_auth=###REP-USERNAME###:###REP-PASSWORD### +wsrep_sst_auth=repl:repl + +# Desired SST donor name. +#wsrep_sst_donor= + +# Reject client queries when donating SST (false) +#wsrep_sst_donor_rejects_queries=0 + +# Protocol version to use +# wsrep_protocol_version= diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server1.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server1.cnf new file mode 100644 index 000000000..38d8d2c8a --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server1.cnf @@ -0,0 +1,39 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=1 +#slave_parallel_threads=2 +user=mysql +## x001 +#max_long_data_size=1000000000 +#innodb_log_file_size=2000000000 + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server10.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server10.cnf new file mode 100644 index 000000000..7c704b7de --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server10.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=10 +#slave_parallel_threads=2 +user=mysql + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server11.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server11.cnf new file mode 100644 index 000000000..815a54f4d --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server11.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=11 +#slave_parallel_threads=2 +user=mysql + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server12.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server12.cnf new file mode 100644 index 000000000..e3af6c8f8 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server12.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=12 +#slave_parallel_threads=2 +user=mysql + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server13.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server13.cnf new file mode 100644 index 000000000..67dade68a --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server13.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=13 +#slave_parallel_threads=2 +user=mysql + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server14.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server14.cnf new file mode 100644 index 000000000..dd237e89d --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server14.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=14 +#slave_parallel_threads=2 +user=mysql + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server15.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server15.cnf new file mode 100644 index 000000000..944cfcf50 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server15.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=15 +#slave_parallel_threads=2 +user=mysql + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server2.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server2.cnf new file mode 100644 index 000000000..321ebf9c4 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server2.cnf @@ -0,0 +1,39 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=2 +#slave_parallel_threads=2 +user=mysql +## x001 +#max_long_data_size=1000000000 +#innodb_log_file_size=2000000000 + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server3.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server3.cnf new file mode 100644 index 000000000..0d3694f79 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server3.cnf @@ -0,0 +1,39 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=3 +#slave_parallel_threads=2 +user=mysql +## x001 +#max_long_data_size=1000000000 +#innodb_log_file_size=2000000000 + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server4.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server4.cnf new file mode 100644 index 000000000..2cf00b9bb --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server4.cnf @@ -0,0 +1,39 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=4 +#slave_parallel_threads=2 +user=mysql +## x001 +#max_long_data_size=1000000000 +#innodb_log_file_size=2000000000 + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server5.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server5.cnf new file mode 100644 index 000000000..9dfd4e786 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server5.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=5 +#slave_parallel_threads=2 +user=mysql + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server6.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server6.cnf new file mode 100644 index 000000000..5007ec9f8 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server6.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=6 +#slave_parallel_threads=2 +user=mysql + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server7.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server7.cnf new file mode 100644 index 000000000..de3f8d470 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server7.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=7 +#slave_parallel_threads=2 +user=mysql + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server8.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server8.cnf new file mode 100644 index 000000000..5ab8c1f83 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server8.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=8 +#slave_parallel_threads=2 +user=mysql + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/mysql56/server9.cnf b/maxscale-system-test/mdbci/cnf/mysql56/server9.cnf new file mode 100644 index 000000000..0cc2cf435 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/mysql56/server9.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +#log-basename=mar +log-bin=mar-bin +#binlog-format=row +binlog-format=STATEMENT +server_id=9 +#slave_parallel_threads=2 +user=mysql + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server1.cnf b/maxscale-system-test/mdbci/cnf/server1.cnf new file mode 100644 index 000000000..3c3367b8f --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server1.cnf @@ -0,0 +1,39 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=row +binlog_row_image=full +server_id=1 +user=mysql +## x001 +max_long_data_size=1000000000 +innodb_log_file_size=2000000000 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server10.cnf b/maxscale-system-test/mdbci/cnf/server10.cnf new file mode 100644 index 000000000..9d7e48b92 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server10.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=STATEMENT +server_id=10 +user=mysql +## x001 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server11.cnf b/maxscale-system-test/mdbci/cnf/server11.cnf new file mode 100644 index 000000000..8733cfede --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server11.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=STATEMENT +server_id=11 +user=mysql +## x001 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server12.cnf b/maxscale-system-test/mdbci/cnf/server12.cnf new file mode 100644 index 000000000..d01d176cf --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server12.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=STATEMENT +server_id=12 +user=mysql +## x001 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server13.cnf b/maxscale-system-test/mdbci/cnf/server13.cnf new file mode 100644 index 000000000..27ccd9ed5 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server13.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=STATEMENT +server_id=13 +user=mysql +## x001 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server14.cnf b/maxscale-system-test/mdbci/cnf/server14.cnf new file mode 100644 index 000000000..e5d2fc2b0 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server14.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=STATEMENT +server_id=14 +user=mysql +## x001 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server15.cnf b/maxscale-system-test/mdbci/cnf/server15.cnf new file mode 100644 index 000000000..776376b2b --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server15.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=STATEMENT +server_id=15 +user=mysql +## x001 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server2.cnf b/maxscale-system-test/mdbci/cnf/server2.cnf new file mode 100644 index 000000000..ae6b3df84 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server2.cnf @@ -0,0 +1,39 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=row +binlog_row_image=full +server_id=2 +user=mysql +## x001 +max_long_data_size=1000000000 +innodb_log_file_size=2000000000 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server3.cnf b/maxscale-system-test/mdbci/cnf/server3.cnf new file mode 100644 index 000000000..24b0eaee4 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server3.cnf @@ -0,0 +1,39 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=row +binlog_row_image=full +server_id=3 +user=mysql +## x001 +max_long_data_size=1000000000 +innodb_log_file_size=2000000000 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server4.cnf b/maxscale-system-test/mdbci/cnf/server4.cnf new file mode 100644 index 000000000..c291692db --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server4.cnf @@ -0,0 +1,39 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=row +binlog_row_image=full +server_id=4 +user=mysql +## x001 +max_long_data_size=1000000000 +innodb_log_file_size=2000000000 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server5.cnf b/maxscale-system-test/mdbci/cnf/server5.cnf new file mode 100644 index 000000000..0abd4dc70 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server5.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=STATEMENT +server_id=5 +user=mysql +## x001 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server6.cnf b/maxscale-system-test/mdbci/cnf/server6.cnf new file mode 100644 index 000000000..5297a2b95 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server6.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=STATEMENT +server_id=6 +user=mysql +## x001 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server7.cnf b/maxscale-system-test/mdbci/cnf/server7.cnf new file mode 100644 index 000000000..6698ca27c --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server7.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=STATEMENT +server_id=7 +user=mysql +## x001 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server8.cnf b/maxscale-system-test/mdbci/cnf/server8.cnf new file mode 100644 index 000000000..a33cf1fb2 --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server8.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=STATEMENT +server_id=8 +user=mysql +## x001 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/cnf/server9.cnf b/maxscale-system-test/mdbci/cnf/server9.cnf new file mode 100644 index 000000000..f00a3454c --- /dev/null +++ b/maxscale-system-test/mdbci/cnf/server9.cnf @@ -0,0 +1,36 @@ + +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see +# +# See the examples of server my.cnf files in /usr/share/mysql/ +# + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +log-slave-updates +log-bin=mar-bin +binlog-format=STATEMENT +server_id=9 +user=mysql +## x001 +slave-skip-errors=all + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB-5.5 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mysqld-5.5] + +# These two groups are only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +[mariadb-5.5] + diff --git a/maxscale-system-test/mdbci/configure_backend.sh b/maxscale-system-test/mdbci/configure_backend.sh new file mode 100644 index 000000000..c05f697f8 --- /dev/null +++ b/maxscale-system-test/mdbci/configure_backend.sh @@ -0,0 +1,9 @@ +. ${script_dir}/set_env.sh $name + +${script_dir}/backend/setup_repl.sh +${script_dir}/backend/galera/setup_galera.sh + +${script_dir}/configure_core.sh + +rm ~/vagrant_lock + diff --git a/maxscale-system-test/mdbci/configure_core.sh b/maxscale-system-test/mdbci/configure_core.sh new file mode 100755 index 000000000..2c72b5ac9 --- /dev/null +++ b/maxscale-system-test/mdbci/configure_core.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -x + +ssh -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $maxscale_access_user@$maxscale_IP '$maxscale_access_sudo service iptables stop' + +ssh -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $maxscale_access_user@$maxscale_IP "$maxscale_access_sudo mkdir ccore; $maxscale_access_sudo chown $maxscale_access_user ccore" + +scp -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no ${script_dir}/add_core_cnf.sh $maxscale_access_user@$maxscale_IP:./ccore/ +ssh -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $maxscale_access_user@$maxscale_IP "$maxscale_access_sudo /home/$maxscale_access_user/ccore/add_core_cnf.sh" + +set +x diff --git a/maxscale-system-test/mdbci/configure_log_dir.sh b/maxscale-system-test/mdbci/configure_log_dir.sh new file mode 100644 index 000000000..568c298b7 --- /dev/null +++ b/maxscale-system-test/mdbci/configure_log_dir.sh @@ -0,0 +1,15 @@ +set -x +LOGS_DIR=${logs_dir:-$HOME/LOGS} +echo $JOB_NAME | grep "/" +if [ $? == 0 ] ; then + export job_name_buildID=`echo $JOB_NAME | sed "s|/|-$BUILD_NUMBER/|"` + export logs_publish_dir="${LOGS_DIR}/${job_name_buildID}/" +else + export logs_publish_dir="${LOGS_DIR}/${JOB_NAME}-${BUILD_NUMBER}" +fi + +export job_name_buildID=`echo ${JOB_NAME} | sed "s|/|-${BUILD_NUMBER}/|"` +export logs_publish_dir="${LOGS_DIR}/${job_name_buildID}-${BUILD_NUMBER}" +echo "Logs go to ${logs_publish_dir}" +mkdir -p ${logs_publish_dir} + diff --git a/maxscale-system-test/mdbci/copy_logs.sh b/maxscale-system-test/mdbci/copy_logs.sh new file mode 100755 index 000000000..a3a9ba3db --- /dev/null +++ b/maxscale-system-test/mdbci/copy_logs.sh @@ -0,0 +1,5 @@ +set -x +rsync -a --no-o --no-g LOGS ${logs_publish_dir} +chmod a+r ${logs_publish_dir}/* +cp -r ${MDBCI_VM_PATH}/$name ${logs_publish_dir} +cp ${MDBCI_VM_PATH}/${name}.json ${logs_publish_dir} diff --git a/maxscale-system-test/mdbci/create_config.sh b/maxscale-system-test/mdbci/create_config.sh new file mode 100755 index 000000000..ed449330f --- /dev/null +++ b/maxscale-system-test/mdbci/create_config.sh @@ -0,0 +1,59 @@ +#!/bin/bash +set -x +export dir=`pwd` + +# read the name of build scripts directory +export script_dir="$(dirname $(readlink -f $0))" + +. ${script_dir}/set_run_test_variables.sh + +${mdbci_dir}/repository-config/generate_all.sh repo.d +${mdbci_dir}/repository-config/maxscale-ci.sh $target repo.d + + +export repo_dir=$dir/repo.d/ + +export provider=`${mdbci_dir}/mdbci show provider $box --silent 2> /dev/null` +export backend_box=${backend_box:-"centos_7_"$provider} + +if [ "$product" == "mysql" ] ; then + export cnf_path=${script_dir}/cnf/mysql56 +fi + +mkdir -p ${MDBCI_VM_PATH}/$name +cd ${MDBCI_VM_PATH}/$name +vagrant destroy -f +cd $dir + +mkdir ${MDBCI_VM_PATH}/$name/cnf +cp -r ${cnf_path}/* ${MDBCI_VM_PATH}/$name/cnf/ +export cnd_path="${MDBCI_VM_PATH}/$name/cnf/" + + eval "cat < /dev/null > ${MDBCI_VM_PATH}/${name}.json + +${mdbci_dir}/mdbci --override --template ${MDBCI_VM_PATH}/${name}.json --repo-dir ${repo_dir} generate $name + +while [ -f ~/vagrant_lock ] +do + echo "vagrant is locked, waiting ..." + sleep 5 +done +touch ~/vagrant_lock +echo ${JOB_NAME}-${BUILD_NUMBER} >> ~/vagrant_lock + +echo "running vagrant up $provider" + +${mdbci_dir}/mdbci up $name --attempts 3 +if [ $? != 0 ]; then + echo "Error creating configuration" + rm ~/vagrant_lock + exit 1 +fi + +cp ~/build-scripts/team_keys . +${mdbci_dir}/mdbci public_keys --key ${team_keys} $name + +rm ~/vagrant_lock +exit 0 diff --git a/maxscale-system-test/mdbci/destroy.sh b/maxscale-system-test/mdbci/destroy.sh new file mode 100755 index 000000000..d12ec0764 --- /dev/null +++ b/maxscale-system-test/mdbci/destroy.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +dir=`pwd` +cd ${MDBCI_VM_PATH}/${name} +vagrant destroy -f +cd $dir + +rm -rf ${MDBCI_VM_PATH}/${name} +rm -rf ${MDBCI_VM_PATH}/${name}.json +rm -rf ${MDBCI_VM_PATH}/${name}_network_config diff --git a/maxscale-system-test/mdbci/run_test.sh b/maxscale-system-test/mdbci/run_test.sh new file mode 100755 index 000000000..eb0f32c73 --- /dev/null +++ b/maxscale-system-test/mdbci/run_test.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +# see set_run_test_variables.sh for default values of all variables + +# $box - Name of Vagrant box for Maxscale machine +# see lists of supported boxes +# https://github.com/mariadb-corporation/mdbci/tree/integration/BOXES + +# $template - name of MDBCI json template file +# Template file have to be in ./templates/, file name +# have to be '$template.json.template +# Template file can contain references to any variables - +# all ${variable_name} will be replaced with values + +# $name - name of test run. It can be any string of leytters or digits +# If it is not defined, name will be automatically genereted +# using $box and current date and time + +# $ci_url - URL to Maxscale CI repository +# (default "http://max-tst-01.mariadb.com/ci-repository/") +# if build is done also locally and binaries are not uploaded to +# max-tst-01.mariadb.com $ci_url should toint to local web server +# e.g. http://192.168.122.1/repository (IP should be a host IP in the +# virtual network (not 127.0.0.1)) + +# $product - 'mariadb' or 'mysql' + +# $version - version of backend DB (e.g. '10.1', '10.2') + +# $galera_version - version of Galera backend DB +# same as $version by default + +# $target - name of binary repository +# (name of subdirectroy http://max-tst-01.mariadb.com/ci-repository/) + +# $team_keys - path to the file with open ssh keys to be +# installed on all VMs (default ${HOME}/team_keys) + +# $don_not_destroy_vm - if 'yes' VM won't be destored afther the test + +# $test_set - parameters to be send to 'ctest' (e.g. '-I 1,100', +# '-LE UNSTABLE' + +export vm_memory=${vm_memory:-"2048"} +export dir=`pwd` + +ulimit -n + +# read the name of build scripts directory +export script_dir="$(dirname $(readlink -f $0))" + +. ${script_dir}/set_run_test_variables.sh + +rm -rf LOGS + +export target=`echo $target | sed "s/?//g"` +export name=`echo $name | sed "s/?//g"` + +. ${script_dir}/configure_log_dir.sh + +cd ${script_dir}/.. + +cmake . -DBUILDNAME=$name -DCMAKE_BUILD_TYPE=Debug +make + +${script_dir}/create_config.sh +res=$? + +if [ $res == 0 ] ; then + . ${script_dir}/configure_backend.sh + ${mdbci_dir}/mdbci snapshot take --path-to-nodes $name --snapshot-name clean + + if [ ! -z "${named_test}" ] ; then + ./${named_test} + else + ./check_backend + if [ $? != 0 ]; then + echo "Backend broken!" + if [ "${do_not_destroy_vm}" != "yes" ] ; then + ${script_dir}/destroy.sh + fi + rm ~/vagrant_lock + exit 1 + fi + ctest -VV -D Nightly ${test_set} + fi + + cd $dir + ${script_dir}/copy_logs.sh +else + echo "Failed to create VMs, exiting" + if [ "${do_not_destroy_vm}" != "yes" ] ; then + ${script_dir}/destroy.sh + fi + rm ~/vagrant_lock + exit 1 +fi + +if [ "${do_not_destroy_vm}" != "yes" ] ; then + ${script_dir}/destroy.sh + echo "clean up done!" +fi + diff --git a/maxscale-system-test/mdbci/run_test_snapshot.sh b/maxscale-system-test/mdbci/run_test_snapshot.sh new file mode 100755 index 000000000..77d319121 --- /dev/null +++ b/maxscale-system-test/mdbci/run_test_snapshot.sh @@ -0,0 +1,90 @@ +#!/bin/bash + +function checkExitStatus { + returnCode=$1 + errorMessage=$2 + lockFilePath=$3 + if [ "$returnCode" != 0 ]; then + echo "$errorMesage" + rm $lockFilePath + echo "Snapshot lock file was deleted due to an error" + exit 1 + fi +} + +set -x +export dir=`pwd` + +# read the name of build scripts directory +export script_dir="$(dirname $(readlink -f $0))" + +. ${script_dir}/set_run_test_variables.sh +export name="$box-$product-$version-permanent" + +export snapshot_name=${snapshot_name:-"clean"} + +rm -rf LOGS + +export target=`echo $target | sed "s/?//g"` +export name=`echo $name | sed "s/?//g"` + +. ${script_dir}/configure_log_dir.sh + +# Setting snapshot_lock +export snapshot_lock_file=${MDBCI_VM_PATH}/${name}_snapshot_lock +if [ -f ${snapshot_lock_file} ]; then + echo "Snapshot is locked, waiting ..." +fi +while [ -f ${snapshot_lock_file} ] +do + sleep 5 +done + +touch ${snapshot_lock_file} +echo $JOB_NAME-$BUILD_NUMBER >> ${snapshot_lock_file} + +export repo_dir=$dir/repo.d/ + +${mdbci_dir}/mdbci snapshot revert --path-to-nodes $name --snapshot-name $snapshot_name + +if [ $? != 0 ]; then + ${script_dir}/destroy.sh + ${MDBCI_VM_PATH}/scripts/clean_vms.sh $name + + ${script_dir}/create_config.sh + checkExitStatus $? "Error creating configuration" $snapshot_lock_file + . ${script_dir}/configure_backend.sh + + echo "Creating snapshot from new config" + $HOME/mdbci/mdbci snapshot take --path-to-nodes $name --snapshot-name $snapshot_name +fi + +. ${script_dir}/set_env.sh "$name" + +${mdbci_dir}/repository-config/maxscale-ci.sh $target repo.d + + +${mdbci_dir}/mdbci sudo --command 'yum remove maxscale -y' $name/maxscale +${mdbci_dir}/mdbci sudo --command 'yum clean all' $name/maxscale + +${mdbci_dir}/mdbci setup_repo --product maxscale $name/maxscale --repo-dir $repo_dir +${mdbci_dir}/mdbci install_product --product maxscale $name/maxscale --repo-dir $repo_dir + +checkExitStatus $? "Error installing Maxscale" $snapshot_lock_file + +cd ${script_dir}/.. + +cmake . -DBUILDNAME=$JOB_NAME-$BUILD_NUMBER-$target +make + +./check_backend --restart-galera + +checkExitStatus $? "Failed to check backends" $snapshot_lock_file + +ctest $test_set -VV -D Nightly + +${script_dir}/copy_logs.sh + +# Removing snapshot_lock +rm ${snapshot_lock_file} + diff --git a/maxscale-system-test/mdbci/set_env.sh b/maxscale-system-test/mdbci/set_env.sh new file mode 100644 index 000000000..1fb25a35e --- /dev/null +++ b/maxscale-system-test/mdbci/set_env.sh @@ -0,0 +1,97 @@ +#!/bin/bash +set -x +echo $* +export MDBCI_VM_PATH=${MDBCI_VM_PATH:-$HOME/vms} +export mdbci_dir=${mdbci_dir:-"$HOME/mdbci/"} + +export config_name="$1" +if [ -z $1 ] ; then + config_name="test1" +fi + +export curr_dir=`pwd` + +export maxscale_binlog_dir="/var/lib/maxscale/Binlog_Service" +export maxdir="/usr/bin/" +export maxdir_bin="/usr/bin/" +export maxscale_cnf="/etc/maxscale.cnf" +export maxscale_log_dir="/var/log/maxscale/" + +# Number of nodes +export galera_N=`cat "$MDBCI_VM_PATH/$config_name"_network_config | grep galera | grep network | wc -l` +export node_N=`cat "$MDBCI_VM_PATH/$config_name"_network_config | grep node | grep network | wc -l` +sed "s/^/export /g" "$MDBCI_VM_PATH/$config_name"_network_config > "$curr_dir"/"$config_name"_network_config_export +source "$curr_dir"/"$config_name"_network_config_export + +# IP Of MaxScale machine +export maxscale_IP=$maxscale_network +export maxscale_sshkey=$maxscale_keyfile + +# User name and Password for Master/Slave replication setup (should have all PRIVILEGES) +export node_user="skysql" +export node_password="skysql" + +# User name and Password for Galera setup (should have all PRIVILEGES) +export galera_user="skysql" +export galera_password="skysql" + +export maxscale_user="skysql" +export maxscale_password="skysql" + +export maxadmin_password="mariadb" + +for prefix in "node" "galera" +do + N_var="$prefix"_N + Nx=${!N_var} + N=`expr $Nx - 1` + for i in $(seq 0 $N) + do + num=`printf "%03d" $i` + eval 'export "$prefix"_"$num"_port=3306' + eval 'export "$prefix"_"$num"_access_sudo=sudo' + + start_cmd_var="$prefix"_"$num"_start_db_command + stop_cmd_var="$prefix"_"$num"_stop_db_command + mysql_exe=`${mdbci_dir}/mdbci ssh --command 'ls /etc/init.d/mysql* 2> /dev/null | tr -cd "[:print:]"' $config_name/node_$num --silent 2> /dev/null` + echo $mysql_exe | grep -i "mysql" + if [ $? != 0 ] ; then + service_name=`${mdbci_dir}/mdbci ssh --command 'systemctl list-unit-files | grep mysql' $config_name/node_$num --silent` + echo $service_name | grep mysql + if [ $? == 0 ] ; then + echo $service_name | grep mysqld + if [ $? == 0 ] ; then + eval 'export $start_cmd_var="service mysqld start "' + eval 'export $stop_cmd_var="service mysqld stop "' + else + eval 'export $start_cmd_var="service mysql start "' + eval 'export $stop_cmd_var="service mysql stop "' + fi + else + ${mdbci_dir}/mdbci ssh --command 'echo \"/usr/sbin/mysqld \$* 2> stderr.log > stdout.log &\" > mysql_start.sh; echo \"sleep 20\" >> mysql_start.sh; echo \"disown\" >> mysql_start.sh; chmod a+x mysql_start.sh' $config_name/node_$num --silent + eval 'export $start_cmd_var="/home/$au/mysql_start.sh "' + eval 'export $start_cmd_var="killall mysqld "' + fi + else + eval 'export $start_cmd_var="$mysql_exe start "' + eval 'export $stop_cmd_var="$mysql_exe stop "' + fi + + eval 'export "$prefix"_"$num"_start_vm_command="cd $mdbci_dir/$config_name;vagrant up node_$num --provider=$provider; cd $curr_dir"' + eval 'export "$prefix"_"$num"_kill_vm_command="cd $mdbci_dir/$config_name;vagrant halt node_$num --provider=$provider; cd $curr_dir"' + done +done + +export maxscale_access_user=$maxscale_whoami +export maxscale_access_sudo="sudo " + +# Sysbench directory (should be sysbench >= 0.5) +export sysbench_dir=${sysbench_dir:-"$HOME/sysbench_deb7/sysbench/"} + +export ssl=true + +export take_snapshot_command="${mdbci_dir}/mdbci snapshot take --path-to-nodes $name --snapshot-name " +export revert_snapshot_command="${mdbci_dir}/mdbci snapshot revert --path-to-nodes $name --snapshot-name " +#export use_snapshots=yes + +set +x diff --git a/maxscale-system-test/mdbci/set_run_test_variables.sh b/maxscale-system-test/mdbci/set_run_test_variables.sh new file mode 100644 index 000000000..27bd17382 --- /dev/null +++ b/maxscale-system-test/mdbci/set_run_test_variables.sh @@ -0,0 +1,32 @@ +#!/bin/bash + + +export MDBCI_VM_PATH=${MDBCI_VM_PATH:-$HOME/vms} +mkdir -p $MDBCI_VM_PATH +echo "MDBCI_VM_PATH=$MDBCI_VM_PATH" + +export box=${box:-"centos_7_libvirt"} +echo "box=$box" + +export template=${template:-"default"} + +export curr_date=`date '+%Y-%m-%d_%H-%M'` + +export name=${name:-$box-${curr_date}} + +export mdbci_dir=${mdbci_dir:-"$HOME/mdbci/"} +export ci_url=${ci_url:-"http://max-tst-01.mariadb.com/ci-repository/"} + +export product=${product:-"mariadb"} +export version=${version:-"10.2"} +export target=${target:-"develop"} +export vm_memory=${vm_memory:-"2048"} +export cnf_path=${script_dir}/cnf +export JOB_NAME=${JOB_NAME:-"local_test"} +export BUILD_NUMBER=${BUILD_NUMBER:-`date '+%Y%m%d%H%M'`} +export BUILD_TAG=${BUILD_TAG:-jenkins-${JOB_NAME}-${BUILD_NUMBER}} +export team_keys=${team_keys:-${HOME}/team_keys} +export galera_version=${galera_version:-$version} +export do_not_destroy_vm=${do_not_destroy_vm:-"no"} +#export test_set=${test_set:-"-LE UNSTABLE"} +export test_set=${test_set:-"-I 1,5"} diff --git a/maxscale-system-test/mdbci/templates/big.json.template b/maxscale-system-test/mdbci/templates/big.json.template new file mode 100644 index 000000000..9592c5647 --- /dev/null +++ b/maxscale-system-test/mdbci/templates/big.json.template @@ -0,0 +1,152 @@ +{ + "node_000" : + { + "hostname" : "node000", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server1.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + + }, + + "node_001" : + { + "hostname" : "node001", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server2.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "node_002" : + { + "hostname" : "node002", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server3.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "node_003" : + { + "hostname" : "node003", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server4.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_004" : + { + "hostname" : "node004", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server5.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_005" : + { + "hostname" : "node005", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server6.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_006" : + { + "hostname" : "node006", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server7.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_007" : + { + "hostname" : "node007", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server8.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "galera_000" : + { + "hostname" : "galera000", + "box" : "centos_7_aws", + "product" : { + "name": "galera", + "version": "###galera_version###", + "cnf_template" : "galera_server1.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "galera_001" : + { + "hostname" : "galera001", + "box" : "centos_7_aws", + "product" : { + "name": "galera", + "version": "###galera_version###", + "cnf_template" : "galera_server2.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "galera_002" : + { + "hostname" : "galera002", + "box" : "centos_7_aws", + "product" : { + "name": "galera", + "version": "###galera_version###", + "cnf_template" : "galera_server3.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "galera_003" : + { + "hostname" : "galera003", + "box" : "centos_7_aws", + "product" : { + "name": "galera", + "version": "###galera_version###", + "cnf_template" : "galera_server4.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "maxscale" : + { + "hostname" : "maxscale", + "box" : "centos_7_aws_large", + "product" : { + "name": "maxscale" + } + + } +} diff --git a/maxscale-system-test/mdbci/templates/big15.json.template b/maxscale-system-test/mdbci/templates/big15.json.template new file mode 100644 index 000000000..5d50432d7 --- /dev/null +++ b/maxscale-system-test/mdbci/templates/big15.json.template @@ -0,0 +1,229 @@ +{ + "node_000" : + { + "hostname" : "node_000", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server1.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + + }, + + "node_001" : + { + "hostname" : "node_001", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server2.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "node_002" : + { + "hostname" : "node_002", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server3.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "node_003" : + { + "hostname" : "node_003", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server4.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_004" : + { + "hostname" : "node_004", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server5.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_005" : + { + "hostname" : "node_005", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server6.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_006" : + { + "hostname" : "node_006", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server7.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_007" : + { + "hostname" : "node_007", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server8.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_008" : + { + "hostname" : "node_008", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server9.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_009" : + { + "hostname" : "node_009", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server10.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_0010" : + { + "hostname" : "node_0010", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server11.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_0011" : + { + "hostname" : "node_0011", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server12.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_0012" : + { + "hostname" : "node_0012", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server13.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_0013" : + { + "hostname" : "node_0013", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server14.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + "node_0014" : + { + "hostname" : "node_0014", + "box" : "centos_7_aws_large", + "product" : { + "name": "###product###", + "version": "###version###", + "cnf_template" : "server15.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "galera_000" : + { + "hostname" : "galera_000", + "box" : "centos_7_aws", + "product" : { + "name": "galera", + "version": "###galera_version###", + "cnf_template" : "galera_server1.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "galera_001" : + { + "hostname" : "galera_001", + "box" : "centos_7_aws", + "product" : { + "name": "galera", + "version": "###galera_version###", + "cnf_template" : "galera_server2.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "galera_002" : + { + "hostname" : "galera_002", + "box" : "centos_7_aws", + "product" : { + "name": "galera", + "version": "###galera_version###", + "cnf_template" : "galera_server3.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "galera_003" : + { + "hostname" : "galera_003", + "box" : "centos_7_aws", + "product" : { + "name": "galera", + "version": "###galera_version###", + "cnf_template" : "galera_server4.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "maxscale" : + { + "hostname" : "maxscale", + "box" : "centos_7_aws_large", + "product" : { + "name": "maxscale" + } + + } +} diff --git a/maxscale-system-test/mdbci/templates/default.json.template b/maxscale-system-test/mdbci/templates/default.json.template new file mode 100644 index 000000000..2376e46a7 --- /dev/null +++ b/maxscale-system-test/mdbci/templates/default.json.template @@ -0,0 +1,117 @@ +{ + "node_000" : + { + "hostname" : "node000", + "box" : "${backend_box}", + "memory_size" : "${vm_memory}", + "product" : { + "name": "${product}", + "version": "${version}", + "cnf_template" : "server1.cnf", + "cnf_template_path": "${cnf_path}" + } + + }, + + "node_001" : + { + "hostname" : "node001", + "box" : "${backend_box}", + "memory_size" : "${vm_memory}", + "product" : { + "name": "${product}", + "version": "${version}", + "cnf_template" : "server2.cnf", + "cnf_template_path": "${cnf_path}" + } + }, + + "node_002" : + { + "hostname" : "node002", + "box" : "${backend_box}", + "memory_size" : "${vm_memory}", + "product" : { + "name": "${product}", + "version": "${version}", + "cnf_template" : "server3.cnf", + "cnf_template_path": "${cnf_path}" + } + }, + + "node_003" : + { + "hostname" : "node003", + "box" : "${backend_box}", + "memory_size" : "${vm_memory}", + "product" : { + "name": "${product}", + "version": "${version}", + "cnf_template" : "server4.cnf", + "cnf_template_path": "${cnf_path}" + } + }, + + "galera_000" : + { + "hostname" : "galera000", + "box" : "${backend_box}", + "memory_size" : "${vm_memory}", + "product" : { + "name": "galera", + "version": "${galera_version}", + "cnf_template" : "galera_server1.cnf", + "cnf_template_path": "${cnf_path}" + } + }, + + "galera_001" : + { + "hostname" : "galera001", + "box" : "${backend_box}", + "memory_size" : "${vm_memory}", + "product" : { + "name": "galera", + "version": "${galera_version}", + "cnf_template" : "galera_server2.cnf", + "cnf_template_path": "${cnf_path}" + } + }, + + "galera_002" : + { + "hostname" : "galera002", + "box" : "${backend_box}", + "memory_size" : "${vm_memory}", + "product" : { + "name": "galera", + "version": "${galera_version}", + "cnf_template" : "galera_server3.cnf", + "cnf_template_path": "${cnf_path}" + } + }, + + "galera_003" : + { + "hostname" : "galera003", + "box" : "${backend_box}", + "memory_size" : "${vm_memory}", + "product" : { + "name": "galera", + "version": "${galera_version}", + "cnf_template" : "galera_server4.cnf", + "cnf_template_path": "${cnf_path}" + } + }, + + "maxscale" : + { + "hostname" : "maxscale", + "box" : "${box}", + "memory_size" : "${vm_memory}", + "product" : { + "name": "maxscale" + } + + } +} diff --git a/maxscale-system-test/mdbci/templates/performance.json.template b/maxscale-system-test/mdbci/templates/performance.json.template new file mode 100644 index 000000000..a767f9fc2 --- /dev/null +++ b/maxscale-system-test/mdbci/templates/performance.json.template @@ -0,0 +1,67 @@ +{ + +###nodes### + + "galera_000" : + { + "hostname" : "galera000", + "box" : "centos_7_libvirt", + "memory_size" : "2048", + "product" : { + "name": "galera", + "version": "###galera_version###", + "cnf_template" : "galera_server1.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "galera_001" : + { + "hostname" : "galera001", + "box" : "centos_7_libvirt", + "memory_size" : "2048", + "product" : { + "name": "galera", + "version": "###galera_version###", + "cnf_template" : "galera_server2.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "galera_002" : + { + "hostname" : "galera002", + "box" : "centos_7_libvirt", + "memory_size" : "2048", + "product" : { + "name": "galera", + "version": "###galera_version###", + "cnf_template" : "galera_server3.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "galera_003" : + { + "hostname" : "galera003", + "box" : "centos_7_libvirt", + "memory_size" : "2048", + "product" : { + "name": "galera", + "version": "###galera_version###", + "cnf_template" : "galera_server4.cnf", + "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf" + } + }, + + "maxscale" : + { + "hostname" : "maxscale", + "box" : "###box###", + "memory_size" : "2048", + "product" : { + "name": "maxscale" + } + + } +} diff --git a/maxscale-system-test/mxs1476.cpp b/maxscale-system-test/mxs1476.cpp index d4770de54..8dc1e367d 100644 --- a/maxscale-system-test/mxs1476.cpp +++ b/maxscale-system-test/mxs1476.cpp @@ -19,7 +19,7 @@ void do_test(TestConnections& test, int master, int slave) test.try_query(test.maxscales->conn_rwsplit[0], "INSERT INTO test.t1 VALUES (1)"); test.tprintf("Start the slave node and perform another insert"); - test.galera->start_node(slave, (char *) ""); + test.galera->start_node(slave, (char*)""); sleep(5); test.try_query(test.maxscales->conn_rwsplit[0], "INSERT INTO test.t1 VALUES (1)"); test.maxscales->close_maxscale_connections(0); @@ -31,7 +31,7 @@ void do_test(TestConnections& test, int master, int slave) test.try_query(test.maxscales->conn_rwsplit[0], "INSERT INTO test.t1 VALUES (1)"); test.tprintf("Start the master node and perform another insert (expecting failure)"); - test.galera->start_node(master, (char *) ""); + test.galera->start_node(master, (char*)""); sleep(5); test.add_result(execute_query_silent(test.maxscales->conn_rwsplit[0], "INSERT INTO test.t1 VALUES (1)") == 0, "Query should fail"); diff --git a/maxscale-system-test/mxs1509.cpp b/maxscale-system-test/mxs1509.cpp index 4c495e217..4ae7f61bb 100644 --- a/maxscale-system-test/mxs1509.cpp +++ b/maxscale-system-test/mxs1509.cpp @@ -6,7 +6,6 @@ #include "testconnections.h" #include -#include "maxscales.h" void change_master(TestConnections& test, int slave, int master, const char* name = NULL) { @@ -19,8 +18,7 @@ void change_master(TestConnections& test, int slave, int master, const char* nam source += "'"; } - execute_query(test.repl->nodes[slave], - "STOP ALL SLAVES;CHANGE MASTER %s TO master_host='%s', master_port=3306, " + execute_query(test.repl->nodes[slave], "STOP ALL SLAVES;CHANGE MASTER %s TO master_host='%s', master_port=3306, " "master_user='%s', master_password='%s', master_use_gtid=slave_pos;START ALL SLAVES", source.c_str(), test.repl->IP[master], test.repl->user_name, test.repl->password, source.c_str()); } @@ -30,14 +28,14 @@ const char* dump_status(const StringSet& current, const StringSet& expected) std::stringstream ss; ss << "Current status: ("; - for (const auto& a : current) + for (const auto& a: current) { ss << a << ","; } ss << ") Expected status: ("; - for (const auto& a : expected) + for (const auto& a: expected) { ss << a << ","; } @@ -51,8 +49,8 @@ const char* dump_status(const StringSet& current, const StringSet& expected) void check_status(TestConnections& test, const StringSet& expected_master, const StringSet& expected_slave) { sleep(2); - StringSet master = test.maxscales->get_server_status(0, "server1"); - StringSet slave = test.maxscales->get_server_status(0, "server2"); + StringSet master = test.get_server_status("server1"); + StringSet slave = test.get_server_status("server2"); test.add_result(master != expected_master, "Master status is not what was expected: %s", dump_status(master, expected_master)); test.add_result(slave != expected_slave, "Slave status is not what was expected: %s", diff --git a/maxscale-system-test/mxs431.cpp b/maxscale-system-test/mxs431.cpp index 9cf26ce77..15e9d00c8 100644 --- a/maxscale-system-test/mxs431.cpp +++ b/maxscale-system-test/mxs431.cpp @@ -5,62 +5,52 @@ * - Connect repeatedly to MaxScale with 'testdb' as the default database and execute SELECT 1 */ - -#include #include "testconnections.h" -using namespace std; - int main(int argc, char *argv[]) { - TestConnections * Test = new TestConnections(argc, argv); + TestConnections test(argc, argv); char str[256]; - int iterations = Test->smoke ? 100 : 500; - Test->repl->execute_query_all_nodes((char *) "set global max_connections = 600;"); - Test->set_timeout(30); - Test->repl->stop_slaves(); - Test->set_timeout(30); - Test->maxscales->restart_maxscale(0); - Test->set_timeout(30); - Test->repl->connect(); - Test->stop_timeout(); + int iterations = 100; + test.repl->execute_query_all_nodes((char *) "set global max_connections = 600;"); + test.set_timeout(200); + test.repl->stop_slaves(); + test.set_timeout(200); + test.restart_maxscale(); + test.set_timeout(200); + test.repl->connect(); + test.stop_timeout(); /** Create a database on each node */ - for (int i = 0; i < Test->repl->N; i++) + for (int i = 0; i < test.repl->N; i++) { - Test->set_timeout(20); + test.set_timeout(20); sprintf(str, "DROP DATABASE IF EXISTS shard_db%d", i); - Test->tprintf("%s\n", str); - execute_query(Test->repl->nodes[i], str); - Test->set_timeout(20); + test.tprintf("%s\n", str); + execute_query(test.repl->nodes[i], str); + test.set_timeout(20); sprintf(str, "CREATE DATABASE shard_db%d", i); - Test->tprintf("%s\n", str); - execute_query(Test->repl->nodes[i], str); - Test->stop_timeout(); + test.tprintf("%s\n", str); + execute_query(test.repl->nodes[i], str); + test.stop_timeout(); } - Test->repl->close_connections(); + test.repl->close_connections(); - for (int j = 0; j < iterations && Test->global_result == 0; j++) + for (int j = 0; j < iterations && test.global_result == 0; j++) { - for (int i = 0; i < Test->repl->N; i++) + for (int i = 0; i < test.repl->N && test.global_result == 0; i++) { sprintf(str, "shard_db%d", i); - Test->set_timeout(15); - MYSQL *conn = open_conn_db(Test->maxscales->rwsplit_port[0], Test->maxscales->IP[0], - str, Test->maxscales->user_name, - Test->maxscales->password, Test->ssl); - Test->set_timeout(15); - Test->tprintf("Trying DB %d\n", i); - if (execute_query(conn, "SELECT 1")) - { - Test->add_result(1, "Failed at %d\n", j); - break; - } + test.set_timeout(30); + MYSQL *conn = open_conn_db(test.rwsplit_port, test.maxscale_IP, + str, test.maxscale_user, + test.maxscale_password, test.ssl); + test.set_timeout(30); + test.add_result(execute_query(conn, "SELECT 1"), "Trying DB %d failed at %d", i, j); mysql_close(conn); } } - int rval = Test->global_result; - delete Test; - return rval; + + return test.global_result; } diff --git a/maxscale-system-test/testconnections.cpp b/maxscale-system-test/testconnections.cpp index 52d472af7..72ee7fb81 100644 --- a/maxscale-system-test/testconnections.cpp +++ b/maxscale-system-test/testconnections.cpp @@ -332,28 +332,44 @@ TestConnections::~TestConnections() } } -void TestConnections::add_result(int result, const char *format, ...) +void TestConnections::report_result(const char *format, va_list argp) { timeval t2; gettimeofday(&t2, NULL); double elapsedTime = (t2.tv_sec - start_time.tv_sec); elapsedTime += (double) (t2.tv_usec - start_time.tv_usec) / 1000000.0; - if (result != 0) + global_result += 1; + + printf("%04f: TEST_FAILED! ", elapsedTime); + + vprintf(format, argp); + + if (format[strlen(format) - 1] != '\n') { - global_result += result; - - printf("%04f: TEST_FAILED! ", elapsedTime); + printf("\n"); + } +} +void TestConnections::add_result(bool result, const char *format, ...) +{ + if (result) + { va_list argp; va_start(argp, format); - vprintf(format, argp); + report_result(format, argp); va_end(argp); + } +} - if (format[strlen(format) - 1] != '\n') - { - printf("\n"); - } +void TestConnections::assert(bool result, const char *format, ...) +{ + if (!result) + { + va_list argp; + va_start(argp, format); + report_result(format, argp); + va_end(argp); } } @@ -1695,6 +1711,47 @@ int TestConnections::try_query_all(int m, const char *sql) try_query(maxscales->conn_slave[m], sql); } +StringSet TestConnections::get_server_status(const char* name) +{ + std::set rval; + int rc; + char* res = maxscales->ssh_node_output_f(0, true, &rc, "maxadmin list servers|grep \'%s\'", name); + char* pipe = strrchr(res, '|'); + + if (res && pipe) + { + pipe++; + char* tok = strtok(pipe, ","); + + while (tok) + { + char* p = tok; + char *end = strchr(tok, '\n'); + if (!end) + end = strchr(tok, '\0'); + + // Trim leading whitespace + while (p < end && isspace(*p)) + { + p++; + } + + // Trim trailing whitespace + while (end > tok && isspace(*end)) + { + *end-- = '\0'; + } + + rval.insert(p); + tok = strtok(NULL, ",\n"); + } + + free(res); + } + + return rval; +} + int TestConnections::list_dirs(int m) { for (int i = 0; i < repl->N; i++) diff --git a/maxscale-system-test/testconnections.h b/maxscale-system-test/testconnections.h index 6ac25658a..a886a3e85 100644 --- a/maxscale-system-test/testconnections.h +++ b/maxscale-system-test/testconnections.h @@ -10,6 +10,7 @@ #include #include +typedef std::set StringSet; /** * @brief Class contains references to Master/Slave and Galera test setups @@ -223,7 +224,10 @@ public: * @param result 0 if step PASSED * @param format ... message to pring if result is not 0 */ - void add_result(int result, const char *format, ...); + void add_result(bool result, const char *format, ...); + + /** Same as add_result() but inverted */ + void assert(bool result, const char *format, ...); /** * @brief ReadEnv Reads all Maxscale and Master/Slave and Galera setups info from environmental variables @@ -430,6 +434,15 @@ public: */ int try_query_all(int m, const char *sql); + /** + * @brief Get the set of labels that are assigned to server @c name + * + * @param name The name of the server that must be present in the output `maxadmin list servers` + * + * @return A set of string labels assigned to this server + */ + StringSet get_server_status(const char* name); + /** * @brief check_maxscale_processes Check if number of running Maxscale processes is equal to 'expected' * @param expected expected number of Maxscale processes @@ -478,6 +491,10 @@ public: void check_current_connections(int m, int value); int stop_maxscale(int m); + void process_template(const char *src, const char *dest = "/etc/maxscale.cnf"); + +private: + void report_result(const char *format, va_list argp); }; /** diff --git a/maxscale-system-test/utilities.cmake b/maxscale-system-test/utilities.cmake index c284958c7..e7be837da 100644 --- a/maxscale-system-test/utilities.cmake +++ b/maxscale-system-test/utilities.cmake @@ -91,7 +91,7 @@ add_test_executable_notest(sysbench_example.cpp sysbench_example replication) # Build the MariaDB Connector/C 3.0 -set(CONNECTOR_C_VERSION "3.0" CACHE STRING "The Connector-C version to use") +set(CONNECTOR_C_VERSION "v3.0.2" CACHE STRING "The Connector-C version to use") include(ExternalProject) ExternalProject_Add(connector-c diff --git a/server/core/mysql_utils.cc b/server/core/mysql_utils.cc index 6e0a26bcf..e49ef39b5 100644 --- a/server/core/mysql_utils.cc +++ b/server/core/mysql_utils.cc @@ -172,6 +172,19 @@ MYSQL *mxs_mysql_real_connect(MYSQL *con, SERVER *server, const char *user, cons MY_CHARSET_INFO cs_info; mysql_get_character_set_info(mysql, &cs_info); server->charset = cs_info.number; + + if (listener && mysql_get_ssl_cipher(con) == NULL) + { + if (server->log_warning.ssl_not_enabled) + { + server->log_warning.ssl_not_enabled = false; + MXS_ERROR("An encrypted connection to '%s' could not be created, " + "ensure that TLS is enabled on the target server.", + server->unique_name); + } + // Don't close the connection as it is closed elsewhere, just set to NULL + mysql = NULL; + } } return mysql; diff --git a/server/core/server.cc b/server/core/server.cc index 64823821a..95b17b612 100644 --- a/server/core/server.cc +++ b/server/core/server.cc @@ -149,6 +149,9 @@ SERVER* server_alloc(const char *name, const char *address, unsigned short port, server->last_event = SERVER_UP_EVENT; server->triggered_at = 0; + // Log all warnings once + memset(&server->log_warning, 1, sizeof(server->log_warning)); + spinlock_acquire(&server_spin); server->next = allServers; allServers = server; diff --git a/server/core/service.cc b/server/core/service.cc index 399eb58dc..3385b3c2c 100644 --- a/server/core/service.cc +++ b/server/core/service.cc @@ -1607,6 +1607,7 @@ service_update(SERVICE *service, char *router, char *user, char *auth) */ int service_refresh_users(SERVICE *service) { + ss_dassert(service); int ret = 1; int self = mxs_worker_get_current_id(); ss_dassert(self >= 0); diff --git a/server/core/utils.cc b/server/core/utils.cc index 3d595874f..043d84f43 100644 --- a/server/core/utils.cc +++ b/server/core/utils.cc @@ -588,7 +588,9 @@ strip_escape_chars(char* val) #define BUFFER_GROWTH_RATE 2.0 static pcre2_code* remove_comments_re = NULL; static const PCRE2_SPTR remove_comments_pattern = (PCRE2_SPTR) - "(?:`[^`]*`\\K)|(\\/[*](?!(M?!)).*?[*]\\/)|(?:#.*|--[[:space:]].*)"; + "(?:`[^`]*`\\K)|" + "(\\/[*](?!(M?!)).*?[*]\\/)|" + "([[:space:]](?:#.*|--[[:space:]].*(\\n|\\r\\n)))"; /** * Remove SQL comments from the end of a string diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index c0ca5f5e1..7673fe3e7 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -49,6 +49,7 @@ typedef struct bool detectStaleMaster; /**< Monitor flag for MySQL replication Stale Master detection */ bool detectStaleSlave; /**< Monitor flag for MySQL replication Stale Master detection */ bool multimaster; /**< Detect and handle multi-master topologies */ + bool ignore_external_masters; /**< Ignore masters outside of the monitor configuration */ int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */ int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ diff --git a/server/modules/monitor/mysqlmon/mysql_mon.c b/server/modules/monitor/mysqlmon/mysql_mon.c new file mode 100644 index 000000000..14226c14b --- /dev/null +++ b/server/modules/monitor/mysqlmon/mysql_mon.c @@ -0,0 +1,2109 @@ +/* + * 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: 2019-07-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. + */ + +/** + * @file mysql_mon.c - A MySQL replication cluster monitor + * + * @verbatim + * Revision History + * + * Date Who Description + * 08/07/13 Mark Riddoch Initial implementation + * 11/07/13 Mark Riddoch Addition of code to check replication status + * 25/07/13 Mark Riddoch Addition of decrypt for passwords and diagnostic interface + * 20/05/14 Massimiliano Pinto Addition of support for MariadDB multimaster replication setup. + * New server field version_string is updated. + * 28/05/14 Massimiliano Pinto Added set Id and configuration options (setInverval) + * Parameters are now printed in diagnostics + * 03/06/14 Mark Ridoch Add support for maintenance mode + * 17/06/14 Massimiliano Pinto Addition of getServerByNodeId routine and first implementation for + * depth of replication for nodes. + * 23/06/14 Massimiliano Pinto Added replication consistency after replication tree computation + * 27/06/14 Massimiliano Pinto Added replication pending status in monitored server, storing there + * the status to update in server status field before + * starting the replication consistency check. + * This will also give routers a consistent "status" of all servers + * 28/08/14 Massimiliano Pinto Added detectStaleMaster feature: previous detected master will be used again, even if the replication is stopped. + * This means both IO and SQL threads are not working on slaves. + * This option is not enabled by default. + * 10/11/14 Massimiliano Pinto Addition of setNetworkTimeout for connect, read, write + * 18/11/14 Massimiliano Pinto One server only in configuration becomes master. servers=server1 must + * be present in mysql_mon and in router sections as well. + * 08/05/15 Markus Makela Added launchable scripts + * 17/10/15 Martin Brampton Change DCB callback to hangup + * + * @endverbatim + */ + +#define MXS_MODULE_NAME "mysqlmon" + +#include "../mysqlmon.h" +#include +#include +#include +#include +#include + +/** Column positions for SHOW SLAVE STATUS */ +#define MYSQL55_STATUS_BINLOG_POS 5 +#define MYSQL55_STATUS_BINLOG_NAME 6 +#define MYSQL55_STATUS_IO_RUNNING 10 +#define MYSQL55_STATUS_SQL_RUNNING 11 +#define MYSQL55_STATUS_MASTER_ID 39 + +/** Column positions for SHOW SLAVE STATUS */ +#define MARIA10_STATUS_BINLOG_NAME 7 +#define MARIA10_STATUS_BINLOG_POS 8 +#define MARIA10_STATUS_IO_RUNNING 12 +#define MARIA10_STATUS_SQL_RUNNING 13 +#define MARIA10_STATUS_MASTER_ID 41 + +/** Column positions for SHOW SLAVE HOSTS */ +#define SLAVE_HOSTS_SERVER_ID 0 +#define SLAVE_HOSTS_HOSTNAME 1 +#define SLAVE_HOSTS_PORT 2 + +static void monitorMain(void *); + +static void *startMonitor(MXS_MONITOR *, const MXS_CONFIG_PARAMETER*); +static void stopMonitor(MXS_MONITOR *); +static void diagnostics(DCB *, const MXS_MONITOR *); +static MXS_MONITOR_SERVERS *getServerByNodeId(MXS_MONITOR_SERVERS *, long); +static MXS_MONITOR_SERVERS *getSlaveOfNodeId(MXS_MONITOR_SERVERS *, long); +static MXS_MONITOR_SERVERS *get_replication_tree(MXS_MONITOR *, int); +static void set_master_heartbeat(MYSQL_MONITOR *, MXS_MONITOR_SERVERS *); +static void set_slave_heartbeat(MXS_MONITOR *, MXS_MONITOR_SERVERS *); +static int add_slave_to_master(long *, int, long); +static bool isMySQLEvent(mxs_monitor_event_t event); +void check_maxscale_schema_replication(MXS_MONITOR *monitor); +static bool report_version_err = true; +static const char* hb_table_name = "maxscale_schema.replication_heartbeat"; + +/** + * The module entry point routine. It is this routine that + * must populate the structure that is referred to as the + * "module object", this is a structure with the set of + * external entry points for this module. + * + * @return The module object + */ +MXS_MODULE* MXS_CREATE_MODULE() +{ + MXS_NOTICE("Initialise the MySQL Monitor module."); + + static MXS_MONITOR_OBJECT MyObject = + { + startMonitor, + stopMonitor, + diagnostics + }; + + static MXS_MODULE info = + { + MXS_MODULE_API_MONITOR, + MXS_MODULE_GA, + MXS_MONITOR_VERSION, + "A MySQL Master/Slave replication monitor", + "V1.5.0", + &MyObject, + NULL, /* Process init. */ + NULL, /* Process finish. */ + NULL, /* Thread init. */ + NULL, /* Thread finish. */ + { + {"detect_replication_lag", MXS_MODULE_PARAM_BOOL, "false"}, + {"detect_stale_master", MXS_MODULE_PARAM_BOOL, "true"}, + {"detect_stale_slave", MXS_MODULE_PARAM_BOOL, "true"}, + {"mysql51_replication", MXS_MODULE_PARAM_BOOL, "false"}, + {"multimaster", MXS_MODULE_PARAM_BOOL, "false"}, + {"detect_standalone_master", MXS_MODULE_PARAM_BOOL, "false"}, + {"failcount", MXS_MODULE_PARAM_COUNT, "5"}, + {"allow_cluster_recovery", MXS_MODULE_PARAM_BOOL, "true"}, + {"ignore_external_masters", MXS_MODULE_PARAM_BOOL, "false"}, + { + "script", + MXS_MODULE_PARAM_PATH, + NULL, + MXS_MODULE_OPT_PATH_X_OK + }, + { + "events", + MXS_MODULE_PARAM_ENUM, + MXS_MONITOR_EVENT_DEFAULT_VALUE, + MXS_MODULE_OPT_NONE, + mxs_monitor_event_enum_values + }, + {MXS_END_MODULE_PARAMS} + } + }; + + return &info; +} + +/** + * Monitor specific information about a server + */ +typedef struct mysql_server_info +{ + int server_id; /**< Value of @@server_id */ + int master_id; /**< Master server id from SHOW SLAVE STATUS*/ + int group; /**< Multi-master group where this server + belongs, 0 for servers not in groups */ + bool read_only; /**< Value of @@read_only */ + bool slave_configured; /**< Whether SHOW SLAVE STATUS returned rows */ + bool slave_io; /**< If Slave IO thread is running */ + bool slave_sql; /**< If Slave SQL thread is running */ + uint64_t binlog_pos; /**< Binlog position from SHOW SLAVE STATUS */ + char *binlog_name; /**< Binlog name from SHOW SLAVE STATUS */ +} MYSQL_SERVER_INFO; + +/** Other values are implicitly zero initialized */ +#define MYSQL_SERVER_INFO_INIT {.binlog_name = ""} + +void* info_copy_func(const void *val) +{ + ss_dassert(val); + MYSQL_SERVER_INFO *old_val = (MYSQL_SERVER_INFO*)val; + MYSQL_SERVER_INFO *new_val = MXS_MALLOC(sizeof(MYSQL_SERVER_INFO)); + char *binlog_name = MXS_STRDUP(old_val->binlog_name); + + if (new_val && binlog_name) + { + *new_val = *old_val; + new_val->binlog_name = binlog_name; + } + else + { + MXS_FREE(new_val); + MXS_FREE(binlog_name); + new_val = NULL; + } + + return new_val; +} + +void info_free_func(void *val) +{ + if (val) + { + MYSQL_SERVER_INFO *old_val = (MYSQL_SERVER_INFO*)val; + MXS_FREE(old_val->binlog_name); + MXS_FREE(old_val); + } +} + +/** + * @brief Helper function that initializes the server info hashtable + * + * @param handle MySQL monitor handle + * @param database List of monitored databases + * @return True on success, false if initialization failed. At the moment + * initialization can only fail if memory allocation fails. + */ +bool init_server_info(MYSQL_MONITOR *handle, MXS_MONITOR_SERVERS *database) +{ + MYSQL_SERVER_INFO info = MYSQL_SERVER_INFO_INIT; + bool rval = true; + + while (database) + { + /** Delete any existing structures and replace them with empty ones */ + hashtable_delete(handle->server_info, database->server->unique_name); + + if (!hashtable_add(handle->server_info, database->server->unique_name, &info)) + { + rval = false; + break; + } + + database = database->next; + } + + return rval; +} + +/*lint +e14 */ + +/** + * Start the instance of the monitor, returning a handle on the monitor. + * + * This function creates a thread to execute the actual monitoring. + * + * @param arg The current handle - NULL if first start + * @param opt Configuration parameters + * @return A handle to use when interacting with the monitor + */ +static void * +startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) +{ + MYSQL_MONITOR *handle = (MYSQL_MONITOR*) monitor->handle; + + if (handle) + { + handle->shutdown = 0; + MXS_FREE(handle->script); + } + else + { + handle = (MYSQL_MONITOR *) MXS_MALLOC(sizeof(MYSQL_MONITOR)); + HASHTABLE *server_info = hashtable_alloc(MAX_NUM_SLAVES, hashtable_item_strhash, hashtable_item_strcmp); + + if (handle == NULL || server_info == NULL) + { + MXS_FREE(handle); + hashtable_free(server_info); + return NULL; + } + + hashtable_memory_fns(server_info, hashtable_item_strdup, info_copy_func, + hashtable_item_free, info_free_func); + handle->server_info = server_info; + handle->shutdown = 0; + handle->id = config_get_global_options()->id; + handle->warn_failover = true; + spinlock_init(&handle->lock); + } + + /** This should always be reset to NULL */ + handle->master = NULL; + + handle->detectStaleMaster = config_get_bool(params, "detect_stale_master"); + handle->detectStaleSlave = config_get_bool(params, "detect_stale_slave"); + handle->replicationHeartbeat = config_get_bool(params, "detect_replication_lag"); + handle->multimaster = config_get_bool(params, "multimaster"); + handle->ignore_external_masters = config_get_bool(params, "ignore_external_masters"); + handle->detect_standalone_master = config_get_bool(params, "detect_standalone_master"); + handle->failcount = config_get_integer(params, "failcount"); + handle->allow_cluster_recovery = config_get_bool(params, "allow_cluster_recovery"); + handle->mysql51_replication = config_get_bool(params, "mysql51_replication"); + handle->script = config_copy_string(params, "script"); + handle->events = config_get_enum(params, "events", mxs_monitor_event_enum_values); + + bool error = false; + + if (!check_monitor_permissions(monitor, "SHOW SLAVE STATUS")) + { + MXS_ERROR("Failed to start monitor. See earlier errors for more information."); + error = true; + } + + if (!init_server_info(handle, monitor->databases)) + { + error = true; + } + + if (error) + { + hashtable_free(handle->server_info); + MXS_FREE(handle->script); + MXS_FREE(handle); + handle = NULL; + } + else if (thread_start(&handle->thread, monitorMain, monitor) == NULL) + { + MXS_ERROR("Failed to start monitor thread for monitor '%s'.", monitor->name); + } + + return handle; +} + +/** + * Stop a running monitor + * + * @param arg Handle on thr running monior + */ +static void +stopMonitor(MXS_MONITOR *mon) +{ + MYSQL_MONITOR *handle = (MYSQL_MONITOR *) mon->handle; + + handle->shutdown = 1; + thread_wait(handle->thread); +} + +/** + * Daignostic interface + * + * @param dcb DCB to print diagnostics + * @param arg The monitor handle + */ +static void diagnostics(DCB *dcb, const MXS_MONITOR *mon) +{ + const MYSQL_MONITOR *handle = (const MYSQL_MONITOR *)mon->handle; + + dcb_printf(dcb, "MaxScale MonitorId:\t%lu\n", handle->id); + dcb_printf(dcb, "Replication lag:\t%s\n", (handle->replicationHeartbeat == 1) ? "enabled" : "disabled"); + dcb_printf(dcb, "Detect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled"); + dcb_printf(dcb, "Server information\n\n"); + + for (MXS_MONITOR_SERVERS *db = mon->databases; db; db = db->next) + { + MYSQL_SERVER_INFO *serv_info = hashtable_fetch(handle->server_info, db->server->unique_name); + dcb_printf(dcb, "Server: %s\n", db->server->unique_name); + dcb_printf(dcb, "Server ID: %d\n", serv_info->server_id); + dcb_printf(dcb, "Read only: %s\n", serv_info->read_only ? "ON" : "OFF"); + dcb_printf(dcb, "Slave configured: %s\n", serv_info->slave_configured ? "YES" : "NO"); + dcb_printf(dcb, "Slave IO running: %s\n", serv_info->slave_io ? "YES" : "NO"); + dcb_printf(dcb, "Slave SQL running: %s\n", serv_info->slave_sql ? "YES" : "NO"); + dcb_printf(dcb, "Master ID: %d\n", serv_info->master_id); + dcb_printf(dcb, "Master binlog file: %s\n", serv_info->binlog_name); + dcb_printf(dcb, "Master binlog position: %lu\n", serv_info->binlog_pos); + + if (handle->multimaster) + { + dcb_printf(dcb, "Master group: %d\n", serv_info->group); + } + + dcb_printf(dcb, "\n"); + } +} + +enum mysql_server_version +{ + MYSQL_SERVER_VERSION_100, + MYSQL_SERVER_VERSION_55, + MYSQL_SERVER_VERSION_51 +}; + +static inline void monitor_mysql_db(MXS_MONITOR_SERVERS* database, MYSQL_SERVER_INFO *serv_info, + enum mysql_server_version server_version) +{ + int columns, i_io_thread, i_sql_thread, i_binlog_pos, i_master_id, i_binlog_name; + const char *query; + + if (server_version == MYSQL_SERVER_VERSION_100) + { + columns = 42; + query = "SHOW ALL SLAVES STATUS"; + i_io_thread = MARIA10_STATUS_IO_RUNNING; + i_sql_thread = MARIA10_STATUS_SQL_RUNNING; + i_binlog_name = MARIA10_STATUS_BINLOG_NAME; + i_binlog_pos = MARIA10_STATUS_BINLOG_POS; + i_master_id = MARIA10_STATUS_MASTER_ID; + } + else + { + columns = server_version == MYSQL_SERVER_VERSION_55 ? 40 : 38; + query = "SHOW SLAVE STATUS"; + i_io_thread = MYSQL55_STATUS_IO_RUNNING; + i_sql_thread = MYSQL55_STATUS_SQL_RUNNING; + i_binlog_name = MYSQL55_STATUS_BINLOG_NAME; + i_binlog_pos = MYSQL55_STATUS_BINLOG_POS; + i_master_id = MYSQL55_STATUS_MASTER_ID; + } + + /** Clear old states */ + monitor_clear_pending_status(database, SERVER_SLAVE | SERVER_MASTER | SERVER_RELAY_MASTER | + SERVER_STALE_STATUS | SERVER_SLAVE_OF_EXTERNAL_MASTER); + + MYSQL_RES* result; + + if (mxs_mysql_query(database->con, query) == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + if (mysql_field_count(database->con) < columns) + { + mysql_free_result(result); + MXS_ERROR("\"%s\" returned less than the expected amount of columns. " + "Expected %d columns.", query, columns); + return; + } + + MYSQL_ROW row = mysql_fetch_row(result); + long master_id = -1; + + if (row) + { + serv_info->slave_configured = true; + int nconfigured = 0; + int nrunning = 0; + + do + { + /* get Slave_IO_Running and Slave_SQL_Running values*/ + serv_info->slave_io = strncmp(row[i_io_thread], "Yes", 3) == 0; + serv_info->slave_sql = strncmp(row[i_sql_thread], "Yes", 3) == 0; + + if (serv_info->slave_io && serv_info->slave_sql) + { + if (nrunning == 0) + { + /** Only check binlog name for the first running slave */ + char *binlog_name = MXS_STRDUP(row[i_binlog_name]); + + if (binlog_name) + { + MXS_FREE(serv_info->binlog_name); + serv_info->binlog_name = binlog_name; + serv_info->binlog_pos = atol(row[i_binlog_pos]); + } + } + + nrunning++; + } + + /* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building + * the replication tree, slaves ids will be added to master(s) and we will have at least the + * root master server. + * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' + */ + if (serv_info->slave_io && server_version != MYSQL_SERVER_VERSION_51) + { + /* Get Master_Server_Id */ + master_id = atol(row[i_master_id]); + if (master_id == 0) + { + master_id = -1; + } + } + + nconfigured++; + row = mysql_fetch_row(result); + } + while (row); + + + /* If all configured slaves are running set this node as slave */ + if (nrunning > 0 && nrunning == nconfigured) + { + monitor_set_pending_status(database, SERVER_SLAVE); + } + } + else + { + /** Query returned no rows, replication is not configured */ + serv_info->slave_configured = false; + serv_info->slave_io = false; + serv_info->slave_sql = false; + serv_info->binlog_pos = 0; + serv_info->binlog_name[0] = '\0'; + } + + /** Store master_id of current node. For MySQL 5.1 it will be set at a later point. */ + database->server->master_id = master_id; + serv_info->master_id = master_id; + + mysql_free_result(result); + } + else + { + mon_report_query_error(database); + } +} + +/** + * Build the replication tree for a MySQL 5.1 cluster + * + * This function queries each server with SHOW SLAVE HOSTS to determine which servers + * have slaves replicating from them. + * @param mon Monitor + * @return Lowest server ID master in the monitor + */ +static MXS_MONITOR_SERVERS *build_mysql51_replication_tree(MXS_MONITOR *mon) +{ + MXS_MONITOR_SERVERS* database = mon->databases; + MXS_MONITOR_SERVERS *ptr, *rval = NULL; + int i; + MYSQL_MONITOR *handle = mon->handle; + + while (database) + { + bool ismaster = false; + MYSQL_RES* result; + MYSQL_ROW row; + int nslaves = 0; + if (database->con) + { + if (mxs_mysql_query(database->con, "SHOW SLAVE HOSTS") == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + if (mysql_field_count(database->con) < 4) + { + mysql_free_result(result); + MXS_ERROR("\"SHOW SLAVE HOSTS\" " + "returned less than the expected amount of columns. " + "Expected 4 columns."); + return NULL; + } + + if (mysql_num_rows(result) > 0) + { + ismaster = true; + while (nslaves < MAX_NUM_SLAVES && (row = mysql_fetch_row(result))) + { + /* get Slave_IO_Running and Slave_SQL_Running values*/ + database->server->slaves[nslaves] = atol(row[SLAVE_HOSTS_SERVER_ID]); + nslaves++; + MXS_DEBUG("Found slave at %s:%s", row[SLAVE_HOSTS_HOSTNAME], row[SLAVE_HOSTS_PORT]); + } + database->server->slaves[nslaves] = 0; + } + + mysql_free_result(result); + } + else + { + mon_report_query_error(database); + } + + /* Set the Slave Role */ + if (ismaster) + { + handle->master = database; + + MXS_DEBUG("Master server found at [%s]:%d with %d slaves", + database->server->name, + database->server->port, + nslaves); + + monitor_set_pending_status(database, SERVER_MASTER); + database->server->depth = 0; // Add Depth 0 for Master + + if (rval == NULL || rval->server->node_id > database->server->node_id) + { + rval = database; + } + } + } + database = database->next; + } + + database = mon->databases; + + /** Set master server IDs */ + while (database) + { + ptr = mon->databases; + + while (ptr) + { + for (i = 0; ptr->server->slaves[i]; i++) + { + if (ptr->server->slaves[i] == database->server->node_id) + { + database->server->master_id = ptr->server->node_id; + database->server->depth = 1; // Add Depth 1 for Slave + break; + } + } + ptr = ptr->next; + } + if (SERVER_IS_SLAVE(database->server) && + (database->server->master_id <= 0 || + database->server->master_id != handle->master->server->node_id)) + { + monitor_set_pending_status(database, SERVER_SLAVE); + monitor_set_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); + } + database = database->next; + } + return rval; +} + +/** + * Monitor an individual server + * + * @param handle The MySQL Monitor object + * @param database The database to probe + */ +static void +monitorDatabase(MXS_MONITOR *mon, MXS_MONITOR_SERVERS *database) +{ + MYSQL_MONITOR* handle = mon->handle; + MYSQL_ROW row; + MYSQL_RES *result; + unsigned long int server_version = 0; + char *server_string; + + /* Don't probe servers in maintenance mode */ + if (SERVER_IN_MAINT(database->server)) + { + return; + } + + /** Store previous status */ + database->mon_prev_status = database->server->status; + + if (database->con == NULL || mysql_ping(database->con) != 0) + { + mxs_connect_result_t rval; + if ((rval = mon_connect_to_db(mon, database)) == MONITOR_CONN_OK) + { + server_clear_status_nolock(database->server, SERVER_AUTH_ERROR); + monitor_clear_pending_status(database, SERVER_AUTH_ERROR); + } + else + { + /* The current server is not running + * + * Store server NOT running in server and monitor server pending struct + * + */ + if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR) + { + server_set_status_nolock(database->server, SERVER_AUTH_ERROR); + monitor_set_pending_status(database, SERVER_AUTH_ERROR); + } + server_clear_status_nolock(database->server, SERVER_RUNNING); + monitor_clear_pending_status(database, SERVER_RUNNING); + + /* Also clear M/S state in both server and monitor server pending struct */ + server_clear_status_nolock(database->server, SERVER_SLAVE); + server_clear_status_nolock(database->server, SERVER_MASTER); + server_clear_status_nolock(database->server, SERVER_RELAY_MASTER); + monitor_clear_pending_status(database, SERVER_SLAVE); + monitor_clear_pending_status(database, SERVER_MASTER); + monitor_clear_pending_status(database, SERVER_RELAY_MASTER); + + /* Clean addition status too */ + server_clear_status_nolock(database->server, SERVER_SLAVE_OF_EXTERNAL_MASTER); + server_clear_status_nolock(database->server, SERVER_STALE_STATUS); + server_clear_status_nolock(database->server, SERVER_STALE_SLAVE); + monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); + monitor_clear_pending_status(database, SERVER_STALE_STATUS); + monitor_clear_pending_status(database, SERVER_STALE_SLAVE); + + /* Log connect failure only once */ + if (mon_status_changed(database) && mon_print_fail_status(database)) + { + mon_log_connect_error(database, rval); + } + + return; + } + } + /* Store current status in both server and monitor server pending struct */ + server_set_status_nolock(database->server, SERVER_RUNNING); + monitor_set_pending_status(database, SERVER_RUNNING); + + /* get server version from current server */ + server_version = mysql_get_server_version(database->con); + + /* get server version string */ + server_string = (char *) mysql_get_server_info(database->con); + if (server_string) + { + server_set_version_string(database->server, server_string); + } + + MYSQL_SERVER_INFO *serv_info = hashtable_fetch(handle->server_info, database->server->unique_name); + ss_dassert(serv_info); + + /* Get server_id and read_only from current node */ + if (mxs_mysql_query(database->con, "SELECT @@server_id, @@read_only") == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + long server_id = -1; + + if (mysql_field_count(database->con) != 2) + { + mysql_free_result(result); + MXS_ERROR("Unexpected result for 'SELECT @@server_id, @@read_only'. Expected 2 columns." + " MySQL Version: %s", server_string); + return; + } + + while ((row = mysql_fetch_row(result))) + { + server_id = strtol(row[0], NULL, 10); + if ((errno == ERANGE && (server_id == LONG_MAX + || server_id == LONG_MIN)) || (errno != 0 && server_id == 0)) + { + server_id = -1; + } + + database->server->node_id = server_id; + serv_info->server_id = server_id; + serv_info->read_only = (row[1] && strcmp(row[1], "1") == 0); + } + mysql_free_result(result); + } + else + { + mon_report_query_error(database); + } + + /* Check first for MariaDB 10.x.x and get status for multi-master replication */ + if (server_version >= 100000) + { + monitor_mysql_db(database, serv_info, MYSQL_SERVER_VERSION_100); + } + else if (server_version >= 5 * 10000 + 5 * 100) + { + monitor_mysql_db(database, serv_info, MYSQL_SERVER_VERSION_55); + } + else + { + if (handle->mysql51_replication) + { + monitor_mysql_db(database, serv_info, MYSQL_SERVER_VERSION_51); + } + else if (report_version_err) + { + report_version_err = false; + MXS_ERROR("MySQL version is lower than 5.5 and 'mysql51_replication' option is " + "not enabled, replication tree cannot be resolved. To enable MySQL 5.1 replication " + "detection, add 'mysql51_replication=true' to the monitor section."); + } + } + +} + +/** + * @brief A node in a graph + */ +struct graph_node +{ + int index; + int lowest_index; + int cycle; + bool active; + struct graph_node *parent; + MYSQL_SERVER_INFO *info; + MXS_MONITOR_SERVERS *db; +}; + +/** + * @brief Visit a node in the graph + * + * This function is the main function used to determine whether the node is a + * part of a cycle. It is an implementation of the Tarjan's strongly connected + * component algorithm. All one node cycles are ignored since normal + * master-slave monitoring handles that. + * + * Tarjan's strongly connected component algorithm: + * + * https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm + */ +static void visit_node(struct graph_node *node, struct graph_node **stack, + int *stacksize, int *index, int *cycle) +{ + /** Assign an index to this node */ + node->lowest_index = node->index = *index; + node->active = true; + *index += 1; + + stack[*stacksize] = node; + *stacksize += 1; + + if (node->parent == NULL) + { + /** This node does not connect to another node, it can't be a part of a cycle */ + node->lowest_index = -1; + } + else if (node->parent->index == 0) + { + /** Node has not been visited */ + visit_node(node->parent, stack, stacksize, index, cycle); + + if (node->parent->lowest_index < node->lowest_index) + { + /** The parent connects to a node with a lower index, this node + could be a part of a cycle. */ + node->lowest_index = node->parent->lowest_index; + } + } + else if (node->parent->active) + { + /** This node could be a root node of the cycle */ + if (node->parent->index < node->lowest_index) + { + /** Root node found */ + node->lowest_index = node->parent->index; + } + } + else + { + /** Node connects to an already connected cycle, it can't be a part of it */ + node->lowest_index = -1; + } + + if (node->active && node->parent && node->lowest_index > 0) + { + if (node->lowest_index == node->index && + node->lowest_index == node->parent->lowest_index) + { + /** + * Found a multi-node cycle from the graph. The cycle is formed from the + * nodes with a lowest_index value equal to the lowest_index value of the + * current node. Rest of the nodes on the stack are not part of a cycle + * and can be discarded. + */ + + *cycle += 1; + + while (*stacksize > 0) + { + struct graph_node *top = stack[(*stacksize) - 1]; + top->active = false; + + if (top->lowest_index == node->lowest_index) + { + top->cycle = *cycle; + } + *stacksize -= 1; + } + } + } + else + { + /** Pop invalid nodes off the stack */ + node->active = false; + if (*stacksize > 0) + { + *stacksize -= 1; + } + } +} + +/** + * @brief Find the strongly connected components in the replication tree graph + * + * Each replication cluster is a directed graph made out of replication + * trees. If this graph has strongly connected components (more generally + * cycles), it is considered a multi-master cluster due to the fact that there + * are multiple nodes where the data can originate. + * + * Detecting the cycles in the graph allows this monitor to better understand + * the relationships between the nodes. All nodes that are a part of a cycle can + * be labeled as master nodes. This information will later be used to choose the + * right master where the writes should go. + * + * This function also populates the MYSQL_SERVER_INFO structures group + * member. Nodes in a group get a positive group ID where the nodes not in a + * group get a group ID of 0. + */ +void find_graph_cycles(MYSQL_MONITOR *handle, MXS_MONITOR_SERVERS *database, int nservers) +{ + struct graph_node graph[nservers]; + struct graph_node *stack[nservers]; + int nodes = 0; + + for (MXS_MONITOR_SERVERS *db = database; db; db = db->next) + { + graph[nodes].info = hashtable_fetch(handle->server_info, db->server->unique_name); + graph[nodes].db = db; + ss_dassert(graph[nodes].info); + graph[nodes].index = graph[nodes].lowest_index = 0; + graph[nodes].cycle = 0; + graph[nodes].active = false; + graph[nodes].parent = NULL; + nodes++; + } + + /** Build the graph */ + for (int i = 0; i < nservers; i++) + { + if (graph[i].info->master_id > 0) + { + /** Found a connected node */ + for (int k = 0; k < nservers; k++) + { + if (graph[k].info->server_id == graph[i].info->master_id) + { + graph[i].parent = &graph[k]; + break; + } + } + } + } + + int index = 1; + int cycle = 0; + int stacksize = 0; + + for (int i = 0; i < nservers; i++) + { + if (graph[i].index == 0) + { + /** Index is 0, this node has not yet been visited */ + visit_node(&graph[i], stack, &stacksize, &index, &cycle); + } + } + + for (int i = 0; i < nservers; i++) + { + graph[i].info->group = graph[i].cycle; + + if (graph[i].cycle > 0) + { + /** We have at least one cycle in the graph */ + if (graph[i].info->read_only) + { + monitor_set_pending_status(graph[i].db, SERVER_SLAVE); + monitor_clear_pending_status(graph[i].db, SERVER_MASTER); + } + else + { + monitor_set_pending_status(graph[i].db, SERVER_MASTER); + monitor_clear_pending_status(graph[i].db, SERVER_SLAVE); + } + } + else if (handle->detectStaleMaster && cycle == 0 && + graph[i].db->server->status & SERVER_MASTER && + (graph[i].db->pending_status & SERVER_MASTER) == 0) + { + /** + * Stale master detection is handled here for multi-master mode. + * + * If we know that no cycles were found from the graph and that a + * server once had the master status, replication has broken + * down. These masters are assigned the stale master status allowing + * them to be used as masters even if they lose their slaves. A + * slave in this case can be either a normal slave or another + * master. + */ + if (graph[i].info->read_only) + { + /** The master is in read-only mode, set it into Slave state */ + monitor_set_pending_status(graph[i].db, SERVER_SLAVE); + monitor_clear_pending_status(graph[i].db, SERVER_MASTER | SERVER_STALE_STATUS); + } + else + { + monitor_set_pending_status(graph[i].db, SERVER_MASTER | SERVER_STALE_STATUS); + monitor_clear_pending_status(graph[i].db, SERVER_SLAVE); + } + } + } +} + +/** + * @brief Check whether failover conditions have been met + * + * This function checks whether all the conditions to trigger a failover have + * been met. For a failover to happen, only one server must be available and + * other servers must have passed the configured tolerance level of failures. + * + * @param handle Monitor instance + * @param db Monitor servers + * + * @return True if failover is required + */ +bool failover_required(MYSQL_MONITOR *handle, MXS_MONITOR_SERVERS *db) +{ + int candidates = 0; + + while (db) + { + if (SERVER_IS_RUNNING(db->server)) + { + candidates++; + MYSQL_SERVER_INFO *server_info = hashtable_fetch(handle->server_info, db->server->unique_name); + + if (server_info->read_only || server_info->slave_configured || candidates > 1) + { + return false; + } + } + else if (db->mon_err_count < handle->failcount) + { + return false; + } + + db = db->next; + } + + return candidates == 1; +} + +/** + * @brief Initiate simple failover + * + * This function does the actual failover by assigning the last remaining server + * the master status and setting all other servers into maintenance mode. By + * setting the servers into maintenance mode, we prevent any possible conflicts + * when the failed servers come back up. + * + * @param handle Monitor instance + * @param db Monitor servers + */ +void do_failover(MYSQL_MONITOR *handle, MXS_MONITOR_SERVERS *db) +{ + while (db) + { + if (SERVER_IS_RUNNING(db->server)) + { + if (!SERVER_IS_MASTER(db->server) && handle->warn_failover) + { + MXS_WARNING("Failover initiated, server '%s' is now the master.%s", + db->server->unique_name, + handle->allow_cluster_recovery ? + "" : " All other servers are set into maintenance mode."); + handle->warn_failover = false; + } + + server_clear_set_status(db->server, SERVER_SLAVE, SERVER_MASTER | SERVER_STALE_STATUS); + monitor_set_pending_status(db, SERVER_MASTER | SERVER_STALE_STATUS); + monitor_clear_pending_status(db, SERVER_SLAVE); + handle->master = db; + } + else if (!handle->allow_cluster_recovery) + { + server_set_status_nolock(db->server, SERVER_MAINT); + monitor_set_pending_status(db, SERVER_MAINT); + } + db = db->next; + } +} + +/** + * The entry point for the monitoring module thread + * + * @param arg The handle of the monitor + */ +static void +monitorMain(void *arg) +{ + MXS_MONITOR* mon = (MXS_MONITOR*) arg; + MYSQL_MONITOR *handle; + MXS_MONITOR_SERVERS *ptr; + int replication_heartbeat; + bool detect_stale_master; + int num_servers = 0; + MXS_MONITOR_SERVERS *root_master = NULL; + size_t nrounds = 0; + int log_no_master = 1; + bool heartbeat_checked = false; + + spinlock_acquire(&mon->lock); + handle = (MYSQL_MONITOR *) mon->handle; + spinlock_release(&mon->lock); + replication_heartbeat = handle->replicationHeartbeat; + detect_stale_master = handle->detectStaleMaster; + + if (mysql_thread_init()) + { + MXS_ERROR("mysql_thread_init failed in monitor module. Exiting."); + return; + } + handle->status = MXS_MONITOR_RUNNING; + + while (1) + { + if (handle->shutdown) + { + handle->status = MXS_MONITOR_STOPPING; + mysql_thread_end(); + handle->status = MXS_MONITOR_STOPPED; + return; + } + /** Wait base interval */ + thread_millisleep(MXS_MON_BASE_INTERVAL_MS); + + if (handle->replicationHeartbeat && !heartbeat_checked) + { + check_maxscale_schema_replication(mon); + heartbeat_checked = true; + } + + /** + * Calculate how far away the monitor interval is from its full + * cycle and if monitor interval time further than the base + * interval, then skip monitoring checks. Excluding the first + * round. + */ + if (nrounds != 0 && + (((nrounds * MXS_MON_BASE_INTERVAL_MS) % mon->interval) >= + MXS_MON_BASE_INTERVAL_MS) && (!mon->server_pending_changes)) + { + nrounds += 1; + continue; + } + nrounds += 1; + /* reset num_servers */ + num_servers = 0; + + lock_monitor_servers(mon); + servers_status_pending_to_current(mon); + + /* start from the first server in the list */ + ptr = mon->databases; + + while (ptr) + { + ptr->mon_prev_status = ptr->server->status; + + /* copy server status into monitor pending_status */ + ptr->pending_status = ptr->server->status; + + /* monitor current node */ + monitorDatabase(mon, ptr); + + /* reset the slave list of current node */ + memset(&ptr->server->slaves, 0, sizeof(ptr->server->slaves)); + + num_servers++; + + if (mon_status_changed(ptr)) + { + if (SRV_MASTER_STATUS(ptr->mon_prev_status)) + { + /** Master failed, can't recover */ + MXS_NOTICE("Server [%s]:%d lost the master status.", + ptr->server->name, + ptr->server->port); + } + } + + if (mon_status_changed(ptr)) + { +#if defined(SS_DEBUG) + MXS_INFO("Backend server [%s]:%d state : %s", + ptr->server->name, + ptr->server->port, + STRSRVSTATUS(ptr->server)); +#else + MXS_DEBUG("Backend server [%s]:%d state : %s", + ptr->server->name, + ptr->server->port, + STRSRVSTATUS(ptr->server)); +#endif + } + + if (SERVER_IS_DOWN(ptr->server)) + { + /** Increase this server'e error count */ + ptr->mon_err_count += 1; + } + else + { + /** Reset this server's error count */ + ptr->mon_err_count = 0; + } + + ptr = ptr->next; + } + + ptr = mon->databases; + /* if only one server is configured, that's is Master */ + if (num_servers == 1) + { + if (SERVER_IS_RUNNING(ptr->server)) + { + ptr->server->depth = 0; + /* status cleanup */ + monitor_clear_pending_status(ptr, SERVER_SLAVE); + + /* master status set */ + monitor_set_pending_status(ptr, SERVER_MASTER); + + ptr->server->depth = 0; + handle->master = ptr; + root_master = ptr; + } + } + else + { + /* Compute the replication tree */ + if (handle->mysql51_replication) + { + root_master = build_mysql51_replication_tree(mon); + } + else + { + root_master = get_replication_tree(mon, num_servers); + } + + } + + if (handle->multimaster && num_servers > 0) + { + /** Find all the master server cycles in the cluster graph. If + multiple masters are found, the servers with the read_only + variable set to ON will be assigned the slave status. */ + find_graph_cycles(handle, mon->databases, num_servers); + } + + ptr = mon->databases; + while (ptr) + { + MYSQL_SERVER_INFO *serv_info = hashtable_fetch(handle->server_info, ptr->server->unique_name); + ss_dassert(serv_info); + + if (ptr->server->node_id > 0 && ptr->server->master_id > 0 && + getSlaveOfNodeId(mon->databases, ptr->server->node_id) && + getServerByNodeId(mon->databases, ptr->server->master_id) && + (!handle->multimaster || serv_info->group == 0)) + { + /** This server is both a slave and a master i.e. a relay master */ + monitor_set_pending_status(ptr, SERVER_RELAY_MASTER); + monitor_clear_pending_status(ptr, SERVER_MASTER); + } + ptr = ptr->next; + } + + /* Update server status from monitor pending status on that server*/ + + ptr = mon->databases; + while (ptr) + { + if (!SERVER_IN_MAINT(ptr->server)) + { + MYSQL_SERVER_INFO *serv_info = hashtable_fetch(handle->server_info, ptr->server->unique_name); + ss_dassert(serv_info); + + /** If "detect_stale_master" option is On, let's use the previous master. + * + * Multi-master mode detects the stale masters in find_graph_cycles(). + */ + if (detect_stale_master && root_master && !handle->multimaster && + (strcmp(ptr->server->name, root_master->server->name) == 0 && + ptr->server->port == root_master->server->port) && + (ptr->server->status & SERVER_MASTER) && + !(ptr->pending_status & SERVER_MASTER) && + !serv_info->read_only) + { + /** + * In this case server->status will not be updated from pending_status + * Set the STALE bit for this server in server struct + */ + server_set_status_nolock(ptr->server, SERVER_STALE_STATUS | SERVER_MASTER); + ptr->pending_status |= SERVER_STALE_STATUS | SERVER_MASTER; + + /** Log the message only if the master server didn't have + * the stale master bit set */ + if ((ptr->mon_prev_status & SERVER_STALE_STATUS) == 0) + { + MXS_WARNING("All slave servers under the current master " + "server have been lost. Assigning Stale Master" + " status to the old master server '%s' (%s:%i).", + ptr->server->unique_name, ptr->server->name, + ptr->server->port); + } + } + + if (handle->detectStaleSlave) + { + int bits = SERVER_SLAVE | SERVER_RUNNING; + + if ((ptr->mon_prev_status & bits) == bits && + root_master && SERVER_IS_MASTER(root_master->server)) + { + /** Slave with a running master, assign stale slave candidacy */ + if ((ptr->pending_status & bits) == bits) + { + ptr->pending_status |= SERVER_STALE_SLAVE; + } + /** Server lost slave when a master is available, remove + * stale slave candidacy */ + else if ((ptr->pending_status & bits) == SERVER_RUNNING) + { + ptr->pending_status &= ~SERVER_STALE_SLAVE; + } + } + /** If this server was a stale slave candidate, assign + * slave status to it */ + else if (ptr->mon_prev_status & SERVER_STALE_SLAVE && + ptr->pending_status & SERVER_RUNNING && + // Master is down + (!root_master || !SERVER_IS_MASTER(root_master->server) || + // Master just came up + (SERVER_IS_MASTER(root_master->server) && + (root_master->mon_prev_status & SERVER_MASTER) == 0))) + { + ptr->pending_status |= SERVER_SLAVE; + } + else if (root_master == NULL && serv_info->slave_configured) + { + ptr->pending_status |= SERVER_SLAVE; + } + } + + ptr->server->status = ptr->pending_status; + } + ptr = ptr->next; + } + + /** Now that all servers have their status correctly set, we can check + if we need to do a failover */ + if (handle->detect_standalone_master) + { + if (failover_required(handle, mon->databases)) + { + /** Other servers have died, initiate a failover to the last remaining server */ + do_failover(handle, mon->databases); + } + else + { + handle->warn_failover = true; + } + } + + /** + * Clear external slave status from master if configured to do so. + * This allows parts of a multi-tiered replication setup to be used + * in MaxScale. + */ + if (root_master && SERVER_IS_SLAVE_OF_EXTERNAL_MASTER(root_master->server) && + SERVER_IS_MASTER(root_master->server) && handle->ignore_external_masters) + { + monitor_clear_pending_status(root_master, + SERVER_SLAVE | SERVER_SLAVE_OF_EXTERNAL_MASTER); + server_clear_status_nolock(root_master->server, SERVER_SLAVE | SERVER_SLAVE_OF_EXTERNAL_MASTER); + } + + /** + * After updating the status of all servers, check if monitor events + * need to be launched. + */ + mon_process_state_changes(mon, handle->script, handle->events); + + /* log master detection failure of first master becomes available after failure */ + if (root_master && + mon_status_changed(root_master) && + !(root_master->server->status & SERVER_STALE_STATUS)) + { + if (root_master->pending_status & (SERVER_MASTER) && SERVER_IS_RUNNING(root_master->server)) + { + if (!(root_master->mon_prev_status & SERVER_STALE_STATUS) && + !(root_master->server->status & SERVER_MAINT)) + { + MXS_NOTICE("A Master Server is now available: %s:%i", + root_master->server->name, + root_master->server->port); + } + } + else + { + MXS_ERROR("No Master can be determined. Last known was %s:%i", + root_master->server->name, + root_master->server->port); + } + log_no_master = 1; + } + else + { + if (!root_master && log_no_master) + { + MXS_ERROR("No Master can be determined"); + log_no_master = 0; + } + } + + /* Do now the heartbeat replication set/get for MySQL Replication Consistency */ + if (replication_heartbeat && + root_master && + (SERVER_IS_MASTER(root_master->server) || + SERVER_IS_RELAY_SERVER(root_master->server))) + { + set_master_heartbeat(handle, root_master); + ptr = mon->databases; + + while (ptr) + { + if ((!SERVER_IN_MAINT(ptr->server)) && SERVER_IS_RUNNING(ptr->server)) + { + if (ptr->server->node_id != root_master->server->node_id && + (SERVER_IS_SLAVE(ptr->server) || + SERVER_IS_RELAY_SERVER(ptr->server))) + { + set_slave_heartbeat(mon, ptr); + } + } + ptr = ptr->next; + } + } + + mon_hangup_failed_servers(mon); + servers_status_current_to_pending(mon); + release_monitor_servers(mon); + } /*< while (1) */ +} + +/** + * Fetch a MySQL node by node_id + * + * @param ptr The list of servers to monitor + * @param node_id The MySQL server_id to fetch + * @return The server with the required server_id + */ +static MXS_MONITOR_SERVERS * +getServerByNodeId(MXS_MONITOR_SERVERS *ptr, long node_id) +{ + SERVER *current; + while (ptr) + { + current = ptr->server; + if (current->node_id == node_id) + { + return ptr; + } + ptr = ptr->next; + } + return NULL; +} + +/** + * Fetch a MySQL slave node from a node_id + * + * @param ptr The list of servers to monitor + * @param node_id The MySQL server_id to fetch + * @return The slave server of this node_id + */ +static MXS_MONITOR_SERVERS * +getSlaveOfNodeId(MXS_MONITOR_SERVERS *ptr, long node_id) +{ + SERVER *current; + while (ptr) + { + current = ptr->server; + if (current->master_id == node_id) + { + return ptr; + } + ptr = ptr->next; + } + return NULL; +} + +/******* + * This function sets the replication heartbeat + * into the maxscale_schema.replication_heartbeat table in the current master. + * The inserted values will be seen from all slaves replication from this master. + * + * @param handle The monitor handle + * @param database The number database server + */ +static void set_master_heartbeat(MYSQL_MONITOR *handle, MXS_MONITOR_SERVERS *database) +{ + unsigned long id = handle->id; + time_t heartbeat; + time_t purge_time; + char heartbeat_insert_query[512] = ""; + char heartbeat_purge_query[512] = ""; + MYSQL_RES *result; + long returned_rows; + + if (handle->master == NULL) + { + MXS_ERROR("set_master_heartbeat called without an available Master server"); + return; + } + + /* check if the maxscale_schema database and replication_heartbeat table exist */ + if (mxs_mysql_query(database->con, "SELECT table_name FROM information_schema.tables " + "WHERE table_schema = 'maxscale_schema' AND table_name = 'replication_heartbeat'")) + { + MXS_ERROR( "Error checking for replication_heartbeat in Master server" + ": %s", mysql_error(database->con)); + database->server->rlag = MAX_RLAG_NOT_AVAILABLE; + } + + result = mysql_store_result(database->con); + + if (result == NULL) + { + returned_rows = 0; + } + else + { + returned_rows = mysql_num_rows(result); + mysql_free_result(result); + } + + if (0 == returned_rows) + { + /* create repl_heartbeat table in maxscale_schema database */ + if (mxs_mysql_query(database->con, "CREATE TABLE IF NOT EXISTS " + "maxscale_schema.replication_heartbeat " + "(maxscale_id INT NOT NULL, " + "master_server_id INT NOT NULL, " + "master_timestamp INT UNSIGNED NOT NULL, " + "PRIMARY KEY ( master_server_id, maxscale_id ) ) " + "ENGINE=MYISAM DEFAULT CHARSET=latin1")) + { + MXS_ERROR("Error creating maxscale_schema.replication_heartbeat " + "table in Master server: %s", mysql_error(database->con)); + + database->server->rlag = MAX_RLAG_NOT_AVAILABLE; + } + } + + /* auto purge old values after 48 hours*/ + purge_time = time(0) - (3600 * 48); + + sprintf(heartbeat_purge_query, + "DELETE FROM maxscale_schema.replication_heartbeat WHERE master_timestamp < %lu", purge_time); + + if (mxs_mysql_query(database->con, heartbeat_purge_query)) + { + MXS_ERROR("Error deleting from maxscale_schema.replication_heartbeat " + "table: [%s], %s", + heartbeat_purge_query, + mysql_error(database->con)); + } + + heartbeat = time(0); + + /* set node_ts for master as time(0) */ + database->server->node_ts = heartbeat; + + sprintf(heartbeat_insert_query, + "UPDATE maxscale_schema.replication_heartbeat SET master_timestamp = %lu WHERE master_server_id = %li AND maxscale_id = %lu", + heartbeat, handle->master->server->node_id, id); + + /* Try to insert MaxScale timestamp into master */ + if (mxs_mysql_query(database->con, heartbeat_insert_query)) + { + + database->server->rlag = MAX_RLAG_NOT_AVAILABLE; + + MXS_ERROR("Error updating maxscale_schema.replication_heartbeat table: [%s], %s", + heartbeat_insert_query, + mysql_error(database->con)); + } + else + { + if (mysql_affected_rows(database->con) == 0) + { + heartbeat = time(0); + sprintf(heartbeat_insert_query, + "REPLACE INTO maxscale_schema.replication_heartbeat (master_server_id, maxscale_id, master_timestamp ) VALUES ( %li, %lu, %lu)", + handle->master->server->node_id, id, heartbeat); + + if (mxs_mysql_query(database->con, heartbeat_insert_query)) + { + + database->server->rlag = MAX_RLAG_NOT_AVAILABLE; + + MXS_ERROR("Error inserting into " + "maxscale_schema.replication_heartbeat table: [%s], %s", + heartbeat_insert_query, + mysql_error(database->con)); + } + else + { + /* Set replication lag to 0 for the master */ + database->server->rlag = 0; + + MXS_DEBUG("heartbeat table inserted data for %s:%i", + database->server->name, database->server->port); + } + } + else + { + /* Set replication lag as 0 for the master */ + database->server->rlag = 0; + + MXS_DEBUG("heartbeat table updated for Master %s:%i", + database->server->name, database->server->port); + } + } +} + +/******* + * This function gets the replication heartbeat + * from the maxscale_schema.replication_heartbeat table in the current slave + * and stores the timestamp and replication lag in the slave server struct + * + * @param handle The monitor handle + * @param database The number database server + */ +static void set_slave_heartbeat(MXS_MONITOR* mon, MXS_MONITOR_SERVERS *database) +{ + MYSQL_MONITOR *handle = (MYSQL_MONITOR*) mon->handle; + unsigned long id = handle->id; + time_t heartbeat; + char select_heartbeat_query[256] = ""; + MYSQL_ROW row; + MYSQL_RES *result; + + if (handle->master == NULL) + { + MXS_ERROR("set_slave_heartbeat called without an available Master server"); + return; + } + + /* Get the master_timestamp value from maxscale_schema.replication_heartbeat table */ + + sprintf(select_heartbeat_query, "SELECT master_timestamp " + "FROM maxscale_schema.replication_heartbeat " + "WHERE maxscale_id = %lu AND master_server_id = %li", + id, handle->master->server->node_id); + + /* if there is a master then send the query to the slave with master_id */ + if (handle->master != NULL && (mxs_mysql_query(database->con, select_heartbeat_query) == 0 + && (result = mysql_store_result(database->con)) != NULL)) + { + int rows_found = 0; + + while ((row = mysql_fetch_row(result))) + { + int rlag = MAX_RLAG_NOT_AVAILABLE; + time_t slave_read; + + rows_found = 1; + + heartbeat = time(0); + slave_read = strtoul(row[0], NULL, 10); + + if ((errno == ERANGE && (slave_read == LONG_MAX || slave_read == LONG_MIN)) || (errno != 0 && + slave_read == 0)) + { + slave_read = 0; + } + + if (slave_read) + { + /* set the replication lag */ + rlag = heartbeat - slave_read; + } + + /* set this node_ts as master_timestamp read from replication_heartbeat table */ + database->server->node_ts = slave_read; + + if (rlag >= 0) + { + /* store rlag only if greater than monitor sampling interval */ + database->server->rlag = ((unsigned int)rlag > (mon->interval / 1000)) ? rlag : 0; + } + else + { + database->server->rlag = MAX_RLAG_NOT_AVAILABLE; + } + + MXS_DEBUG("Slave %s:%i has %i seconds lag", + database->server->name, + database->server->port, + database->server->rlag); + } + if (!rows_found) + { + database->server->rlag = MAX_RLAG_NOT_AVAILABLE; + database->server->node_ts = 0; + } + + mysql_free_result(result); + } + else + { + database->server->rlag = MAX_RLAG_NOT_AVAILABLE; + database->server->node_ts = 0; + + if (handle->master->server->node_id < 0) + { + MXS_ERROR("error: replication heartbeat: " + "master_server_id NOT available for %s:%i", + database->server->name, + database->server->port); + } + else + { + MXS_ERROR("error: replication heartbeat: " + "failed selecting from hearthbeat table of %s:%i : [%s], %s", + database->server->name, + database->server->port, + select_heartbeat_query, + mysql_error(database->con)); + } + } +} + +/******* + * This function computes the replication tree + * from a set of MySQL Master/Slave monitored servers + * and returns the root server with SERVER_MASTER bit. + * The tree is computed even for servers in 'maintenance' mode. + * + * @param handle The monitor handle + * @param num_servers The number of servers monitored + * @return The server at root level with SERVER_MASTER bit + */ + +static MXS_MONITOR_SERVERS *get_replication_tree(MXS_MONITOR *mon, int num_servers) +{ + MYSQL_MONITOR* handle = (MYSQL_MONITOR*) mon->handle; + MXS_MONITOR_SERVERS *ptr; + MXS_MONITOR_SERVERS *backend; + SERVER *current; + int depth = 0; + long node_id; + int root_level; + + ptr = mon->databases; + root_level = num_servers; + + while (ptr) + { + /* The server could be in SERVER_IN_MAINT + * that means SERVER_IS_RUNNING returns 0 + * Let's check only for SERVER_IS_DOWN: server is not running + */ + if (SERVER_IS_DOWN(ptr->server)) + { + ptr = ptr->next; + continue; + } + depth = 0; + current = ptr->server; + + node_id = current->master_id; + if (node_id < 1) + { + MXS_MONITOR_SERVERS *find_slave; + find_slave = getSlaveOfNodeId(mon->databases, current->node_id); + + if (find_slave == NULL) + { + current->depth = -1; + ptr = ptr->next; + + continue; + } + else + { + current->depth = 0; + } + } + else + { + depth++; + } + + while (depth <= num_servers) + { + /* set the root master at lowest depth level */ + if (current->depth > -1 && current->depth < root_level) + { + root_level = current->depth; + handle->master = ptr; + } + backend = getServerByNodeId(mon->databases, node_id); + + if (backend) + { + node_id = backend->server->master_id; + } + else + { + node_id = -1; + } + + if (node_id > 0) + { + current->depth = depth + 1; + depth++; + + } + else + { + MXS_MONITOR_SERVERS *master; + current->depth = depth; + + master = getServerByNodeId(mon->databases, current->master_id); + if (master && master->server && master->server->node_id > 0) + { + add_slave_to_master(master->server->slaves, sizeof(master->server->slaves), + current->node_id); + master->server->depth = current->depth - 1; + + if (handle->master && master->server->depth < handle->master->server->depth) + { + /** A master with a lower depth was found, remove + the master status from the previous master. */ + monitor_clear_pending_status(handle->master, SERVER_MASTER); + } + + MYSQL_SERVER_INFO* info = hashtable_fetch(handle->server_info, + master->server->unique_name); + ss_dassert(info); + + if (SERVER_IS_RUNNING(master->server)) + { + /** Only set the Master status if read_only is disabled */ + monitor_set_pending_status(master, info->read_only ? SERVER_SLAVE : SERVER_MASTER); + } + + handle->master = master; + } + else + { + if (current->master_id > 0) + { + /* this server is slave of another server not in MaxScale configuration + * we cannot use it as a real slave. + */ + monitor_set_pending_status(ptr, SERVER_SLAVE); + monitor_set_pending_status(ptr, SERVER_SLAVE_OF_EXTERNAL_MASTER); + } + } + break; + } + + } + + ptr = ptr->next; + } + + /* + * Return the root master + */ + + if (handle->master != NULL) + { + /* If the root master is in MAINT, return NULL */ + if (SERVER_IN_MAINT(handle->master->server)) + { + return NULL; + } + else + { + return handle->master; + } + } + else + { + return NULL; + } +} + +/******* + * This function add a slave id into the slaves server field + * of its master server + * + * @param slaves_list The slave list array of the master server + * @param list_size The size of the slave list + * @param node_id The node_id of the slave to be inserted + * @return 1 for inserted value and 0 otherwise + */ +static int add_slave_to_master(long *slaves_list, int list_size, long node_id) +{ + for (int i = 0; i < list_size; i++) + { + if (slaves_list[i] == 0) + { + slaves_list[i] = node_id; + return 1; + } + } + return 0; +} + +/** + * Check if replicate_ignore_table is defined and if maxscale_schema.replication_hearbeat + * table is in the list. + * @param database Server to check + * @return False if the table is not replicated or an error occurred when querying + * the server + */ +bool check_replicate_ignore_table(MXS_MONITOR_SERVERS* database) +{ + MYSQL_RES *result; + bool rval = true; + + if (mxs_mysql_query(database->con, + "show variables like 'replicate_ignore_table'") == 0 && + (result = mysql_store_result(database->con)) && + mysql_num_fields(result) > 1) + { + MYSQL_ROW row; + + while ((row = mysql_fetch_row(result))) + { + if (strlen(row[1]) > 0 && + strcasestr(row[1], hb_table_name)) + { + MXS_WARNING("'replicate_ignore_table' is " + "defined on server '%s' and '%s' was found in it. ", + database->server->unique_name, hb_table_name); + rval = false; + } + } + + mysql_free_result(result); + } + else + { + MXS_ERROR("Failed to query server %s for " + "'replicate_ignore_table': %s", + database->server->unique_name, + mysql_error(database->con)); + rval = false; + } + return rval; +} + +/** + * Check if replicate_do_table is defined and if maxscale_schema.replication_hearbeat + * table is not in the list. + * @param database Server to check + * @return False if the table is not replicated or an error occurred when querying + * the server + */ +bool check_replicate_do_table(MXS_MONITOR_SERVERS* database) +{ + MYSQL_RES *result; + bool rval = true; + + if (mxs_mysql_query(database->con, + "show variables like 'replicate_do_table'") == 0 && + (result = mysql_store_result(database->con)) && + mysql_num_fields(result) > 1) + { + MYSQL_ROW row; + + while ((row = mysql_fetch_row(result))) + { + if (strlen(row[1]) > 0 && + strcasestr(row[1], hb_table_name) == NULL) + { + MXS_WARNING("'replicate_do_table' is " + "defined on server '%s' and '%s' was not found in it. ", + database->server->unique_name, hb_table_name); + rval = false; + } + } + mysql_free_result(result); + } + else + { + MXS_ERROR("Failed to query server %s for " + "'replicate_do_table': %s", + database->server->unique_name, + mysql_error(database->con)); + rval = false; + } + return rval; +} + +/** + * Check if replicate_wild_do_table is defined and if it doesn't match + * maxscale_schema.replication_heartbeat. + * @param database Database server + * @return False if the table is not replicated or an error occurred when trying to + * query the server. + */ +bool check_replicate_wild_do_table(MXS_MONITOR_SERVERS* database) +{ + MYSQL_RES *result; + bool rval = true; + + if (mxs_mysql_query(database->con, + "show variables like 'replicate_wild_do_table'") == 0 && + (result = mysql_store_result(database->con)) && + mysql_num_fields(result) > 1) + { + MYSQL_ROW row; + + while ((row = mysql_fetch_row(result))) + { + if (strlen(row[1]) > 0) + { + mxs_pcre2_result_t rc = modutil_mysql_wildcard_match(row[1], hb_table_name); + if (rc == MXS_PCRE2_NOMATCH) + { + MXS_WARNING("'replicate_wild_do_table' is " + "defined on server '%s' and '%s' does not match it. ", + database->server->unique_name, + hb_table_name); + rval = false; + } + } + } + mysql_free_result(result); + } + else + { + MXS_ERROR("Failed to query server %s for " + "'replicate_wild_do_table': %s", + database->server->unique_name, + mysql_error(database->con)); + rval = false; + } + return rval; +} + +/** + * Check if replicate_wild_ignore_table is defined and if it matches + * maxscale_schema.replication_heartbeat. + * @param database Database server + * @return False if the table is not replicated or an error occurred when trying to + * query the server. + */ +bool check_replicate_wild_ignore_table(MXS_MONITOR_SERVERS* database) +{ + MYSQL_RES *result; + bool rval = true; + + if (mxs_mysql_query(database->con, + "show variables like 'replicate_wild_ignore_table'") == 0 && + (result = mysql_store_result(database->con)) && + mysql_num_fields(result) > 1) + { + MYSQL_ROW row; + + while ((row = mysql_fetch_row(result))) + { + if (strlen(row[1]) > 0) + { + mxs_pcre2_result_t rc = modutil_mysql_wildcard_match(row[1], hb_table_name); + if (rc == MXS_PCRE2_MATCH) + { + MXS_WARNING("'replicate_wild_ignore_table' is " + "defined on server '%s' and '%s' matches it. ", + database->server->unique_name, + hb_table_name); + rval = false; + } + } + } + mysql_free_result(result); + } + else + { + MXS_ERROR("Failed to query server %s for " + "'replicate_wild_do_table': %s", + database->server->unique_name, + mysql_error(database->con)); + rval = false; + } + return rval; +} + +/** + * Check if the maxscale_schema.replication_heartbeat table is replicated on all + * servers and log a warning if problems were found. + * @param monitor Monitor structure + */ +void check_maxscale_schema_replication(MXS_MONITOR *monitor) +{ + MXS_MONITOR_SERVERS* database = monitor->databases; + bool err = false; + + while (database) + { + mxs_connect_result_t rval = mon_connect_to_db(monitor, database); + if (rval == MONITOR_CONN_OK) + { + if (!check_replicate_ignore_table(database) || + !check_replicate_do_table(database) || + !check_replicate_wild_do_table(database) || + !check_replicate_wild_ignore_table(database)) + { + err = true; + } + } + else + { + mon_log_connect_error(database, rval); + } + database = database->next; + } + + if (err) + { + MXS_WARNING("Problems were encountered when checking if '%s' is replicated. Make sure that " + "the table is replicated to all slaves.", hb_table_name); + } +} diff --git a/server/modules/protocol/MySQL/MySQLBackend/mysql_backend.c b/server/modules/protocol/MySQL/MySQLBackend/mysql_backend.c index faa779e1f..fbb2251d2 100644 --- a/server/modules/protocol/MySQL/MySQLBackend/mysql_backend.c +++ b/server/modules/protocol/MySQL/MySQLBackend/mysql_backend.c @@ -301,7 +301,7 @@ bool is_error_response(GWBUF *buffer) * @param dcb Backend DCB where authentication failed * @param buffer Buffer containing the response from the backend */ -void log_error_response(DCB *dcb, GWBUF *buffer) +static void handle_error_response(DCB *dcb, GWBUF *buffer) { uint8_t *data = (uint8_t*)GWBUF_DATA(buffer); size_t len = MYSQL_GET_PAYLOAD_LEN(data); @@ -326,6 +326,16 @@ void log_error_response(DCB *dcb, GWBUF *buffer) server_set_status(dcb->server, SERVER_MAINT); } + else if (errcode == ER_ACCESS_DENIED_ERROR || + errcode == ER_DBACCESS_DENIED_ERROR || + errcode == ER_ACCESS_DENIED_NO_PASSWORD_ERROR) + { + if (dcb->session->state != SESSION_STATE_DUMMY) + { + // Authentication failed, reload users + service_refresh_users(dcb->service); + } + } } /** @@ -494,7 +504,7 @@ gw_read_backend_event(DCB *dcb) { /** The server responded with an error */ proto->protocol_auth_state = MXS_AUTH_STATE_FAILED; - log_error_response(dcb, readbuf); + handle_error_response(dcb, readbuf); } if (proto->protocol_auth_state == MXS_AUTH_STATE_CONNECTED) @@ -887,7 +897,7 @@ gw_read_and_write(DCB *dcb) { /** The COM_CHANGE USER failed, generate a fake hangup event to * close the DCB and send an error to the client. */ - log_error_response(dcb, reply); + handle_error_response(dcb, reply); } else { diff --git a/server/modules/protocol/MySQL/MySQLClient/mysql_client.cc b/server/modules/protocol/MySQL/MySQLClient/mysql_client.cc index 879a9d6d7..4ee98a481 100644 --- a/server/modules/protocol/MySQL/MySQLClient/mysql_client.cc +++ b/server/modules/protocol/MySQL/MySQLClient/mysql_client.cc @@ -71,7 +71,6 @@ static void mysql_client_auth_error_handling(DCB *dcb, int auth_val, int packet_ static int gw_read_do_authentication(DCB *dcb, GWBUF *read_buffer, int nbytes_read); static int gw_read_normal_data(DCB *dcb, GWBUF *read_buffer, int nbytes_read); static int gw_read_finish_processing(DCB *dcb, GWBUF *read_buffer, uint64_t capabilities); -static bool ensure_complete_packet(DCB *dcb, GWBUF **read_buffer, int nbytes_read); static void gw_process_one_new_client(DCB *client_dcb); static spec_com_res_t process_special_commands(DCB *client_dcb, GWBUF *read_buffer, int nbytes_read); static spec_com_res_t handle_query_kill(DCB* dcb, GWBUF* read_buffer, spec_com_res_t current, @@ -816,51 +815,31 @@ static bool process_client_commands(DCB* dcb, int bytes_available, GWBUF** buffe int pktlen; uint8_t cmd = (uint8_t)MXS_COM_QUERY; // Treat empty packets as COM_QUERY - /** - * Buffer has at least 5 bytes, the packet is in contiguous memory - * and it's the first packet in the buffer. - */ - if (offset == 0 && GWBUF_LENGTH(queue) >= MYSQL_HEADER_LEN + 1) + uint8_t packet_header[MYSQL_HEADER_LEN]; + + if (gwbuf_copy_data(queue, offset, MYSQL_HEADER_LEN, packet_header) != MYSQL_HEADER_LEN) { - uint8_t *data = (uint8_t*)GWBUF_DATA(queue); - pktlen = gw_mysql_get_byte3(data); - if (pktlen) - { - cmd = *(data + MYSQL_HEADER_LEN); - } + ss_dassert(offset > 0); + queue = split_and_store(dcb, queue, offset); + break; } + + pktlen = gw_mysql_get_byte3(packet_header); + /** - * We have more than one packet in the buffer or the first 5 bytes - * of a packet are split across two buffers. + * Check if the packet is empty, and if not, if we have the command byte. + * If we an empty packet or have at least 5 bytes of data, we can start + * sending the data to the router. */ - else + if (pktlen && gwbuf_copy_data(queue, offset + MYSQL_HEADER_LEN, 1, &cmd) != 1) { - uint8_t packet_header[MYSQL_HEADER_LEN]; - - if (gwbuf_copy_data(queue, offset, MYSQL_HEADER_LEN, packet_header) != MYSQL_HEADER_LEN) + if ((queue = split_and_store(dcb, queue, offset)) == NULL) { - ss_dassert(offset > 0); - queue = split_and_store(dcb, queue, offset); - break; - } - - pktlen = gw_mysql_get_byte3(packet_header); - - /** - * Check if the packet is empty, and if not, if we have the command byte. - * If we an empty packet or have at least 5 bytes of data, we can start - * sending the data to the router. - */ - if (pktlen && gwbuf_copy_data(queue, MYSQL_HEADER_LEN, 1, &cmd) != 1) - { - if ((queue = split_and_store(dcb, queue, offset)) == NULL) - { - ss_dassert(bytes_available == MYSQL_HEADER_LEN); - return false; - } - ss_dassert(offset > 0); - break; + ss_dassert(bytes_available - offset == MYSQL_HEADER_LEN); + return false; } + ss_dassert(offset > 0); + break; } MySQLProtocol *proto = (MySQLProtocol*)dcb->protocol; @@ -978,25 +957,28 @@ gw_read_normal_data(DCB *dcb, GWBUF *read_buffer, int nbytes_read) /** Ask what type of input the router/filter chain expects */ capabilities = service_get_capabilities(session->service); - /** Update the current protocol command being executed */ - if (!process_client_commands(dcb, nbytes_read, &read_buffer)) - { - return 0; - } - - /** If the router requires statement input or we are still authenticating - * we need to make sure that a complete SQL packet is read before continuing */ + /** If the router requires statement input we need to make sure that + * a complete SQL packet is read before continuing. The current command + * that is tracked by the protocol module is updated in route_by_statement() */ if (rcap_type_required(capabilities, RCAP_TYPE_STMT_INPUT)) { - if (nbytes_read < 3 || nbytes_read < - (int)(MYSQL_GET_PAYLOAD_LEN((uint8_t *) GWBUF_DATA(read_buffer)) + 4)) + uint8_t pktlen[MYSQL_HEADER_LEN]; + size_t n_copied = gwbuf_copy_data(read_buffer, 0, MYSQL_HEADER_LEN, pktlen); + + if (n_copied != sizeof(pktlen) || + (uint32_t)nbytes_read < MYSQL_GET_PAYLOAD_LEN(pktlen) + MYSQL_HEADER_LEN) { - dcb_readq_set(dcb, read_buffer); + dcb_readq_append(dcb, read_buffer); return 0; } set_qc_mode(session, &read_buffer); } + /** Update the current protocol command being executed */ + else if (!process_client_commands(dcb, nbytes_read, &read_buffer)) + { + return 0; + } /** The query classifier classifies according to the service's server that has * the smallest version number. */ @@ -1027,6 +1009,30 @@ gw_read_normal_data(DCB *dcb, GWBUF *read_buffer, int nbytes_read) return rval; } +/** + * Check if a connection qualifies to be added into the persistent connection pool + * + * @param dcb The client DCB to check + */ +void check_pool_candidate(DCB* dcb) +{ + MXS_SESSION *session = dcb->session; + MySQLProtocol *proto = (MySQLProtocol*)dcb->protocol; + + if (proto->current_command == MXS_COM_QUIT) + { + /** The client is closing the connection. We know that this will be the + * last command the client sends so the backend connections are very likely + * to be in an idle state. + * + * If the client is pipelining the queries (i.e. sending N request as + * a batch and then expecting N responses) then it is possible that + * the backend connections are not idle when the COM_QUIT is received. + * In most cases we can assume that the connections are idle. */ + session_qualify_for_pool(session); + } +} + /** * @brief Client read event, common processing after single statement handling * @@ -1047,25 +1053,10 @@ gw_read_finish_processing(DCB *dcb, GWBUF *read_buffer, uint64_t capabilities) /** Reset error handler when routing of the new query begins */ dcb->dcb_errhandle_called = false; - if (proto->current_command == MXS_COM_QUIT) - { - /** The client is closing the connection. We know that this will be the - * last command the client sends so the backend connections are very likely - * to be in an idle state. - * - * If the client is pipelining the queries (i.e. sending N request as - * a batch and then expecting N responses) then it is possible that - * the backend connections are not idle when the COM_QUIT is received. - * In most cases we can assume that the connections are idle. */ - session_qualify_for_pool(session); - } - if (rcap_type_required(capabilities, RCAP_TYPE_STMT_INPUT)) { /** - * Feed each statement completely and separately - * to router. The routing functions return 1 for - * success or 0 for failure. + * Feed each statement completely and separately to router. */ return_code = route_by_statement(session, capabilities, &read_buffer) ? 0 : 1; @@ -1080,9 +1071,10 @@ gw_read_finish_processing(DCB *dcb, GWBUF *read_buffer, uint64_t capabilities) } else if (NULL != session->router_session || (rcap_type_required(capabilities, RCAP_TYPE_NO_RSESSION))) { - /** Feed whole packet to router, which will free it - * and return 1 for success, 0 for failure - */ + /** Check if this connection qualifies for the connection pool */ + check_pool_candidate(dcb); + + /** Feed the whole buffer to the router */ return_code = MXS_SESSION_ROUTE_QUERY(session, read_buffer) ? 0 : 1; } /* else return_code is still 0 from when it was originally set */ @@ -1424,13 +1416,41 @@ static int gw_client_hangup_event(DCB *dcb) goto retblock; } - modutil_send_mysql_err_packet(dcb, 0, 0, 1927, "08S01", "Connection killed by MaxScale"); + if (!session_valid_for_pool(session)) + { + // The client did not send a COM_QUIT packet + modutil_send_mysql_err_packet(dcb, 0, 0, 1927, "08S01", "Connection killed by MaxScale"); + } dcb_close(dcb); retblock: return 1; } +/** + * Update protocol tracking information for an individual statement + * + * @param dcb Client DCB + * @param buffer Buffer containing a single packet + */ +void update_current_command(DCB* dcb, GWBUF* buffer) +{ + MySQLProtocol *proto = (MySQLProtocol*)dcb->protocol; + uint8_t cmd = (uint8_t)MXS_COM_QUERY; + + /** + * As we are routing individual packets, we can extract the command byte here. + * Empty packets are treated as COM_QUERY packets by default. + */ + gwbuf_copy_data(buffer, MYSQL_HEADER_LEN, 1, &cmd); + proto->current_command = (mxs_mysql_cmd_t)cmd; + + /** + * Now that we have the current command, we can check if this connection + * can be a candidate for the connection pool. + */ + check_pool_candidate(dcb); +} /** * Detect if buffer includes partial mysql packet or multiple packets. @@ -1464,21 +1484,11 @@ static int route_by_statement(MXS_SESSION* session, uint64_t capabilities, GWBUF { CHK_GWBUF(packetbuf); - MySQLProtocol* proto = (MySQLProtocol*)session->client_dcb->protocol; - proto->current_command = (mxs_mysql_cmd_t)mxs_mysql_get_command(packetbuf); - /** - * This means that buffer includes exactly one MySQL - * statement. - * backend func.write uses the information. MySQL backend - * protocol, for example, stores the command identifier - * to protocol structure. When some other thread reads - * the corresponding response the command tells how to - * handle response. - * - * Set it here instead of gw_read_client_event to make - * sure it is set to each (MySQL) packet. + * Update the currently command being executed. */ + update_current_command(session->client_dcb, packetbuf); + if (rcap_type_required(capabilities, RCAP_TYPE_CONTIGUOUS_INPUT)) { if (!GWBUF_IS_CONTIGUOUS(packetbuf)) @@ -1567,52 +1577,6 @@ return_rc: return rc; } -/** - * if read queue existed appent read to it. if length of read buffer is less - * than 3 or less than mysql packet then return. else copy mysql packets to - * separate buffers from read buffer and continue. else if read queue didn't - * exist, length of read is less than 3 or less than mysql packet then - * create read queue and append to it and return. if length read is less than - * mysql packet length append to read queue append to it and return. - * else (complete packet was read) continue. - * - * @return True if we have a complete packet, otherwise false - */ -static bool ensure_complete_packet(DCB *dcb, GWBUF **read_buffer, int nbytes_read) -{ - if (dcb_readq_has(dcb)) - { - dcb_readq_append(dcb, *read_buffer); - nbytes_read = dcb_readq_length(dcb); - int plen = MYSQL_GET_PAYLOAD_LEN((uint8_t *) GWBUF_DATA(dcb_readq_get(dcb))); - - if (nbytes_read < 3 || nbytes_read < plen + 4) - { - return false; - } - else - { - /** - * There is at least one complete mysql packet in - * read_buffer. - */ - *read_buffer = dcb_readq_release(dcb); - } - } - else - { - uint8_t* data = (uint8_t *) GWBUF_DATA(*read_buffer); - - if (nbytes_read < 3 || nbytes_read < (int)MYSQL_GET_PAYLOAD_LEN(data) + 4) - { - dcb_readq_append(dcb, *read_buffer); - return false; - } - } - - return true; -} - /** * Some SQL commands/queries need to be detected and handled by the protocol * and MaxScale instead of being routed forward as is. diff --git a/server/modules/routing/avrorouter/avro_file.c b/server/modules/routing/avrorouter/avro_file.c index 68307054a..f497b0ac1 100644 --- a/server/modules/routing/avrorouter/avro_file.c +++ b/server/modules/routing/avrorouter/avro_file.c @@ -657,6 +657,13 @@ avro_binlog_end_t avro_read_all_events(AVRO_INSTANCE *router) snprintf(next_file, sizeof(next_file), BINLOG_NAMEFMT, router->fileroot, blr_file_get_next_binlogname(router->binlog_name)); } + else if (hdr.event_type == MARIADB_ANNOTATE_ROWS_EVENT) + { + MXS_INFO("Annotate_rows_event: %.*s", hdr.event_size - BINLOG_EVENT_HDR_LEN, ptr); + pos += original_size; + router->current_pos = pos; + continue; + } else if (hdr.event_type == TABLE_MAP_EVENT) { handle_table_map_event(router, &hdr, ptr); @@ -956,6 +963,8 @@ bool save_and_replace_table_create(AVRO_INSTANCE *router, TABLE_CREATE *created) { if (strcmp(key, table_ident) == 0) { + TABLE_MAP* map = hashtable_fetch(router->table_maps, key); + router->active_maps[map->id % MAX_MAPPED_TABLES] = NULL; hashtable_delete(router->table_maps, key); } } @@ -1000,13 +1009,13 @@ void handle_query_event(AVRO_INSTANCE *router, REP_HEADER *hdr, int *pending_tra memcpy(db, (char*) ptr + PHDR_OFF + vblklen, dblen); db[dblen] = 0; - unify_whitespace(sql, len); size_t sqlsz = len, tmpsz = len; char *tmp = MXS_MALLOC(len); MXS_ABORT_IF_NULL(tmp); remove_mysql_comments((const char**)&sql, &sqlsz, &tmp, &tmpsz); sql = tmp; len = tmpsz; + unify_whitespace(sql, len); if (is_create_table_statement(router, sql, len)) { diff --git a/server/modules/routing/avrorouter/avro_rbr.c b/server/modules/routing/avrorouter/avro_rbr.c index d34820023..89f0e4182 100644 --- a/server/modules/routing/avrorouter/avro_rbr.c +++ b/server/modules/routing/avrorouter/avro_rbr.c @@ -104,72 +104,53 @@ bool handle_table_map_event(AVRO_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr { ss_dassert(create->columns > 0); TABLE_MAP *old = hashtable_fetch(router->table_maps, table_ident); + TABLE_MAP *map = table_map_alloc(ptr, ev_len, create); + MXS_ABORT_IF_NULL(map); // Fatal error at this point + char* json_schema = json_new_schema_from_table(map); - if (old == NULL || old->version != create->version) + if (json_schema) { - TABLE_MAP *map = table_map_alloc(ptr, ev_len, create); + char filepath[PATH_MAX + 1]; + snprintf(filepath, sizeof(filepath), "%s/%s.%06d.avro", + router->avrodir, table_ident, map->version); - if (map) + /** Close the file and open a new one */ + hashtable_delete(router->open_tables, table_ident); + AVRO_TABLE *avro_table = avro_table_alloc(filepath, json_schema, + codec_to_string(router->codec), + router->block_size); + + if (avro_table) { - char* json_schema = json_new_schema_from_table(map); + bool notify = old != NULL; - if (json_schema) + if (old) { - char filepath[PATH_MAX + 1]; - snprintf(filepath, sizeof(filepath), "%s/%s.%06d.avro", - router->avrodir, table_ident, map->version); - - /** Close the file and open a new one */ - hashtable_delete(router->open_tables, table_ident); - AVRO_TABLE *avro_table = avro_table_alloc(filepath, json_schema, - codec_to_string(router->codec), - router->block_size); - - if (avro_table) - { - bool notify = old != NULL; - - if (old) - { router->active_maps[old->id % MAX_MAPPED_TABLES] = NULL; } hashtable_delete(router->table_maps, table_ident); - hashtable_add(router->table_maps, (void*) table_ident, map); - hashtable_add(router->open_tables, table_ident, avro_table); - save_avro_schema(router->avrodir, json_schema, map); - router->active_maps[map->id % MAX_MAPPED_TABLES] = map; - MXS_DEBUG("Table %s mapped to %lu", table_ident, map->id); - rval = true; + hashtable_add(router->table_maps, (void*)table_ident, map); + hashtable_add(router->open_tables, table_ident, avro_table); + save_avro_schema(router->avrodir, json_schema, map); + router->active_maps[map->id % MAX_MAPPED_TABLES] = map; + ss_dassert(router->active_maps[id % MAX_MAPPED_TABLES] == map); + MXS_DEBUG("Table %s mapped to %lu", table_ident, map->id); + rval = true; - if (notify) - { - notify_all_clients(router); - } - } - else - { - MXS_ERROR("Failed to open new Avro file for writing."); - } - MXS_FREE(json_schema); - } - else + if (notify) { - MXS_ERROR("Failed to create JSON schema."); + notify_all_clients(router); } } else { - MXS_ERROR("Failed to allocate new table map."); + MXS_ERROR("Failed to open new Avro file for writing."); } + MXS_FREE(json_schema); } else { - router->active_maps[old->id % MAX_MAPPED_TABLES] = NULL; - table_map_remap(ptr, ev_len, old); - router->active_maps[old->id % MAX_MAPPED_TABLES] = old; - MXS_DEBUG("Table %s re-mapped to %lu", table_ident, old->id); - /** No changes in the schema */ - rval = true; + MXS_ERROR("Failed to create JSON schema."); } } else @@ -363,8 +344,9 @@ bool handle_row_event(AVRO_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr) } else { - MXS_ERROR("Row event and table map event have different column counts." - " Only full row image is currently supported."); + MXS_ERROR("Row event and table map event have different column " + "counts for table %s.%s, only full row image is currently " + "supported.", map->database, map->table); } } else @@ -606,7 +588,6 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value } MXS_INFO("[%ld] CHAR: field: %d bytes, data: %d bytes", i, field_length, bytes); - ss_dassert(bytes || *ptr == '\0'); char str[bytes + 1]; memcpy(str, ptr, bytes); str[bytes] = '\0'; diff --git a/server/modules/routing/avrorouter/avro_schema.c b/server/modules/routing/avrorouter/avro_schema.c index e68dd3434..95c6f5534 100644 --- a/server/modules/routing/avrorouter/avro_schema.c +++ b/server/modules/routing/avrorouter/avro_schema.c @@ -321,11 +321,11 @@ void save_avro_schema(const char *path, const char* schema, TABLE_MAP *map) * @return Pointer to the start of the definition of NULL if the query is * malformed. */ -static const char* get_table_definition(const char *sql, int* size) +static const char* get_table_definition(const char *sql, int len, int* size) { const char *rval = NULL; const char *ptr = sql; - const char *end = strchr(sql, '\0'); + const char *end = sql + len; while (ptr < end && *ptr != '(') { ptr++; @@ -403,10 +403,12 @@ static bool get_table_name(const char* sql, char* dest) /** * Extract the database name from a CREATE TABLE statement + * * @param sql SQL statement * @param dest Destination where the database name is extracted. Must be at least - * MYSQL_DATABASE_MAXLEN bytes long. - * @return True if extraction was successful + * MYSQL_DATABASE_MAXLEN bytes long. + * + * @return True if a database name was extracted */ static bool get_database_name(const char* sql, char* dest) { @@ -426,22 +428,27 @@ static bool get_database_name(const char* sql, char* dest) ptr--; } - while (*ptr == '`' || *ptr == '.' || isspace(*ptr)) + if (*ptr == '.') { - ptr--; + // The query defines an explicit database + + while (*ptr == '`' || *ptr == '.' || isspace(*ptr)) + { + ptr--; + } + + const char* end = ptr + 1; + + while (*ptr != '`' && *ptr != '.' && !isspace(*ptr)) + { + ptr--; + } + + ptr++; + memcpy(dest, ptr, end - ptr); + dest[end - ptr] = '\0'; + rval = true; } - - const char* end = ptr + 1; - - while (*ptr != '`' && *ptr != '.' && !isspace(*ptr)) - { - ptr--; - } - - ptr++; - memcpy(dest, ptr, end - ptr); - dest[end - ptr] = '\0'; - rval = true; } return rval; @@ -512,12 +519,16 @@ static const char *extract_field_name(const char* ptr, char* dest, size_t size) } } - if (strncasecmp(ptr, "constraint", 10) == 0 || strncasecmp(ptr, "index", 5) == 0 || - strncasecmp(ptr, "key", 3) == 0 || strncasecmp(ptr, "fulltext", 8) == 0 || - strncasecmp(ptr, "spatial", 7) == 0 || strncasecmp(ptr, "foreign", 7) == 0 || - strncasecmp(ptr, "unique", 6) == 0 || strncasecmp(ptr, "primary", 7) == 0) + if (!bt) { - return NULL; + if (strncasecmp(ptr, "constraint", 10) == 0 || strncasecmp(ptr, "index", 5) == 0 || + strncasecmp(ptr, "key", 3) == 0 || strncasecmp(ptr, "fulltext", 8) == 0 || + strncasecmp(ptr, "spatial", 7) == 0 || strncasecmp(ptr, "foreign", 7) == 0 || + strncasecmp(ptr, "unique", 6) == 0 || strncasecmp(ptr, "primary", 7) == 0) + { + // Found a keyword + return NULL; + } } const char *start = ptr; @@ -694,35 +705,42 @@ TABLE_CREATE* table_create_from_schema(const char* file, const char* db, * @param db Database where this query was executed * @return New CREATE_TABLE object or NULL if an error occurred */ -TABLE_CREATE* table_create_alloc(const char* sql, int len, const char* event_db) +TABLE_CREATE* table_create_alloc(const char* sql, int len, const char* db) { /** Extract the table definition so we can get the column names from it */ int stmt_len = 0; - const char* statement_sql = get_table_definition(sql, &stmt_len); + const char* statement_sql = get_table_definition(sql, len, &stmt_len); ss_dassert(statement_sql); char table[MYSQL_TABLE_MAXLEN + 1]; char database[MYSQL_DATABASE_MAXLEN + 1]; - const char *db = event_db; - + const char* err = NULL; MXS_INFO("Create table: %.*s", len, sql); - if (!get_table_name(sql, table)) + if (!statement_sql) { - MXS_ERROR("Malformed CREATE TABLE statement, could not extract table name: %s", sql); - return NULL; + err = "table definition"; + } + else if (!get_table_name(sql, table)) + { + err = "table name"; } - /** The CREATE statement contains the database name */ - if (strlen(db) == 0) + if (get_database_name(sql, database)) { - if (!get_database_name(sql, database)) - { - MXS_ERROR("Malformed CREATE TABLE statement, could not extract " - "database name: %s", sql); - return NULL; - } + // The CREATE statement contains the database name db = database; } + else if (*db == '\0') + { + // No explicit or current database + err = "database name"; + } + + if (err) + { + MXS_ERROR("Malformed CREATE TABLE statement, could not extract %s: %.*s", err, len, sql); + return NULL; + } int* lengths = NULL; char **names = NULL; @@ -893,6 +911,27 @@ static void remove_extras(char* str) ss_dassert(strlen(str) == len); } + +static void remove_backticks(char* src) +{ + char* dest = src; + + while (*src) + { + if (*src != '`') + { + // Non-backtick character, keep it + *dest = *src; + dest++; + } + + src++; + } + + ss_dassert(dest == src || (*dest != '\0' && dest < src)); + *dest = '\0'; +} + /** * Extract both tables from a `CREATE TABLE t1 LIKE t2` statement */ @@ -1095,10 +1134,12 @@ static bool tok_eq(const char *a, const char *b, size_t len) void read_alter_identifier(const char *sql, const char *end, char *dest, int size) { int len = 0; - const char *tok = get_tok(sql, &len, end); - if (tok && (tok = get_tok(tok + len, &len, end)) && (tok = get_tok(tok + len, &len, end))) + const char *tok = get_tok(sql, &len, end); // ALTER + if (tok && (tok = get_tok(tok + len, &len, end)) // TABLE + && (tok = get_tok(tok + len, &len, end))) // Table identifier { snprintf(dest, size, "%.*s", len, tok); + remove_backticks(dest); } } @@ -1174,20 +1215,33 @@ bool table_create_alter(TABLE_CREATE *create, const char *sql, const char *end) if (tok_eq(ptok, "add", plen) && tok_eq(tok, "column", len)) { tok = get_tok(tok + len, &len, end); - - create->column_names = MXS_REALLOC(create->column_names, sizeof(char*) * (create->columns + 1)); - create->column_types = MXS_REALLOC(create->column_types, sizeof(char*) * (create->columns + 1)); - create->column_lengths = MXS_REALLOC(create->column_lengths, sizeof(int) * (create->columns + 1)); - char avro_token[len + 1]; make_avro_token(avro_token, tok, len); - char field_type[200] = ""; // Enough to hold all types - int field_length = extract_type_length(tok + len, field_type); - create->column_names[create->columns] = MXS_STRDUP_A(avro_token); - create->column_types[create->columns] = MXS_STRDUP_A(field_type); - create->column_lengths[create->columns] = field_length; - create->columns++; - updates++; + bool is_new = true; + + for (uint64_t i = 0; i < create->columns; i++) + { + if (strcmp(avro_token, create->column_names[i]) == 0) + { + is_new = false; + break; + } + } + + if (is_new) + { + create->column_names = MXS_REALLOC(create->column_names, sizeof(char*) * (create->columns + 1)); + create->column_types = MXS_REALLOC(create->column_types, sizeof(char*) * (create->columns + 1)); + create->column_lengths = MXS_REALLOC(create->column_lengths, sizeof(int) * (create->columns + 1)); + + char field_type[200] = ""; // Enough to hold all types + int field_length = extract_type_length(tok + len, field_type); + create->column_names[create->columns] = MXS_STRDUP_A(avro_token); + create->column_types[create->columns] = MXS_STRDUP_A(field_type); + create->column_lengths[create->columns] = field_length; + create->columns++; + updates++; + } tok = get_next_def(tok, end); len = 0; } @@ -1393,20 +1447,3 @@ void table_map_free(TABLE_MAP *map) MXS_FREE(map); } } - -/** - * @brief Map a table to a different ID - * - * This updates the table ID that the @c TABLE_MAP object is assigned with - * - * @param ptr Pointer to the start of a table map event - * @param hdr_len Post-header length - * @param map Table map to remap - */ -void table_map_remap(uint8_t *ptr, uint8_t hdr_len, TABLE_MAP *map) -{ - uint64_t table_id = 0; - size_t id_size = hdr_len == 6 ? 4 : 6; - memcpy(&table_id, ptr, id_size); - map->id = table_id; -} diff --git a/server/modules/routing/avrorouter/avrorouter.h b/server/modules/routing/avrorouter/avrorouter.h index 50e5e2c63..27818068c 100644 --- a/server/modules/routing/avrorouter/avrorouter.h +++ b/server/modules/routing/avrorouter/avrorouter.h @@ -328,7 +328,6 @@ extern char* json_new_schema_from_table(TABLE_MAP *map); extern void save_avro_schema(const char *path, const char* schema, TABLE_MAP *map); extern bool handle_table_map_event(AVRO_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr); extern bool handle_row_event(AVRO_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr); -extern void table_map_remap(uint8_t *ptr, uint8_t hdr_len, TABLE_MAP *map); enum avrorouter_file_op { diff --git a/server/modules/routing/binlogrouter/blr_master.c b/server/modules/routing/binlogrouter/blr_master.c index 04a8ced21..6a57036ea 100644 --- a/server/modules/routing/binlogrouter/blr_master.c +++ b/server/modules/routing/binlogrouter/blr_master.c @@ -229,6 +229,7 @@ static void blr_start_master(void* data) return; } client->session = router->session; + client->service = router->service; /** * 'client' is the fake DCB that emulates a client session: @@ -265,6 +266,7 @@ static void blr_start_master(void* data) return; } router->master->remote = MXS_STRDUP_A(router->service->dbref->server->name); + router->master->service = router->service; MXS_NOTICE("%s: attempting to connect to master" " server [%s]:%d, binlog='%s', pos=%lu%s%s",