228 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/**
 | 
						|
 * 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.expect(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.expect(!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;
 | 
						|
}
 |