MXS-1549: Add basic test case

Added a basic test case that checks that the functionality works as
expected.
This commit is contained in:
Markus Mäkelä 2018-06-24 22:02:58 +03:00
parent 56f274d74a
commit 0911c664b1
No known key found for this signature in database
GPG Key ID: 72D48FCE664F7B19
4 changed files with 287 additions and 0 deletions

View File

@ -630,6 +630,10 @@ add_test_executable(mxs1509.cpp mxs1509 mxs1509 LABELS mysqlmon REPL_BACKEND)
# https://jira.mariadb.org/browse/MXS-1516
add_test_executable(mxs1516.cpp mxs1516 replication LABELS readconnroute REPL_BACKEND)
# MXS-1549: Optimistic transaction tests
# https://jira.mariadb.org/browse/MXS-1549
add_test_executable(mxs1549_optimistic_trx.cpp mxs1549_optimistic_trx mxs1549_optimistic_trx LABELS readwritesplit REPL_BACKEND)
# MXS-1542: Check that UTF16 strings work
# https://jira.mariadb.org/browse/MXS-1542
add_test_executable(mxs1542.cpp mxs1542 avro LABELS avrorouter REPL_BACKEND)

View File

@ -0,0 +1,48 @@
[maxscale]
threads=###threads###
log_info=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
[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
optimistic_trx=true
master_failure_mode=fail_on_write
[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

View File

@ -290,6 +290,11 @@ public:
return !row.empty() && row[0] == res;
}
Row row(std::string q)
{
return get_row(m_conn, q);
}
const char* error() const
{
return mysql_error(m_conn);

View File

@ -0,0 +1,230 @@
/**
* MXS-1549: Optimistic transaction tests
*
* https://jira.mariadb.org/browse/MXS-1549
*/
#include "testconnections.h"
#include <functional>
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char** argv)
{
TestConnections test(argc, argv);
Connection conn{test.maxscales->rwsplit()};
auto query = [&](bool should_work, string q)
{
test.assert(conn.query(q) == should_work, "Query '%s' should %s: %s",
q.c_str(), should_work ? "work" : "fail", conn.error());
};
auto compare = [&](bool equal, string q, string res)
{
Row row = conn.row(q);
test.assert(!row.empty() && (row[0] == res) == equal, "Values are %s: `%s` `%s`",
equal ? "not equal" : "equal", row.empty() ? "<empty>" : row[0].c_str(),
res.c_str());
};
auto block = [&](int node)
{
return bind([&](int i)
{
test.repl->block_node(i);
test.maxscales->wait_for_monitor();
}, node);
};
auto unblock = [&](int node)
{
return bind([&](int i)
{
test.repl->unblock_node(i);
test.maxscales->wait_for_monitor();
}, node);
};
auto ok = [&](string q)
{
return bind(query, true, q);
};
auto err = [&](string q)
{
return bind(query, false, q);
};
auto equal = [&](string q, string res)
{
return bind(compare, true, q, res);
};
auto not_equal = [&](string q, string res)
{
return bind(compare, false, q, res);
};
conn.connect();
conn.query("CREATE OR REPLACE TABLE test.t1(id INT)");
conn.disconnect();
test.repl->connect();
std::string master_id = test.repl->get_server_id_str(0);
std::string slave_id = test.repl->get_server_id_str(1);
test.repl->sync_slaves();
struct
{
const char* description;
vector<function<void ()>> steps;
} test_cases[]
{
{
"Minimal transaction works",
{
ok("START TRANSACTION"),
ok("COMMIT")
}
},
{
"Read-only is routed to the slave",
{
ok("START TRANSACTION"),
not_equal("SELECT @@server_id", master_id),
ok("COMMIT")
},
},
{
"Read-write is routed to the master",
{
ok("START TRANSACTION"),
ok("INSERT INTO test.t1 VALUES (1)"),
equal("SELECT @@server_id", master_id),
ok("COMMIT")
}
},
{
"Read-only after read-write is routed to slave",
{
ok("START TRANSACTION"),
ok("INSERT INTO test.t1 VALUES (1)"),
equal("SELECT @@server_id", master_id),
ok("COMMIT"),
ok("START TRANSACTION"),
equal("SELECT @@server_id", slave_id),
ok("COMMIT")
}
},
{
"Read-write after read-only is routed to master",
{
ok("START TRANSACTION"),
equal("SELECT @@server_id", slave_id),
ok("COMMIT"),
ok("START TRANSACTION"),
ok("INSERT INTO test.t1 VALUES (1)"),
equal("SELECT @@server_id", master_id),
ok("COMMIT")
}
},
{
"Blocking slave moves transaction to the master",
{
ok("START TRANSACTION"),
ok("SELECT COUNT(*) FROM test.t1"),
block(1),
equal("SELECT @@server_id", master_id),
ok("COMMIT"),
unblock(1)
}
},
{
"Blocking master has no effect",
{
block(0),
ok("START TRANSACTION"),
equal("SELECT @@server_id", slave_id),
ok("COMMIT"),
unblock(0)
}
},
{
"Blocking master mid-transaction has no effect",
{
ok("START TRANSACTION"),
block(0),
equal("SELECT @@server_id", slave_id),
ok("COMMIT"),
unblock(0)
}
},
{
"Blocking master before commit has no effect",
{
ok("START TRANSACTION"),
equal("SELECT @@server_id", slave_id),
block(0),
ok("COMMIT"),
unblock(0)
}
},
{
"Conflicting results terminate connection",
{
ok("START TRANSACTION"),
equal("SELECT @@server_id", slave_id),
err("INSERT INTO test.t1 VALUES (1)"),
err("COMMIT")
}
},
{
"Read-write works without slaves",
{
block(1),
ok("START TRANSACTION"),
ok("INSERT INTO test.t1 VALUES (1)"),
ok("COMMIT"),
unblock(1)
}
},
{
"Read-only works without slaves",
{
block(1),
ok("START TRANSACTION"),
equal("SELECT @@server_id", master_id),
ok("COMMIT"),
unblock(1)
}
},
};
for (auto& a: test_cases)
{
test.tprintf("%s", a.description);
conn.connect();
// Helps debugging to have a distict query in the log
conn.query(string("SELECT '") + a.description + "'");
for (auto s: a.steps)
{
s();
}
conn.disconnect();
test.repl->sync_slaves();
}
// Cleanup
conn.connect();
conn.query("DROP TABLE test.t1");
conn.disconnect();
test.repl->disconnect();
return test.global_result;
}