Files
MaxScale/maxscale-system-test/mxs2512_trx_replay_rollback.cpp
2019-11-13 08:37:17 +02:00

181 lines
4.5 KiB
C++

/*
* 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 <maxbase/assert.h>
#include <maxbase/semaphore.hh>
#include "mariadb_func.h"
#include "testconnections.h"
#include <iostream>
using namespace std;
namespace
{
namespace Query
{
enum Status
{
SUCCESS, // Execution succeeded.
FAILURE, // Execution failed (e.g. broken SQL).
ERROR // Execution succeeded but ended with an error (e.g. deadlock).
};
struct Result : std::pair<Query::Status, std::string>
{
Result(Status status = Query::FAILURE)
{
first = status;
}
Result(const std::string& message)
{
mxb_assert(!message.empty());
first = Query::ERROR;
second = message;
}
};
Result execute(MYSQL* pConn, const char* zStmt)
{
Result rv;
if (mysql_query(pConn, zStmt) == 0)
{
MYSQL_RES* pRes = mysql_store_result(pConn);
rv.second = mysql_error(pConn);
if (rv.second.empty())
{
rv.first = Query::SUCCESS;
}
else
{
rv.first = Query::ERROR;
}
mysql_free_result(pRes);
}
else
{
rv.second = mysql_error(pConn);
}
return rv;
}
void execute(MYSQL* pConn, const char* zStmt, Query::Status expectation)
{
Result rv = execute(pConn, zStmt);
mxb_assert(rv.first == expectation);
}
}
enum class Expectation
{
SUCCESS,
FAILURE
};
void run_test(TestConnections& test, Expectation expectation)
{
maxbase::Semaphore sem1;
maxbase::Semaphore sem2;
std::thread t1([&test, &sem1, &sem2]() {
MYSQL* pConn = test.maxscales->open_rwsplit_connection();
mxb_assert(pConn);
mysql_autocommit(pConn, false);
Query::execute(pConn, "BEGIN", Query::SUCCESS);
Query::execute(pConn, "INSERT INTO mxs2512 VALUES(1)", Query::SUCCESS);
sem1.post();
sem2.wait();
// First we sleep to be sure that t2 has time to issue its SELECT (that will block).
sleep(5);
Query::execute(pConn, "INSERT INTO mxs2512 VALUES(0)", Query::SUCCESS);
mysql_close(pConn);
});
Query::Result rv;
std::thread t2([&test, &sem1, &sem2, &rv]() {
MYSQL* pConn = test.maxscales->open_rwsplit_connection();
mxb_assert(pConn);
mysql_autocommit(pConn, false);
Query::execute(pConn, "BEGIN", Query::SUCCESS);
sem1.wait();
sem2.post();
rv = Query::execute(pConn, "SELECT * from mxs2512 FOR UPDATE");
mysql_close(pConn);
});
t1.join();
t2.join();
if (expectation == Expectation::FAILURE)
{
test.expect(rv.first == Query::ERROR, "SELECT did NOT fail.");
}
else
{
test.expect(rv.first == Query::SUCCESS, "SELECT DID fail.");
}
}
}
int main(int argc, char* argv[])
{
TestConnections test(argc, argv);
MYSQL* pConn = test.maxscales->open_rwsplit_connection();
test.expect(pConn, "Could not connect to rwsplit.");
// Preparations
test.try_query(pConn, "DROP TABLE IF EXISTS mxs2512");
test.try_query(pConn, "CREATE TABLE mxs2512 (x INT PRIMARY KEY)");
// Test with 'transaction_replay=false' => should fail.
cout << "Testing with 'transaction_replay=false', SELECT should fail." << endl;
run_test(test, Expectation::FAILURE);
// Intermediate cleanup; delete contents from table, turn on transaction replay, restart MaxScale.
test.try_query(pConn, "DELETE FROM mxs2512");
mysql_close(pConn);
test.stop_maxscale();
const char* zSed = "sed -i -e 's/transaction_replay=false/transaction_replay=true/' /etc/maxscale.cnf";
test.add_result(test.maxscales->ssh_node(0, zSed, true), "Could not tweak /etc/maxscale.cnf");
test.start_maxscale();
// Test with 'transaction_replay=true' => should succeed.
cout << "Testing with 'transaction_replay=true', SELECT should succeed." << endl;
run_test(test, Expectation::SUCCESS);
// Final cleanup
pConn = test.maxscales->open_rwsplit_connection();
test.expect(pConn, "Could not connect to rwsplit.");
test.try_query(pConn, "DROP TABLE mxs2512");
mysql_close(pConn);
return test.global_result;
}