Merge branch '2.1' into develop

This commit is contained in:
Markus Mäkelä 2017-06-05 13:25:27 +03:00
commit 457fc80647
39 changed files with 300 additions and 257 deletions

View File

@ -1,4 +1,4 @@
# MariaDB MaxScale 2.1.2 Release Notes
# MariaDB MaxScale 2.1.2 Release Notes -- 2017-04-03
Release 2.1.2 is a Beta release.

View File

@ -1,4 +1,4 @@
# MariaDB MaxScale 2.1.3 Release Notes
# MariaDB MaxScale 2.1.3 Release Notes -- 2017-05-23
Release 2.1.3 is a GA release.

View File

@ -36,6 +36,7 @@
#include <errno.h>
#include <getopt.h>
#include <stdbool.h>
#include <pwd.h>
#include <maxscale/version.h>
@ -253,6 +254,11 @@ main(int argc, char **argv)
if ((so = connectUsingInetSocket(hostname, port, user, passwd)) == -1)
{
if (access(MAXADMIN_DEFAULT_SOCKET, R_OK) == 0)
{
fprintf(stderr, "Found default MaxAdmin socket in: %s\n", MAXADMIN_DEFAULT_SOCKET);
fprintf(stderr, "Try connecting with:\n\n\tmaxadmin -S %s\n\n", MAXADMIN_DEFAULT_SOCKET);
}
exit(EXIT_FAILURE);
}
}
@ -597,7 +603,13 @@ authUnixSocket(int so)
if (!authenticated)
{
fprintf(stderr, "Could connect to MaxScale, but was not authorized.\n");
uid_t id = geteuid();
struct passwd* pw = getpwuid(id);
fprintf(stderr, "Could connect to MaxScale, but was not authorized.\n"
"Check that the current user is added to the list of allowed users.\n"
"To add this user to the list, execute:\n\n"
"\tsudo maxadmin enable account %s\n\n"
"This assumes that the root user account is enabled in MaxScale.\n", pw->pw_name);
}
return authenticated;

View File

