From 450ffaa169500ba6989892c10f31736fd55e2fce Mon Sep 17 00:00:00 2001 From: Niclas Antti Date: Mon, 9 Dec 2019 14:27:52 +0200 Subject: [PATCH] MXS-2590 SmartQuery system test --- maxscale-system-test/CMakeLists.txt | 1 + .../cnf/maxscale.cnf.template.smart_query | 150 +++++++++++++++++ maxscale-system-test/smart_query.cpp | 153 ++++++++++++++++++ 3 files changed, 304 insertions(+) create mode 100644 maxscale-system-test/cnf/maxscale.cnf.template.smart_query create mode 100644 maxscale-system-test/smart_query.cpp diff --git a/maxscale-system-test/CMakeLists.txt b/maxscale-system-test/CMakeLists.txt index c3ec92b56..1e98e5e89 100644 --- a/maxscale-system-test/CMakeLists.txt +++ b/maxscale-system-test/CMakeLists.txt @@ -1005,6 +1005,7 @@ add_test_executable(mxs2521_double_exec.cpp mxs2521_double_exec mxs2521_double_e # MXS-2572: Add smartrouter tests. add_test_executable(sr_basics.cpp sr_basics sr_basics LABELS REPL_BACKEND) +add_test_executable(smart_query.cpp smart_query smart_query LABELS REPL_BACKEND) # MXS-2490: Direct execution doesn't work with MaxScale add_test_executable(mxs2490_ps_execute_direct.cpp mxs2490_ps_execute_direct replication LABELS REPL_BACKEND readwritesplit) diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.smart_query b/maxscale-system-test/cnf/maxscale.cnf.template.smart_query new file mode 100644 index 000000000..9c59a0da5 --- /dev/null +++ b/maxscale-system-test/cnf/maxscale.cnf.template.smart_query @@ -0,0 +1,150 @@ +[maxscale] +threads=###threads### +log_warning=1 +log_debug=1 + +[server1] +type=server +address=###node_server_IP_1### +port=###node_server_port_1### +protocol=MySQLBackend + +[server2] +type=server +address=###node_server_IP_2### +port=###node_server_port_2### +protocol=MySQLBackend + +[server3] +type=server +address=###node_server_IP_3### +port=###node_server_port_3### +protocol=MySQLBackend + +[server4] +type=server +address=###node_server_IP_4### +port=###node_server_port_4### +protocol=MySQLBackend + +[Monitor1] +type=monitor +module=mariadbmon +servers=server1 +user=maxskysql +password=skysql +monitor_interval=1000 + +[Monitor2] +type=monitor +module=mariadbmon +servers=server2 +user=maxskysql +password=skysql +monitor_interval=1000 + +[Monitor3] +type=monitor +module=mariadbmon +servers=server3 +user=maxskysql +password=skysql +monitor_interval=1000 + +[Monitor4] +type=monitor +module=mariadbmon +servers=server4 +user=maxskysql +password=skysql +monitor_interval=1000 + +[RWS1] +type=service +router=readwritesplit +servers=server1 +user=maxskysql +password=skysql +slave_selection_criteria=ADAPTIVE_ROUTING + +[RWS1-Listener] +type=listener +service=RWS1 +protocol=mariadbclient +socket=/tmp/rws1.sock + +[RWS1-As-Server] +type=server +protocol=mariadbbackend +socket=/tmp/rws1.sock + +[RWS2] +type=service +router=readwritesplit +servers=server2 +user=maxskysql +password=skysql +slave_selection_criteria=ADAPTIVE_ROUTING + +[RWS2-Listener] +type=listener +service=RWS2 +protocol=mariadbclient +socket=/tmp/rws2.sock + +[RWS2-As-Server] +type=server +protocol=mariadbbackend +socket=/tmp/rws2.sock + +[RWS3] +type=service +router=readwritesplit +servers=server3 +user=maxskysql +password=skysql +slave_selection_criteria=ADAPTIVE_ROUTING + +[RWS3-Listener] +type=listener +service=RWS3 +protocol=mariadbclient +socket=/tmp/rws3.sock + +[RWS3-As-Server] +type=server +protocol=mariadbbackend +socket=/tmp/rws3.sock + +[RWS4] +type=service +router=readwritesplit +servers=server4 +user=maxskysql +password=skysql +slave_selection_criteria=ADAPTIVE_ROUTING + +[RWS4-Listener] +type=listener +service=RWS4 +protocol=mariadbclient +socket=/tmp/rws4.sock + +[RWS4-As-Server] +type=server +protocol=mariadbbackend +socket=/tmp/rws4.sock + +[SmartQuery] +type=service +router=smartrouter +servers=RWS1-As-Server, RWS2-As-Server, RWS3-As-Server, RWS4-As-Server +master=RWS1-As-Server +user=maxskysql +password=skysql + +[SmartQuery-Listener] +type=listener +service=SmartQuery +protocol=mariadbclient +port=4006 diff --git a/maxscale-system-test/smart_query.cpp b/maxscale-system-test/smart_query.cpp new file mode 100644 index 000000000..134efe4aa --- /dev/null +++ b/maxscale-system-test/smart_query.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2019 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: 2023-11-12 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include "testconnections.h" +#include +#include +#include +#include + + +/** Smart query test. + * + * Since there is no columnstore support in the system-test yet, this code + * makes sure that when a query has been executed once, the subsequent runs + * of the same query are executed on the same server. + * + * The setup is such that there are multiple ReadWriteSplits, each with a + * single server, where these RWS:s are the "servers" of the SmartRouter. + * + * Caveat: This test assumes that the servers are (nearly) identical, which + * they are in system-test. If one server is much faster than the + * others this test might not find a problem even if there is one. + */ +namespace +{ + +constexpr int NUM_INTS = 1000; +constexpr int NUM_THREADS = 25; + +// The test must finish before SmartRouter invalidates the cached entry (2 minutes). +const maxbase::Duration TEST_RUN_TIME = std::chrono::seconds(60); + +/** + * @brief setup two tables to be joined, enabling a query with only a little + * IO but sufficient server work to make a difference at the servers. + * @param test + */ +void setup_test(TestConnections& test) +{ + Connection c = test.maxscales->rwsplit(); + test.expect(c.connect(), "Could not connect to MaxScale."); + test.expect(c.query("drop table if exists ints1"), "Could not drop ints1."); + test.expect(c.query("drop table if exists ints2"), "Could not drop ints2."); + test.expect(c.query("create table ints1(val int)"), "Could not create table ints1."); + test.expect(c.query("create table ints2(val int)"), "Could not create table ints2."); + + std::string sequence = std::string("seq_1_to_") + std::to_string(NUM_INTS); + + test.expect(c.query("insert into ints1 select seq from " + sequence), + "Could not insert into ints1."); + test.expect(c.query("insert into ints2 select seq from " + sequence), + "Could not insert into ints2."); + + test.repl->sync_slaves(); +} + +/** + * @brief drop the tables + * @param test + */ +void tear_down_test(TestConnections& test) +{ + Connection c = test.maxscales->rwsplit(); + test.expect(c.connect(), "Could not connect to MaxScale."); + + test.expect(c.query("drop table if exists ints1"), "Could not drop ints1."); + test.expect(c.query("drop table if exists ints2"), "Could not drop ints2."); +} + +/** + * @brief the one and only query. This should require enough work by the + * servers to make it unpredictable which one will finish first. + */ +const std::string THE_QUERY = "select @@server_id, count(*)" + " from ints1, ints2 where ints1.val = ints2.val"; + +/** + * @brief execute the_query on a new connection + * @param test + * @return the server that executed the query + */ +int track_server(TestConnections& test) +{ + Connection c = test.maxscales->rwsplit(); + test.expect(c.connect(), "Could not connect to MaxScale."); + + Result rows = c.rows(THE_QUERY); + test.expect(rows.size() == 1, "Expected exactly one row."); + + int server_id = std::stoi(rows[0][0]); + int count = std::stoi(rows[0][1]); + + test.expect(count == NUM_INTS, "Expected a count of %d, but got %d.", NUM_INTS, count); + + return server_id; +} + +/** + * @brief First execute the query once to establish which server the + * smart-router selected. Then run the query in parallel expecting + * all queries to be executed by the selected server. + * @param test + */ +void run_test(TestConnections& test) +{ + maxbase::StopWatch sw; + int selected_server_id = track_server(test); + int test_count = 0; + + std::cout << "selected_server_id " << selected_server_id << std::endl; + + while (!test.global_result && sw.split() < TEST_RUN_TIME) + { + std::vector> futures; + for (int i = 0; i < NUM_THREADS; ++i) + { + futures.emplace_back(std::async(std::launch::async, track_server, std::ref(test))); + } + + for (auto& fut : futures) + { + ++test_count; + int server_id = fut.get(); + test.expect(selected_server_id == server_id, + "Expected %d server_id but got %d.", selected_server_id, server_id); + } + } + + std::cout << "number of tests run: " << test_count << std::endl; +} +} + + +int main(int argc, char* argv[]) +{ + TestConnections test(argc, argv); + + setup_test(test); + run_test(test); + tear_down_test(test); + + return test.global_result; +}