MXS-2590 SmartQuery system test
This commit is contained in:
@ -1005,6 +1005,7 @@ add_test_executable(mxs2521_double_exec.cpp mxs2521_double_exec mxs2521_double_e
|
|||||||
|
|
||||||
# MXS-2572: Add smartrouter tests.
|
# MXS-2572: Add smartrouter tests.
|
||||||
add_test_executable(sr_basics.cpp sr_basics sr_basics LABELS REPL_BACKEND)
|
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
|
# 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)
|
add_test_executable(mxs2490_ps_execute_direct.cpp mxs2490_ps_execute_direct replication LABELS REPL_BACKEND readwritesplit)
|
||||||
|
|||||||
150
maxscale-system-test/cnf/maxscale.cnf.template.smart_query
Normal file
150
maxscale-system-test/cnf/maxscale.cnf.template.smart_query
Normal file
@ -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
|
||||||
153
maxscale-system-test/smart_query.cpp
Normal file
153
maxscale-system-test/smart_query.cpp
Normal file
@ -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 <maxbase/stopwatch.hh>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
|
||||||
|
/** 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<std::future<int>> 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user