@ -121,7 +121,7 @@ add_test_executable(bug547.cpp bug547 replication LABELS readwritesplit REPL_BAC
add_test_executable(bug681.cpp bug681 galera.bug681 LABELS readwritesplit GALERA_BACKEND)
# Regression case for the bug "crash with tee filter"
add_test_executable(bug643.cpp bug643 bug643 LABELS tee REPL_BACKEND)
#add_test_executable(bug643.cpp bug643 bug643 LABELS tee REPL_BACKEND)
# Regression case for the bug ""Different error messages from MariaDB and Maxscale"
add_test_script(bug561.sh bug561.sh replication LABELS MySQLAuth REPL_BACKEND)
@ -166,13 +166,13 @@ add_test_executable(bug626.cpp bug626 replication LABELS MySQLAuth MySQLProtocol
add_test_executable(bug634.cpp bug634 replication LABELS readwritesplit REPL_BACKEND)
# Regression cases for several TEE filter hangs
add_test_executable(bug645.cpp bug645 bug645 LABELS tee REPL_BACKEND)
add_test_executable(bug645_1.cpp bug645_1 bug645_1 LABELS tee REPL_BACKEND)
add_test_executable(bug649.cpp bug649 bug645 LABELS tee)
add_test_executable(bug650.cpp bug650 bug650 LABELS tee REPL_BACKEND)
#add_test_executable(bug645.cpp bug645 bug645 LABELS tee REPL_BACKEND)
#add_test_executable(bug645_1.cpp bug645_1 bug645_1 LABELS tee REPL_BACKEND)
#add_test_executable(bug649.cpp bug649 bug645 LABELS tee)
#add_test_executable(bug650.cpp bug650 bug650 LABELS tee REPL_BACKEND)
# Heavy test for TEE filter
add_test_script(bug648 sql_queries bug648 LABELS tee UNSTABLE HEAVY REPL_BACKEND)
#add_test_script(bug648 sql_queries bug648 LABELS tee UNSTABLE HEAVY REPL_BACKEND)
# Crash when host name for some user in mysql.user is very long
add_test_executable(bug653.cpp bug653 replication LABELS MySQLAuth MySQLProtocol REPL_BACKEND)
@ -181,7 +181,7 @@ add_test_executable(bug653.cpp bug653 replication LABELS MySQLAuth MySQLProtocol
add_test_executable(bug654.cpp bug654 replication LABELS maxscale REPL_BACKEND)
# Regression case for the bug "Tee filter: closing child session causes MaxScale to fail"
add_test_executable(bug657.cpp bug657 bug657 LABELS tee REPL_BACKEND)
#add_test_executable(bug657.cpp bug657 bug657 LABELS tee REPL_BACKEND)
# Block backends (master or all slaves) and tries to connect Maxscale
add_test_executable(bug658.cpp bug658 replication LABELS readwritesplit readconnroute maxscale REPL_BACKEND)
@ -193,7 +193,7 @@ add_test_executable(bug662.cpp bug662 replication LABELS readwritesplit readconn
add_test_executable(bug664.cpp bug664 bug664 LABELS MySQLAuth MySQLProtocol)
# TEE fileter: execute long sequence of queries ans session commands in the loop
add_test_executable(bug670.cpp bug670 bug670 LABELS tee REPL_BACKEND)
#add_test_executable(bug670.cpp bug670 bug670 LABELS tee REPL_BACKEND)
# Regression case for the bug "MaxScale crashes if "Users table data" is empty and "show dbusers" is executed in maxadmin"
add_test_executable(bug673.cpp bug673 bug673 LABELS MySQLAuth REPL_BACKEND)
@ -327,7 +327,7 @@ add_test_executable(mm.cpp mm mm LABELS mmmon BREAKS_REPL)
add_test_executable(mm_mysqlmon.cpp mm_mysqlmon mm_mysqlmon LABELS mysqlmon REPL_BACKEND BREAKS_REPL)
# MySQL Monitor crash safety
add_test_executable(mysqlmon_backup.cpp mysqlmon_backup mysqlmon_backup LABELS mysqlmon REPL_BACKEND)
#add_test_executable(mysqlmon_backup.cpp mysqlmon_backup mysqlmon_backup LABELS mysqlmon REPL_BACKEND)
# Regression case for the bug "Two monitors loaded at the same time result into not working installation"
add_test_executable(mxs118.cpp mxs118 mxs118 LABELS maxscale LIGHT REPL_BACKEND)
@ -366,7 +366,7 @@ add_test_executable(mxs431.cpp mxs431 sharding LABELS schemarouter REPL_BACKEND
add_test_executable(mxs47.cpp mxs47 replication LABELS MySQLProtocol LIGHT REPL_BACKEND)
# Regression case for the bug "USE <db> hangs when Tee filter uses matching"
add_test_executable(mxs501_tee_usedb.cpp mxs501_tee_usedb mxs501 LABELS tee REPL_BACKEND)
#add_test_executable(mxs501_tee_usedb.cpp mxs501_tee_usedb mxs501 LABELS tee REPL_BACKEND)
# Open connection, execute 'change user', close connection in the loop
add_test_executable(mxs548_short_session_change_user.cpp mxs548_short_session_change_user mxs548 LABELS MySQLProtocol REPL_BACKEND)
@ -422,7 +422,7 @@ add_test_executable(mxs822_maxpasswd.cpp mxs822_maxpasswd maxpasswd LABELS maxsc
# Do only SELECTS during time > wait_timeout and then do INSERT
# This test will fail because the functionality hasn't been implemented
add_test_executable(mxs827_write_timeout.cpp mxs827_write_timeout mxs827_write_timeout LABELS readwritesplit REPL_BACKEND)
#add_test_executable(mxs827_write_timeout.cpp mxs827_write_timeout mxs827_write_timeout LABELS readwritesplit REPL_BACKEND)
# Block and unblock first and second slaves and check that they are recovered
add_test_executable(mxs874_slave_recovery.cpp mxs874_slave_recovery mxs874 LABELS readwritesplit REPL_BACKEND)
@ -536,8 +536,8 @@ add_test_executable(rwsplit_read_only_trx.cpp rwsplit_read_only_trx rwsplit_read
# Test replication-manager with MaxScale
add_test_executable(replication_manager.cpp replication_manager replication_manager LABELS maxscale REPL_BACKEND)
add_test_executable_notest(replication_manager_2nodes.cpp replication_manager_2nodes replication_manager_2nodes LABELS maxscale REPL_BACKEND)
add_test_executable_notest(replication_manager_3nodes.cpp replication_manager_3nodes replication_manager_3nodes LABELS maxscale REPL_BACKEND)
#add_test_executable_notest(replication_manager_2nodes.cpp replication_manager_2nodes replication_manager_2nodes LABELS maxscale REPL_BACKEND)
#add_test_executable_notest(replication_manager_3nodes.cpp replication_manager_3nodes replication_manager_3nodes LABELS maxscale REPL_BACKEND)
# Schemarouter duplicate database detection test: create DB on all nodes and then try query againt schema router
add_test_executable(schemarouter_duplicate_db.cpp schemarouter_duplicate_db schemarouter_duplicate_db LABELS schemarouter REPL_BACKEND)
@ -584,8 +584,12 @@ add_test_script(sql_queries_pers10 sql_queries sql_queries_pers10 LABELS maxscal
# Execute queries of different size, check data is the same when accessing via Maxscale and directly to backend, client ssl is ON
add_test_script(ssl sql_queries ssl LABELS maxscale readwritesplit REPL_BACKEND)
# Check load balancing, client ssl is ON
add_test_script(ssl_load load_balancing ssl_load LABELS maxscale readwritesplit REPL_BACKEND)
# Disabled due to some strangeness in Connector-C 3.0 TLS connections which
# cause uneven distribution of connections.
#add_test_script(ssl_load load_balancing ssl_load LABELS maxscale readwritesplit REPL_BACKEND)
# Check load balancing, client ssl is ON, Galera backend
add_test_script(ssl_load_galera load_balancing_galera ssl_load_galera LABELS maxscale readwritesplit GALERA_BACKEND)
@ -606,7 +610,7 @@ add_test_executable(test_hints.cpp test_hints hints2 LABELS hintfilter LIGHT REP
add_test_executable(avro.cpp avro avro LABELS avrorouter binlogrouter LIGHT BREAKS_REPL)
# Test avrorouter file compression
add_test_script(avro_compression avro avro_compression LABELS avrorouter binlogrouter LIGHT BREAKS_REPL)
#add_test_script(avro_compression avro avro_compression LABELS avrorouter binlogrouter LIGHT BREAKS_REPL)
# In the binlog router setup stop Master and promote one of the Slaves to be new Master
add_test_executable(binlog_change_master.cpp binlog_change_master setup_binlog_tx_safe LABELS binlogrouter BREAKS_REPL)
@ -680,7 +684,7 @@ add_test_executable(kerberos_setup.cpp kerberos_setup kerberos LABELS HEAVY gssa
#add_test_executable(bad_pers.cpp bad_pers bad_pers LABELS REPL_BACKEND)
# Test Aurora RDS monitor
add_test_executable(auroramon.cpp auroramon auroramon LABELS HEAVY EXTERNAL_BACKEND)
#add_test_executable(auroramon.cpp auroramon auroramon LABELS HEAVY EXTERNAL_BACKEND)
# Disabled for the time being
# add_test_executable(gatekeeper.cpp gatekeeper gatekeeper LABELS gatekeeper)

View File

@ -25,45 +25,33 @@ using std::endl;
int main(int argc, char *argv[])
{
TestConnections * Test = new TestConnections(argc, argv);
Test->set_timeout(600);
Test->stop_maxscale();
Test->ssh_maxscale(true, (char *) "rm -rf /var/lib/maxscale/avro");
TestConnections test(argc, argv);
test.set_timeout(600);
test.ssh_maxscale(true, (char *) "rm -rf /var/lib/maxscale/avro");
Test->repl->connect();
execute_query(Test->repl->nodes[0], "DROP TABLE IF EXISTS t1");
Test->repl->close_connections();
sleep(5);
/** Start master to binlogrouter replication */
if (!test.replicate_from_master())
{
return 1;
}
test.set_timeout(120);
test.repl->connect();
Test->start_binlog();
create_t1(test.repl->nodes[0]);
insert_into_t1(test.repl->nodes[0], 3);
execute_query(test.repl->nodes[0], "FLUSH LOGS");
Test->set_timeout(120);
Test->stop_maxscale();
Test->ssh_maxscale(true, "rm -rf /var/lib/maxscale/avro");
Test->set_timeout(120);
Test->start_maxscale();
Test->set_timeout(60);
Test->repl->connect();
create_t1(Test->repl->nodes[0]);
insert_into_t1(Test->repl->nodes[0], 3);
execute_query(Test->repl->nodes[0], "FLUSH LOGS");
Test->repl->close_connections();
Test->set_timeout(120);
test.repl->close_connections();
/** Give avrorouter some time to process the events */
test.stop_timeout();
sleep(10);
test.set_timeout(120);
char * avro_check = Test->ssh_maxscale_output(true,
"maxavrocheck -vv /var/lib/maxscale/avro/test.t1.000001.avro | grep \"{\"");
char * output = Test->ssh_maxscale_output(true, "maxavrocheck -d /var/lib/maxscale/avro/test.t1.000001.avro");
char * avro_check = test.ssh_maxscale_output(true,
"maxavrocheck -vv /var/lib/maxscale/avro/test.t1.000001.avro | grep \"{\"");
char * output = test.ssh_maxscale_output(true, "maxavrocheck -d /var/lib/maxscale/avro/test.t1.000001.avro");
std::istringstream iss;
iss.str(output);
@ -74,13 +62,13 @@ int main(int argc, char *argv[])
for (std::string line; std::getline(iss, line);)
{
long long int x1, fl;
Test->set_timeout(20);
test.set_timeout(20);
get_x_fl_from_json((char*)line.c_str(), &x1, &fl);
if (x1 != x1_exp || fl != fl_exp)
{
Test->add_result(1, "Output:x1 %lld, fl %lld, Expected: x1 %d, fl %d",
x1, fl, x1_exp, fl_exp);
test.add_result(1, "Output:x1 %lld, fl %lld, Expected: x1 %d, fl %d",
x1, fl, x1_exp, fl_exp);
break;
}
@ -89,19 +77,17 @@ int main(int argc, char *argv[])
x1_exp = 0;
x = x * 16;
fl_exp++;
Test->tprintf("fl = %d", fl_exp);
test.tprintf("fl = %d", fl_exp);
}
}
if (fl_exp != 3)
{
Test->add_result(1, "not enough lines in avrocheck output\n");
test.add_result(1, "not enough lines in avrocheck output");
}
Test->set_timeout(120);
execute_query(test.repl->nodes[0], "DROP TABLE test.t1;RESET MASTER");
test.repl->fix_replication();
int rval = Test->global_result;
delete Test;
return rval;
return test.global_result;
}

View File

@ -31,7 +31,10 @@ int main(int argc, char** argv)
}
}
// Wait for the connections to clean up
Test->stop_timeout();
sleep(5);
Test->check_maxscale_alive();
int rval = Test->global_result;
delete Test;

View File

@ -25,6 +25,7 @@ TestConnections * Test ;
int exit_flag;
int master = 0;
int i_trans = 0;
const int trans_max = 100;
int failed_transaction_num = 0;
/** The amount of rows each transaction inserts */
@ -294,7 +295,7 @@ void *transaction_thread( void *ptr )
Test->add_result(mysql_errno(conn), "Error connecting to Binlog router, error: %s\n", mysql_error(conn));
create_t1(conn);
while ((exit_flag == 0))
while ((exit_flag == 0) && i_trans < trans_max)
{
Test->tprintf("Transaction %d\n", i_trans);
trans_result = transaction(conn, i_trans);

View File

@ -43,12 +43,12 @@ int main(int argc, char *argv[])
Test->set_timeout(30);
Test->tprintf("Trying some queries, expecting failure, but not a crash\n");
execute_query(Test->conn_rwsplit, (char *) "DROP TABLE IF EXISTS t1");
execute_query(Test->conn_rwsplit, (char *) "CREATE TABLE t1 (x INT)");
execute_query(Test->conn_rwsplit, (char *) "INSERT INTO t1 (x) VALUES (1)");
execute_query(Test->conn_rwsplit, (char *) "select * from t1");
execute_query(Test->conn_master, (char *) "select * from t1");
execute_query(Test->conn_slave, (char *) "select * from t1");
execute_query(Test->conn_rwsplit, "DROP TABLE IF EXISTS t1");
execute_query(Test->conn_rwsplit, "CREATE TABLE t1 (x INT)");
execute_query(Test->conn_rwsplit, "INSERT INTO t1 (x) VALUES (1)");
execute_query(Test->conn_rwsplit, "select * from t1");
execute_query(Test->conn_master, "select * from t1");
execute_query(Test->conn_slave, "select * from t1");
Test->set_timeout(10);
Test->close_maxscale_connections();
@ -58,12 +58,8 @@ int main(int argc, char *argv[])
Test->stop_timeout();
sleep(15);
Test->check_log_err((char *) "fatal signal 11", false);
Test->check_log_err((char *) "Failed to create new router session for service 'RW-Split-Router'", true);
Test->check_log_err((char *)
"Failed to create new router session for service 'Read-Connection-Router-Master'", true);
Test->check_log_err((char *) "Failed to create new router session for service 'Read-Connection-Router-Slave'",
true);
Test->check_log_err("fatal signal 11", false);
Test->check_log_err("Failed to create new router session for service", true);
int rval = Test->global_result;
delete Test;

View File

@ -22,16 +22,6 @@ service=RW Split Router
* - check warnig in the log "RW Split Router: Recursive use of tee filter in service"
*/
/*
Mark Riddoch 2014-12-11 11:59:19 UTC
There is a recursive use of the tee filter in the configuration.
The "RW Split Router" uses the"duplicate" filter that will then duplicate all traffic to the original destination and another copy of the "RW Split Router", which again will duplicate all traffic to the original destination and another copy of the "RW Split Router"...
Really this needs to be trapped as a configuration error.
*/
#include <iostream>
#include "testconnections.h"
@ -56,7 +46,7 @@ int main(int argc, char *argv[])
Test->add_result(1, "FAIL: Query to broken service succeeded!\n");
}
Test->close_maxscale_connections();
Test->check_log_err((char *) "RW-Split-Router: Recursive use of tee filter in service", true);
Test->check_log_err("Recursive use of tee filter in service", true);
int rval = Test->global_result;
delete Test;

View File

@ -124,6 +124,11 @@ int main(int argc, char *argv[])
Test->try_query(Test->conn_rwsplit, (char *) "show processlist;");
Test->close_rwsplit();
/** Clean up */
Test->repl->connect();
execute_query(Test->repl->nodes[0], "DROP DATABASE test");
execute_query(Test->repl->nodes[0], "CREATE DATABASE test");
int rval = Test->global_result;
delete Test;
return rval;

View File

@ -40,42 +40,6 @@ service=RW Split Router
* - Reconnect readconnrouter
*/
/*
Vilho Raatikka 2014-12-22 08:35:52 UTC
How to reproduce:
1. Configure readconnrouter with tee filter and tee filter with a readwritesplit as a child service.
2. Start MaxScale
3. Connect readconnrouter
4. Fail the master node
5. Reconnect readconnrouter
As a consequence, next routeQuery will be duplicated to closed readwritesplit router and eventually fred memory will be accessed which causes SEGFAULT.
Reason for this is that situation where child (=branch -) session is closed as a consequence of node failure, is not handled in tee filter. Tee filter handles the case where client closes the session.
Comment 1 Vilho Raatikka 2014-12-22 09:14:13 UTC
Background: client session may be closed for different reasons. If client actively closes it by sending COM_QUIT packet, it happens from top to bottom: packet is identified and client DCB is closed. Client's DCB close routine also closes the client router session.
If backend fails and monitor detects it, then every DCB that isn't running or isn't master, slave, joined (Galera) nor ndb calls its hangup function. If the failed node was master then client session gets state SESSION_STATE_STOPPING which triggers first closing the client DCB and as a part of it, the whole session.
In tee filter, the first issue is the client DCB's close routine which doesn't trigger closing the session. The other issue is that if child session gets closed there's no mechanism that would prevent future queries being routed to tee's child service. As a consequence, future calls to routeQuery will access closed child session including freed memory etc.
Comment 2 Vilho Raatikka 2014-12-22 22:32:25 UTC
session.c:session_free:if session is child of another service (tee in this case), it is the parent which releases child's allocated memory back to the system. This now also includes the child router session.
dcb.h: Added DCB_IS_CLONE macro
tee.c:freeSession:if parent session triggered closing of tee, then child session may not be closed yet. In that case free the child session first and only then free child router session and release child session's memory back to system.
tee.c:routeQuery: only route if child session is ready for routing. Log if session is not ready for routing and set tee session inactive
mysql_client.c:gw_client_close:if DCB is cloned one don't close the protocol because they it is shared with the original DCB.
Comment 3 Vilho Raatikka 2014-12-23 10:04:11 UTC
If monitor haven't yet changed the status for failed backend, even the fixed won't notice the failure, and the client is left waiting for reply until some lower level timeout exceeds and closes the socket.
The solution is to register a callback function to readconnrouter's backend DCB in the same way that it is done in readwritesplit. Callback needs to be implemented and tests added.
By using this mechanism the client must wait at most one monitor interval before the session is closed.
Vilho Raatikka 2014-12-31 23:19:41 UTC
filter.c:filter_free:if filter parameter is NULL, return.
tee.c:freeSession: if my_session->dummy_filterdef is NULL, don't try to release the memory
*/
#include <iostream>
#include "testconnections.h"
#include "sql_t1.h"

View File

@ -44,8 +44,7 @@ int main(int argc, char *argv[])
Test->close_maxscale_connections();
Test->check_log_err((char *)
"Unable to start RW-Split-Router service. There are too few backend servers configured in", true);
Test->check_log_err("There are too few backend servers configured in", true);
int rval = Test->global_result;
delete Test;

View File

@ -204,6 +204,14 @@ bool Connection::readRow(std::string& dest)
else
{
dest += buf;
if (dest[0] == 'E' && dest[1] == 'R' & dest[2] == 'R')
{
m_error = "Server responded with an error: ";
m_error += dest;
rval = false;
break;
}
}
}

View File

@ -143,6 +143,7 @@ bool run_test(TestConnections& test)
{
bool rval = true;
test.tprintf("Inserting data");
for (int x = 0; test_set[x].types; x++)
{
for (int i = 0; test_set[x].types[i]; i++)
@ -152,6 +153,7 @@ bool run_test(TestConnections& test)
}
}
test.tprintf("Waiting for avrorouter to process data");
test.repl->connect();
execute_query(test.repl->nodes[0], "FLUSH LOGS");
test.repl->close_connections();
@ -196,6 +198,7 @@ bool run_test(TestConnections& test)
std::string err = conn.getError();
test.tprintf("Failed to request data: %s", err.c_str());
rval = false;
break;
}
test.stop_timeout();
}
@ -209,8 +212,7 @@ int main(int argc, char *argv[])
TestConnections::check_nodes(false);
TestConnections test(argc, argv);
test.start_binlog();
test.restart_maxscale();
test.replicate_from_master();
if (!run_test(test))
{

View File

@ -38,7 +38,9 @@ public:
bool operator ==(const TestOutput& output) const
{
return m_value == output.getValue();
return m_value == output.getValue() ||
(m_type.find("BLOB") != std::string::npos &&
output.getValue().length() == 0);
}
bool operator !=(const TestOutput& output) const

View File

@ -1,6 +1,5 @@
[maxscale]
threads=###threads###
log_warning=1
[Galera Monitor]
type=monitor
@ -17,11 +16,13 @@ router=readwritesplit
servers=server1,server2,server3,server4
user=maxskysql
passwd=skysql
router_options=slave_selection_criteria=LEAST_GLOBAL_CONNECTIONS
max_slave_connections=1
[Read Connection Router Slave]
type=service
router=readconnroute
router_options= slave
router_options=slave
servers=server1,server2,server3,server4
user=maxskysql
passwd=skysql
@ -39,7 +40,6 @@ type=listener
service=RW Split Router
protocol=MySQLClient
port=4006
#socket=/tmp/rwsplit.sock
[Read Connection Listener Slave]
type=listener
@ -53,6 +53,16 @@ service=Read Connection Router Master
protocol=MySQLClient
port=4008
[CLI]
type=service
router=cli
[CLI Listener]
type=listener
service=CLI
protocol=maxscaled
socket=default
[server1]
type=server
address=###galera_server_IP_1###
@ -76,14 +86,3 @@ type=server
address=###galera_server_IP_4###
port=###galera_server_port_4###
protocol=MySQLBackend
[CLI]
type=service
router=cli
[CLI Listener]
type=listener
service=CLI
protocol=maxscaled
#address=localhost
socket=default

View File

@ -44,6 +44,16 @@ protocol=MySQLClient
port=4008
#socket=/tmp/readconn.sock
[CLI]
type=service
router=cli
[CLI Listener]
type=listener
service=CLI
protocol=maxscaled
socket=default
[server1]
type=server
address=###galera_server_IP_1###

View File

@ -101,3 +101,13 @@ type=server
address=###node_server_IP_4###
port=###node_server_port_4###
protocol=MySQLBackend
[CLI]
type=service
router=cli
[CLI Listener]
type=listener
service=CLI
protocol=maxscaled
socket=default

View File

@ -87,4 +87,3 @@ type=server
address=###node_server_IP_4###
port=###node_server_port_4###
protocol=MySQLBackend

View File

@ -17,6 +17,7 @@ max_slave_connections=1
servers=server1,server2,server3,server4
user=maxskysql
passwd=skysql
router_options=disable_sescmd_history=false
[Read Connection Router Slave]
type=service
@ -53,6 +54,16 @@ service=Read Connection Router Master
protocol=MySQLClient
port=4008
[CLI]
type=service
router=cli
[CLI Listener]
type=listener
service=CLI
protocol=maxscaled
socket=default
[server1]
type=server
address=###node_server_IP_1###

View File

@ -36,7 +36,7 @@ int main(int argc, char *argv[])
sprintf(str, "rules%d", i);
Test->set_timeout(180);
copy_rules(Test, str, rules_dir);
Test->ssh_maxscale(true, "maxadmin call command dbfwfilter rules/reload Database-Firewall");
Test->ssh_maxscale(true, "maxadmin call command dbfwfilter rules/reload \"Database Firewall\"");
int local_result = 0;
sprintf(pass_file, "%s/fw/pass%d", test_dir, i);

View File

@ -461,14 +461,14 @@ int get_conn_num(MYSQL *conn, char * ip, char *hostname, char * db)
MYSQL_RES *res;
MYSQL_ROW row;
unsigned long long int num_fields;
//unsigned long long int row_i=0;
unsigned long long int rows;
unsigned long long int i;
unsigned int conn_num = 0;
char * hostname_internal;
const char * hostname_internal;
if (strcmp(ip, "127.0.0.1") == 0)
{
hostname_internal = (char*) "localhost";
hostname_internal = "localhost";
}
else
{
@ -516,9 +516,9 @@ int get_conn_num(MYSQL *conn, char * ip, char *hostname, char * db)
}
if (strcmp(ip, "127.0.0.1") == 0)
{
// one extra connection i svisible in the processlist
// one extra connection is visible in the process list
// output in case of local test
// (when maxscale is on the same machine as backends)
// (when MaxScale is on the same machine as backends)
conn_num--;
}
return conn_num;

View File

@ -18,13 +18,22 @@ function(add_java_test name src entry_point template)
endfunction()
# Some constants that make changing the connector easier
set(JDBC_JAR ${CMAKE_CURRENT_SOURCE_DIR}/mariadb-java-client-1.5.4.jar CACHE INTERNAL "")
set(JDBC_JAR_NAME "mariadb-java-client-1.5.9.jar")
set(JDBC_JAR ${CMAKE_CURRENT_BINARY_DIR}/${JDBC_JAR_NAME} CACHE INTERNAL "")
set(MXS_JAR ${CMAKE_CURRENT_BINARY_DIR}/maxscale_java.jar CACHE INTERNAL "")
set(TEST_JARPATH "${MXS_JAR}:${JDBC_JAR}" CACHE INTERNAL "")
# If we don't have the JDBC driver, download it
if(NOT EXISTS ${JDBC_JAR})
message(STATUS "Downloading MariaDB Connector-J: ${JDBC_JAR_NAME}")
file(DOWNLOAD https://downloads.mariadb.com/Connectors/java/connector-java-1.5.9/mariadb-java-client-1.5.9.jar
${CMAKE_CURRENT_BINARY_DIR}/${JDBC_JAR_NAME}
SHOW_PROGRESS)
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/MaxScaleConfiguration.java.in ${CMAKE_CURRENT_BINARY_DIR}/MaxScaleConfiguration.java @ONLY)
add_jar(maxscale_java SOURCES MaxScaleConnection.java MaxScaleConfiguration.java
INCLUDE_JARS mariadb-java-client-1.5.4.jar)
INCLUDE_JARS ${JDBC_JAR_NAME})
add_subdirectory(test1)
add_subdirectory(prep_stmt)
add_subdirectory(batch)

View File

@ -42,7 +42,7 @@ void check_status(TestConnections *Test, const char *server, const char *status)
void check_group(TestConnections *Test, const char *server, const char *group)
{
char *output = Test->ssh_maxscale_output(true, "maxadmin show monitor MySQL-Monitor");
char *output = Test->ssh_maxscale_output(true, "maxadmin show monitor \"MySQL Monitor\"");
if (output == NULL)
{

View File

@ -34,9 +34,10 @@ int main(int argc, char *argv[])
test->ssh_maxscale(true, "cp /etc/maxscale.cnf.backup /etc/maxscale.cnf");
/** Set router_options to a bad value */
test->ssh_maxscale(true, "sed -i -e 's/router_options.*/router_options=bad_option=true/' /etc/maxscale.cnf");
test->add_result(baseline == test->ssh_maxscale(true, "maxscale -c --user=maxscale"),
"Bad router_options should be detected.\n");
// Disabled for 2.0
//test->ssh_maxscale(true, "sed -i -e 's/router_options.*/router_options=bad_option=true/' /etc/maxscale.cnf");
//test->add_result(baseline == test->ssh_maxscale(true, "maxscale -c --user=maxscale"),
// "Bad router_options should be detected.\n");
test->ssh_maxscale(true, "cp /etc/maxscale.cnf.backup /etc/maxscale.cnf");

View File

@ -31,7 +31,7 @@ void try_password(TestConnections* Test, char * pass)
*/
Test->tprintf("Encrypting password: %s", pass);
Test->set_timeout(30);
int rc = Test->ssh_maxscale(true, "maxpasswd '%s' | tr -dc '[:xdigit:]' > /tmp/pw.txt && "
int rc = Test->ssh_maxscale(true, "maxpasswd /var/lib/maxscale/ '%s' | tr -dc '[:xdigit:]' > /tmp/pw.txt && "
"sed -i 's/user=.*/user=test/' /etc/maxscale.cnf && "
"sed -i \"s/passwd=.*/passwd=$(cat /tmp/pw.txt)/\" /etc/maxscale.cnf && "
"service maxscale restart && "

View File

@ -132,7 +132,7 @@ int main(int argc, char *argv[])
Test->tprintf("Connecting to RWSplit %s\n", Test->maxscale_IP);
Test->connect_rwsplit();
Test->execute_maxadmin_command((char *) "shutdown monitor MySQL-Monitor");
Test->execute_maxadmin_command((char *) "shutdown monitor \"MySQL Monitor\"");
get_global_status_allnodes(&selects[0], &inserts[0], Test->repl, silent);

View File

@ -30,7 +30,7 @@ int main(int argc, char *argv[])
Test->try_query(Test->routers[j], (char*) "select 1;");
Test->try_query(Test->routers[j], (char*) "set autocommit=1;");
Test->try_query(Test->routers[j], (char*) "select 2;");
if ((i / 100) * 100 == i)
if ((i / 1000) * 1000 == i)
{
Test->tprintf("i=%d\n", i);
}

View File

@ -16,59 +16,37 @@
int main(int argc, char *argv[])
{
TestConnections * Test = new TestConnections(argc, argv);
Test->set_timeout(20);
TestConnections test(argc, argv);
printf("Connecting to RWSplit");
test.set_timeout(60);
test.add_result(test.connect_rwsplit(), "Error connection to RWSplit! Exiting");
sleep(5);
test.tprintf("Checking current slave");
int res = 0;
int old_slave = test.find_connected_slave(&res);
test.add_result(res, "no current slave");
unsigned int current_slave;
unsigned int old_slave;
test.tprintf("Setup firewall to block mysql on old slave (oldslave is node %d)", old_slave);
printf("Connecting to RWSplit %s\n", Test->maxscale_IP);
if (Test->connect_rwsplit() != 0)
{
Test->add_result(1, "Error connection to RWSplit! Exiting\n");
}
else
{
test.add_result((old_slave < 0) || (old_slave >= test.repl->N), "Active slave is not found");
test.repl->block_node(old_slave);
Test->tprintf("Checking current slave\n");
old_slave = Test->find_connected_slave( &res);
test.tprintf("Waiting for MaxScale to find a new slave");
test.stop_timeout();
sleep(10);
Test->add_result(res, "no current slave\n");
test.set_timeout(20);
int current_slave = test.find_connected_slave(&res);
test.add_result((current_slave == old_slave) || (current_slave < 0), "No failover happened");
Test->tprintf("Setup firewall to block mysql on old slave (oldslave is node %d)\n", old_slave);
if ((old_slave < 0) || (old_slave >= Test->repl->N))
{
Test->add_result(1, "Active slave is not found\n");
}
else
{
Test->repl->block_node(old_slave);
test.tprintf("Unblock old node");
test.repl->unblock_node(old_slave);
test.close_rwsplit();
Test->tprintf("Sleeping 60 seconds to let MaxScale to find new slave\n");
Test->stop_timeout();
sleep(60);
Test->set_timeout(20);
test.check_maxscale_alive();
test.stop_timeout();
test.repl->fix_replication();
current_slave = Test->find_connected_slave(&res);
if ((current_slave == old_slave) || (current_slave < 0))
{
Test->add_result(1, "No failover happened\n");
}
Test->tprintf("Setup firewall back to allow mysql\n");
Test->repl->unblock_node(old_slave);
Test->close_rwsplit();
Test->check_maxscale_alive();
Test->set_timeout(20);
}
Test->set_timeout(200);
Test->repl->start_replication();
}
int rval = Test->global_result;
delete Test;
return rval;
return test.global_result;
}

View File

@ -954,6 +954,44 @@ int TestConnections::start_binlog()
return global_result;
}
bool TestConnections::replicate_from_master()
{
bool rval = true;
/** Stop the binlogrouter */
MYSQL* conn = open_conn_no_db(binlog_port, maxscale_IP, repl->user_name, repl->password, ssl);
if (execute_query(conn, "stop slave"))
{
rval = false;
}
mysql_close(conn);
/** Clean up MaxScale directories */
prepare_binlog();
ssh_maxscale(true, "service maxscale restart");
char log_file[256] = "";
char log_pos[256] = "4";
repl->execute_query_all_nodes("STOP SLAVE");
repl->connect();
execute_query(repl->nodes[0], "RESET MASTER");
conn = open_conn_no_db(binlog_port, maxscale_IP, repl->user_name, repl->password, ssl);
if (find_field(repl->nodes[0], "show master status", "File", log_file) ||
repl->set_slave(conn, repl->IP[0], repl->port[0], log_file, log_pos) ||
execute_query(conn, "start slave"))
{
rval = false;
}
mysql_close(conn);
return rval;
}
int TestConnections::start_mm()
{
int i;

View File

@ -442,6 +442,11 @@ public:
*/
int start_binlog();
/**
* @brief Start binlogrouter replication from master
*/
bool replicate_from_master();
/**
* @brief prepare_binlog clean up binlog directory, set proper access rights to it
* @return 0

View File

@ -413,19 +413,34 @@ auto_ptr<MaskingRules::Rule> create_rule_from_elements(json_t* pReplace,
json_t* pValue = json_object_get(pWith, KEY_VALUE);
json_t* pFill = json_object_get(pWith, KEY_FILL);
if ((pValue || pFill) &&
(!pValue || json_is_string(pValue)) &&
(!pFill || json_is_string(pFill)))
if (!pFill)
{
sRule = create_rule_from_elements(pColumn, pTable, pDatabase,
pValue, pFill,
pApplies_to, pExempted);
// Allowed. Use default value for fill and add it to pWith.
pFill = json_string("X");
if (pFill)
{
json_object_set_new(pWith, KEY_FILL, pFill);
}
else
{
MXS_ERROR("json_string() error, cannot produce a valid rule.");
}
}
else
if (pFill)
{
MXS_ERROR("The '%s' object of a masking rule does not have either '%s' "
"or '%s' as keys, or their values are not strings.",
KEY_WITH, KEY_VALUE, KEY_FILL);
if ((!pValue || (json_is_string(pValue) && json_string_length(pValue))) &&
(json_is_string(pFill) && json_string_length(pFill)))
{
sRule = create_rule_from_elements(pColumn, pTable, pDatabase,
pValue, pFill,
pApplies_to, pExempted);
}
else
{
MXS_ERROR("One of the keys '%s' or '%s' of masking rule object '%s' "
"has a non-string value or the string is empty.",
KEY_VALUE, KEY_FILL, KEY_WITH);
}
}
}
else

View File

@ -16,7 +16,6 @@
# pip install kafka-python
#
import json
import sys
import argparse
from kafka import KafkaProducer
@ -30,8 +29,6 @@ parser.add_argument("-T", "--kafka-topic", dest="kafka_topic",
default=None, required=True)
opts = parser.parse_args(sys.argv[1:])
decoder = json.JSONDecoder()
rbuf = bytes()
producer = KafkaProducer(bootstrap_servers=[opts.kafka_broker])
while True:
@ -41,18 +38,9 @@ while True:
if len(buf) == 0:
break
rbuf += buf.encode()
while True:
rbuf = rbuf.lstrip()
data = decoder.raw_decode(rbuf.decode('utf_8'))
rbuf = rbuf[data[1]:]
producer.send(topic=opts.kafka_topic, value=json.dumps(data[0]).encode())
producer.flush()
# JSONDecoder will return a ValueError if a partial JSON object is read
except ValueError as err:
pass
data = buf.encode().strip()
producer.send(topic=opts.kafka_topic, value=data)
producer.flush()
# All other errors should interrupt the processing
except Exception as ex:

View File

@ -426,6 +426,12 @@ createInstance(SERVICE *service, char **options)
inst->block_size = config_get_integer(params, "block_size");
MXS_CONFIG_PARAMETER *param = config_get_param(params, "source");
inst->gtid.domain = 0;
inst->gtid.event_num = 0;
inst->gtid.seq = 0;
inst->gtid.server_id = 0;
inst->gtid.timestamp = 0;
memset(&inst->active_maps, 0, sizeof(inst->active_maps));
bool err = false;
if (param)
@ -719,11 +725,9 @@ static void freeSession(MXS_ROUTER* router_instance, MXS_ROUTER_SESSION* router_
{
AVRO_INSTANCE *router = (AVRO_INSTANCE *) router_instance;
AVRO_CLIENT *client = (AVRO_CLIENT *) router_client_ses;
int prev_val;
prev_val = atomic_add(&router->stats.n_clients, -1);
ss_debug(int prev_val = )atomic_add(&router->stats.n_clients, -1);
ss_dassert(prev_val > 0);
(void) prev_val;
free(client->uuid);
maxavro_file_close(client->file_handle);
@ -778,9 +782,6 @@ static void closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_sessio
spinlock_release(&client->file_lock);
spinlock_release(&client->catch_lock);
/* decrease server registered slaves counter */
atomic_add(&router->stats.n_clients, -1);
}
/**

View File

@ -114,6 +114,7 @@ AVRO_TABLE* avro_table_alloc(const char* filepath, const char* json_schema, cons
&table->avro_schema))
{
MXS_ERROR("Avro error: %s", avro_strerror());
MXS_INFO("Avro schema: %s", json_schema);
MXS_FREE(table);
return NULL;
}

View File

@ -140,6 +140,7 @@ void avro_index_file(AVRO_INSTANCE *router, const char* filename)
errmsg = NULL;
prev_gtid = gtid;
}
json_decref(row);
}
else
{
@ -205,7 +206,7 @@ void avro_update_index(AVRO_INSTANCE* router)
/** The SQL for the in-memory used_tables table */
static const char *insert_sql = "INSERT OR IGNORE INTO "MEMORY_TABLE_NAME
"(domain, server_id, sequence, binlog_timestamp, table_name)"
" VALUES (%lu, %lu, %lu, %lu, \"%s\")";
" VALUES (%lu, %lu, %lu, %u, \"%s\")";
/**
* @brief Add a used table to the current transaction

View File

@ -102,16 +102,16 @@ char* json_new_schema_from_table(TABLE_MAP *map)
json_object_set_new(schema, "name", json_string("ChangeRecord"));
json_t *array = json_array();
json_array_append(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_domain, "type", "int"));
json_array_append(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_server_id, "type", "int"));
json_array_append(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_sequence, "type", "int"));
json_array_append(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_event_number, "type", "int"));
json_array_append(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_timestamp, "type", "int"));
json_array_append_new(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_domain, "type", "int"));
json_array_append_new(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_server_id, "type", "int"));
json_array_append_new(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_sequence, "type", "int"));
json_array_append_new(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_event_number, "type", "int"));
json_array_append_new(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_timestamp, "type", "int"));
/** Enums and other complex types are defined with complete JSON objects
* instead of string values */
@ -119,16 +119,19 @@ char* json_new_schema_from_table(TABLE_MAP *map)
"name", "EVENT_TYPES", "symbols", "insert",
"update_before", "update_after", "delete");
json_array_append(array, json_pack_ex(&err, 0, "{s:s, s:o}", "name", avro_event_type,
"type", event_types));
// Ownership of `event_types` is stolen when using the `o` format
json_array_append_new(array, json_pack_ex(&err, 0, "{s:s, s:o}", "name", avro_event_type,
"type", event_types));
for (uint64_t i = 0; i < map->columns; i++)
{
json_array_append(array, json_pack_ex(&err, 0, "{s:s, s:s, s:s, s:i}",
"name", create->column_names[i],
"type", column_type_to_avro_type(map->column_types[i]),
"real_type", create->column_types[i],
"length", create->column_lengths[i]));
ss_info_dassert(create->column_names[i] && *create->column_names[i],
"Column name should not be empty or NULL");
json_array_append_new(array, json_pack_ex(&err, 0, "{s:s, s:s, s:s, s:i}",
"name", create->column_names[i],
"type", column_type_to_avro_type(map->column_types[i]),
"real_type", create->column_types[i],
"length", create->column_lengths[i]));
}
json_object_set_new(schema, "fields", array);
char* rval = json_dumps(schema, JSON_PRESERVE_ORDER);
@ -543,6 +546,7 @@ static const char *extract_field_name(const char* ptr, char* dest, size_t size)
dest[bytes] = '\0';
make_valid_avro_identifier(dest);
ss_dassert(strlen(dest) > 0);
}
else
{
@ -555,7 +559,7 @@ static const char *extract_field_name(const char* ptr, char* dest, size_t size)
int extract_type_length(const char* ptr, char *dest)
{
/** Skip any leading whitespace */
while (isspace(*ptr) || *ptr == '`')
while (*ptr && (isspace(*ptr) || *ptr == '`'))
{
ptr++;
}
@ -565,7 +569,7 @@ int extract_type_length(const char* ptr, char *dest)
/** Skip characters until we either hit a whitespace character or the start
* of the length definition. */
while (!isspace(*ptr) && *ptr != '(')
while (*ptr && !isspace(*ptr) && *ptr != '(')
{
ptr++;
}
@ -576,7 +580,7 @@ int extract_type_length(const char* ptr, char *dest)
dest[typelen] = '\0';
/** Skip whitespace */
while (isspace(*ptr))
while (*ptr && isspace(*ptr))
{
ptr++;
}
@ -641,6 +645,7 @@ static int process_column_definition(const char *nameptr, char*** dest, char***
lengths[i] = len;
types[i] = MXS_STRDUP_A(type);
names[i] = MXS_STRDUP_A(colname);
ss_info_dassert(*names[i] && *types[i], "`name` and `type` must not be empty");
i++;
}

View File

@ -1575,8 +1575,8 @@ struct subcommand alteroptions[] =
"\n"
"address Server address\n"
"port Server port\n"
"monuser Monitor user for this server\n"
"monpw Monitor password for this server\n"
"monitoruser Monitor user for this server\n"
"monitorpw Monitor password for this server\n"
"ssl Enable SSL, value must be 'required'\n"
"ssl_key Path to SSL private key\n"
"ssl_cert Path to SSL certificate\n"