Files
MaxScale/maxscale-system-test/mxs1507_trx_replay.cpp
Markus Mäkelä 86cdb14286 Don't process queued commands when replaying transaction
If a transaction is replayed, queued commands must not be processed. The
exception to this rule is when pending session commands are executed
before the first statement in the replayed transaction is executed.
2018-07-11 14:08:47 +03:00

285 lines
7.6 KiB
C++

/**
* MXS-1507: Transaction replay tests
*
* https://jira.mariadb.org/browse/MXS-1507
*/
#include "testconnections.h"
#include <functional>
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char** argv)
{
TestConnections test(argc, argv);
auto query = [&](string q)
{
return execute_query_silent(test.maxscales->conn_rwsplit[0], q.c_str()) == 0;
};
auto ok = [&](string q)
{
test.assert(query(q), "Query '%s' should work: %s", q.c_str(), mysql_error(test.maxscales->conn_rwsplit[0]));
};
auto err = [&](string q)
{
test.assert(!query(q), "Query should not work: %s", q.c_str());
};
auto check = [&](string q, string res)
{
Row row = get_row(test.maxscales->conn_rwsplit[0], q.c_str());
test.assert(!row.empty() && row[0] == res, "Query '%s' should return 1: %s (%s)",
q.c_str(), row.empty() ? "<empty>": row[0].c_str(),
mysql_error(test.maxscales->conn_rwsplit[0]));
};
struct TrxTest
{
string description;
vector<function<void ()>> pre;
vector<function<void ()>> post;
vector<function<void ()>> check;
};
std::vector<TrxTest> tests
({
{
"Basic transaction",
{
bind(ok, "BEGIN"),
bind(ok, "SELECT 1"),
},
{
bind(ok, "SELECT 2"),
bind(ok, "COMMIT"),
},
{
}
},
{
"Large result",
{
bind(ok, "BEGIN"),
bind(ok, "SELECT REPEAT('a', 100000)"),
},
{
bind(ok, "SELECT REPEAT('a', 100000)"),
bind(ok, "COMMIT"),
},
{
}
},
{
"Transaction with a write",
{
bind(ok, "BEGIN"),
bind(ok, "INSERT INTO test.t1 VALUES (1)"),
},
{
bind(ok, "INSERT INTO test.t1 VALUES (2)"),
bind(ok, "COMMIT"),
},
{
bind(check, "SELECT COUNT(*) FROM test.t1 WHERE id IN (1, 2)", "2"),
}
},
{
"Read-only transaction",
{
bind(ok, "START TRANSACTION READ ONLY"),
bind(ok, "SELECT 1"),
},
{
bind(ok, "SELECT 2"),
bind(ok, "COMMIT"),
},
{
}
},
{
"Trx started, no queries",
{
bind(ok, "BEGIN"),
},
{
bind(ok, "SELECT 1"),
bind(ok, "COMMIT"),
},
{
}
},
{
"Trx waiting on commit",
{
bind(ok, "BEGIN"),
bind(ok, "SELECT 1"),
},
{
bind(ok, "COMMIT"),
},
{
}
},
{
"Trx with NOW()",
{
bind(ok, "BEGIN"),
bind(ok, "SELECT NOW(), SLEEP(1)"),
},
{
bind(err, "SELECT 1"),
},
{
}
},
{
"Commit trx with NOW()",
{
bind(ok, "BEGIN"),
bind(ok, "SELECT NOW(), SLEEP(1)"),
},
{
bind(err, "COMMIT"),
},
{
}
},
{
"NOW() used after replay",
{
bind(ok, "BEGIN"),
bind(ok, "SELECT 1"),
},
{
bind(ok, "SELECT NOW()"),
bind(ok, "COMMIT"),
},
{
}
},
{
"Exceed transaction length limit",
{
bind(ok, "BEGIN"),
bind(ok, "SELECT 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'"),
},
{
bind(err, "SELECT 7"),
bind(err, "COMMIT"),
},
{
}
},
{
"Normal trx after hitting limit",
{
bind(ok, "BEGIN"),
bind(ok, "SELECT 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'"),
},
{
bind(err, "SELECT 7"),
bind(err, "COMMIT"),
},
{
bind(ok, "BEGIN"),
bind(ok, "SELECT 1"),
bind(ok, "SELECT 2"),
bind(ok, "COMMIT"),
}
},
{
"Session command inside transaction",
{
bind(ok, "BEGIN"),
bind(ok, "SET @a = 1"),
},
{
bind(check, "SELECT @a", "1"),
bind(ok, "COMMIT"),
},
{
}
},
{
"Empty transaction",
{
bind(ok, "BEGIN"),
},
{
bind(ok, "COMMIT"),
},
{
}
}
});
// Create a table for testing
test.maxscales->connect();
test.try_query(test.maxscales->conn_rwsplit[0], "CREATE OR REPLACE TABLE test.t1(id INT)");
test.maxscales->disconnect();
int i = 1;
for (auto& a : tests)
{
test.set_timeout(90);
test.tprintf("%d: %s", i++, a.description.c_str());
test.maxscales->connect();
for (auto& f : a.pre)
{
f();
}
// Block and unblock the master
test.repl->block_node(0);
test.maxscales->wait_for_monitor();
test.repl->unblock_node(0);
test.maxscales->wait_for_monitor();
for (auto& f : a.post)
{
f();
}
test.maxscales->disconnect();
test.repl->connect();
test.repl->sync_slaves();
test.repl->disconnect();
test.maxscales->connect();
for (auto& f : a.check)
{
f();
}
test.maxscales->disconnect();
// Clear the table at the end of the test
test.maxscales->connect();
test.try_query(test.maxscales->conn_rwsplit[0], "TRUNCATE TABLE test.t1");
test.maxscales->disconnect();
}
test.maxscales->connect();
test.try_query(test.maxscales->conn_rwsplit[0], "DROP TABLE test.t1");
test.maxscales->disconnect();
return test.global_result;
}