The test appears to have failed at least once when the monitor did not, for some reason, notice that a server was down. Also adjusted the queries to be more unique across the test cases.
		
			
				
	
	
		
			288 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			288 lines
		
	
	
		
			7.8 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.expect(query(q),
 | 
						|
                        "Query '%s' should work: %s",
 | 
						|
                        q.c_str(),
 | 
						|
                        mysql_error(test.maxscales->conn_rwsplit[0]));
 | 
						|
        };
 | 
						|
 | 
						|
    auto err = [&](string q) {
 | 
						|
            test.expect(!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.expect(!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 8"),
 | 
						|
                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(2);
 | 
						|
        test.repl->unblock_node(0);
 | 
						|
        test.maxscales->wait_for_monitor(2);
 | 
						|
 | 
						|
        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;
 | 
						|
}
 |