From ee069ac45c74889d686fd29680418ba47904f0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Wed, 31 Oct 2018 12:03:25 +0200 Subject: [PATCH] MXS-2131: Add sanity check test The sanity check replaces several old regression tests and provides a quick test for checking mainly the readwritesplit routing behavior. It also checks some of the connection counts and runs queries that once caused a crash. The set of tests that the sanity check obsoletes is: bug422 bug469 bug448 bug507 bug509 bug634 bug694 bug669 bug711 mxs127 mxs47 mxs682_cyrillic mxs957 mxs1786_statistics rwsplit_read_only_trx --- maxscale-system-test/CMakeLists.txt | 3 + maxscale-system-test/mariadb_func.h | 4 +- maxscale-system-test/sanity_check.cpp | 131 ++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 maxscale-system-test/sanity_check.cpp diff --git a/maxscale-system-test/CMakeLists.txt b/maxscale-system-test/CMakeLists.txt index d5db73b69..d03e30535 100644 --- a/maxscale-system-test/CMakeLists.txt +++ b/maxscale-system-test/CMakeLists.txt @@ -61,6 +61,9 @@ add_test_executable(config_test.cpp config_test replication LABELS CONFIG) add_subdirectory(cdc_datatypes) +# Routing sanity check +add_test_executable(sanity_check.cpp sanity_check replication LABELS LIGHT REPL_BACKEND) + # Repeatedly connect to maxscale while the backends reject all connections, expect no crash add_test_executable(backend_auth_fail.cpp backend_auth_fail replication LABELS readconnroute REPL_BACKEND) diff --git a/maxscale-system-test/mariadb_func.h b/maxscale-system-test/mariadb_func.h index 99124a6c9..df9d86bac 100644 --- a/maxscale-system-test/mariadb_func.h +++ b/maxscale-system-test/mariadb_func.h @@ -323,10 +323,10 @@ public: return get_row(m_conn, q); } - std::string field(std::string q) + std::string field(std::string q, int idx = 0) { Row r = get_row(m_conn, q); - return r.empty() ? std::string() : r[0]; + return r.empty() ? std::string() : r[idx]; } const char* error() const diff --git a/maxscale-system-test/sanity_check.cpp b/maxscale-system-test/sanity_check.cpp new file mode 100644 index 000000000..4f17c7ad1 --- /dev/null +++ b/maxscale-system-test/sanity_check.cpp @@ -0,0 +1,131 @@ +/** + * Sanity check for basic functionality + * + * Combines several old regression tests into one quick test. + */ + +#include "testconnections.h" + + +void test_rwsplit(TestConnections& test) +{ + test.set_timeout(300); + test.repl->connect(); + std::string master_id = test.repl->get_server_id_str(0); + test.repl->disconnect(); + + auto c = test.maxscales->rwsplit(); + test.expect(c.connect(), "Connection to readwritesplit should succeed"); + + // Transactions to master + c.query("START TRANSACTION"); + test.expect(c.field("SELECT @@server_id") == master_id, + "START TRANSACTION should go to the master"); + c.query("COMMIT"); + + // Read-only transactions to slave + c.query("START TRANSACTION READ ONLY"); + test.expect(c.field("SELECT @@server_id") != master_id, + "START TRANSACTION READ ONLY should go to a slave"); + c.query("COMMIT"); + + // @@last_insert_id routed to master + test.expect(c.field("SELECT @@server_id, @@last_insert_id") == master_id, + "@@last_insert_id should go to the master"); + test.expect(c.field("SELECT last_insert_id(), @@server_id", 1) == master_id, + "@@last_insert_id should go to the master"); + + // Replication related queries + test.expect(!c.row("SHOW SLAVE STATUS").empty(), + "SHOW SLAVE STATUS should go to a slave"); + + // User variable modification in SELECT + test.expect(!c.query("SELECT @a:=@a+1 as a, user FROM mysql"), + "Query with variable modification should fail"); + + // Repeated session commands + for (int i = 0; i < 10000; i++) + { + test.expect(c.query("set @test=" + std::to_string(i)), "SET should work: %s", c.error()); + } + + // Large result sets + for (int i = 1; i < 5000; i += 7) + { + c.query("SELECT REPEAT('a'," + std::to_string(i) + ")"); + } + + // Non ASCII characters + c.query("CREATE OR REPLACE TABLE test.t1 AS SELECT 'Кот'"); + c.query("BEGIN"); + c.check("SELECT * FROM test.t1", "Кот"); + c.query("COMMIT"); + c.query("DROP TABLE test.t1"); + + // Temporary tables + for (auto a : { + "USE test", + "CREATE OR REPLACE TABLE t1(`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY)", + "CREATE OR REPLACE TABLE t2(`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY)", + "CREATE TEMPORARY TABLE temp1(`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY)", + "INSERT INTO temp1 values (1), (2), (3)", + "INSERT INTO t1 values (1), (2), (3)", + "INSERT INTO t2 values (1), (2), (3)", + "CREATE TEMPORARY TABLE temp2 SELECT DISTINCT p.id FROM temp1 p JOIN t1 t " + " ON (t.id = p.id) LEFT JOIN t2 ON (t.id = t2.id) WHERE p.id IS NOT NULL " + " AND @@server_id IS NOT NULL", + "SELECT * FROM temp2", + "DROP TABLE t1", + "DROP TABLE t2" + }) + { + test.expect(c.query(a), "Temp table query failed"); + } + + // Temporary and real table overlap + c.query("CREATE OR REPLACE TABLE test.t1 AS SELECT 1 AS id"); + c.query("CREATE TEMPORARY TABLE test.t1 AS SELECT 2 AS id"); + c.check("SELECT id FROM test.t1", "2"); + c.query("DROP TABLE test.t1"); + c.query("DROP TABLE test.t1"); + + // COM_STATISTICS + test.maxscales->connect(); + for (int i = 0; i < 10; i++) + { + mysql_stat(test.maxscales->conn_rwsplit[0]); + test.try_query(test.maxscales->conn_rwsplit[0], "SELECT 1"); + } + test.maxscales->disconnect(); +} + +int main(int argc, char** argv) +{ + TestConnections test(argc, argv); + + auto connections = [&]() { + return test.maxctrl("api get servers/server1 data.attributes.statistics.connections").second; + }; + + test.expect(connections()[0] == '0', "The master should have no connections"); + test.maxscales->connect(); + test.expect(connections()[0] == '2', "The master should have two connections"); + test.maxscales->disconnect(); + test.expect(connections()[0] == '0', "The master should have no connections"); + + test.maxscales->connect(); + for (auto a : {"show status", "show variables", "show global status"}) + { + for (int i = 0; i < 10; i++) + { + test.try_query(test.maxscales->conn_rwsplit[0], "%s", a); + test.try_query(test.maxscales->conn_master[0], "%s", a); + } + } + test.maxscales->disconnect(); + + // Readwritesplit sanity checks + test_rwsplit(test); + + return test.global_result; +}