Merge branch '2.1' into develop

This commit is contained in:
Markus Mäkelä
2017-06-05 13:25:27 +03:00
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. 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. Release 2.1.3 is a GA release.

View File

@ -36,6 +36,7 @@
#include <errno.h> #include <errno.h>
#include <getopt.h> #include <getopt.h>
#include <stdbool.h> #include <stdbool.h>
#include <pwd.h>
#include <maxscale/version.h> #include <maxscale/version.h>
@ -253,6 +254,11 @@ main(int argc, char **argv)
if ((so = connectUsingInetSocket(hostname, port, user, passwd)) == -1) 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); exit(EXIT_FAILURE);
} }
} }
@ -597,7 +603,13 @@ authUnixSocket(int so)
if (!authenticated) 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; 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) add_test_executable(bug681.cpp bug681 galera.bug681 LABELS readwritesplit GALERA_BACKEND)
# Regression case for the bug "crash with tee filter" # 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" # Regression case for the bug ""Different error messages from MariaDB and Maxscale"
add_test_script(bug561.sh bug561.sh replication LABELS MySQLAuth REPL_BACKEND) 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) add_test_executable(bug634.cpp bug634 replication LABELS readwritesplit REPL_BACKEND)
# Regression cases for several TEE filter hangs # Regression cases for several TEE filter hangs
add_test_executable(bug645.cpp bug645 bug645 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(bug645_1.cpp bug645_1 bug645_1 LABELS tee REPL_BACKEND)
add_test_executable(bug649.cpp bug649 bug645 LABELS tee) #add_test_executable(bug649.cpp bug649 bug645 LABELS tee)
add_test_executable(bug650.cpp bug650 bug650 LABELS tee REPL_BACKEND) #add_test_executable(bug650.cpp bug650 bug650 LABELS tee REPL_BACKEND)
# Heavy test for TEE filter # 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 # 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) 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) 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" # 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 # Block backends (master or all slaves) and tries to connect Maxscale
add_test_executable(bug658.cpp bug658 replication LABELS readwritesplit readconnroute maxscale REPL_BACKEND) 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) add_test_executable(bug664.cpp bug664 bug664 LABELS MySQLAuth MySQLProtocol)
# TEE fileter: execute long sequence of queries ans session commands in the loop # 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" # 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) 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) add_test_executable(mm_mysqlmon.cpp mm_mysqlmon mm_mysqlmon LABELS mysqlmon REPL_BACKEND BREAKS_REPL)
# MySQL Monitor crash safety # 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" # 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) 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) 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" # 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 # 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) 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 # Do only SELECTS during time > wait_timeout and then do INSERT
# This test will fail because the functionality hasn't been implemented # 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 # 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) 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 # Test replication-manager with MaxScale
add_test_executable(replication_manager.cpp replication_manager replication_manager LABELS maxscale REPL_BACKEND) 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_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_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 # 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) 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 # 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) add_test_script(ssl sql_queries ssl LABELS maxscale readwritesplit REPL_BACKEND)
# Check load balancing, client ssl is ON # 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 # 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) 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) add_test_executable(avro.cpp avro avro LABELS avrorouter binlogrouter LIGHT BREAKS_REPL)
# Test avrorouter file compression # 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 # 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) 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) #add_test_executable(bad_pers.cpp bad_pers bad_pers LABELS REPL_BACKEND)
# Test Aurora RDS monitor # 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 # Disabled for the time being
# add_test_executable(gatekeeper.cpp gatekeeper gatekeeper LABELS gatekeeper) # add_test_executable(gatekeeper.cpp gatekeeper gatekeeper LABELS gatekeeper)

View File

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

View File

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

View File

@ -25,6 +25,7 @@ TestConnections * Test ;
int exit_flag; int exit_flag;
int master = 0; int master = 0;
int i_trans = 0; int i_trans = 0;
const int trans_max = 100;
int failed_transaction_num = 0; int failed_transaction_num = 0;
/** The amount of rows each transaction inserts */ /** 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)); Test->add_result(mysql_errno(conn), "Error connecting to Binlog router, error: %s\n", mysql_error(conn));
create_t1(conn); create_t1(conn);
while ((exit_flag == 0)) while ((exit_flag == 0) && i_trans < trans_max)
{ {
Test->tprintf("Transaction %d\n", i_trans); Test->tprintf("Transaction %d\n", i_trans);
trans_result = transaction(conn, 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->set_timeout(30);
Test->tprintf("Trying some queries, expecting failure, but not a crash\n"); 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, "DROP TABLE IF EXISTS t1");
execute_query(Test->conn_rwsplit, (char *) "CREATE TABLE t1 (x INT)"); execute_query(Test->conn_rwsplit, "CREATE TABLE t1 (x INT)");
execute_query(Test->conn_rwsplit, (char *) "INSERT INTO t1 (x) VALUES (1)"); execute_query(Test->conn_rwsplit, "INSERT INTO t1 (x) VALUES (1)");
execute_query(Test->conn_rwsplit, (char *) "select * from t1"); execute_query(Test->conn_rwsplit, "select * from t1");
execute_query(Test->conn_master, (char *) "select * from t1"); execute_query(Test->conn_master, "select * from t1");
execute_query(Test->conn_slave, (char *) "select * from t1"); execute_query(Test->conn_slave, "select * from t1");
Test->set_timeout(10); Test->set_timeout(10);
Test->close_maxscale_connections(); Test->close_maxscale_connections();
@ -58,12 +58,8 @@ int main(int argc, char *argv[])
Test->stop_timeout(); Test->stop_timeout();
sleep(15); sleep(15);
Test->check_log_err((char *) "fatal signal 11", false); Test->check_log_err("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("Failed to create new router session for service", 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);
int rval = Test->global_result; int rval = Test->global_result;
delete Test; 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" * - 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 <iostream>
#include "testconnections.h" #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->add_result(1, "FAIL: Query to broken service succeeded!\n");
} }
Test->close_maxscale_connections(); 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; int rval = Test->global_result;
delete Test; delete Test;

View File

@ -124,6 +124,11 @@ int main(int argc, char *argv[])
Test->try_query(Test->conn_rwsplit, (char *) "show processlist;"); Test->try_query(Test->conn_rwsplit, (char *) "show processlist;");
Test->close_rwsplit(); 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; int rval = Test->global_result;
delete Test; delete Test;
return rval; return rval;

View File

@ -40,42 +40,6 @@ service=RW Split Router
* - Reconnect readconnrouter * - 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 <iostream>
#include "testconnections.h" #include "testconnections.h"
#include "sql_t1.h" #include "sql_t1.h"

View File

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

View File

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

View File

@ -38,7 +38,9 @@ public:
bool operator ==(const TestOutput& output) const 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 bool operator !=(const TestOutput& output) const

View File

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

View File

@ -101,3 +101,13 @@ type=server
address=###node_server_IP_4### address=###node_server_IP_4###
port=###node_server_port_4### port=###node_server_port_4###
protocol=MySQLBackend 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### address=###node_server_IP_4###
port=###node_server_port_4### port=###node_server_port_4###
protocol=MySQLBackend protocol=MySQLBackend

View File

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

View File

@ -36,7 +36,7 @@ int main(int argc, char *argv[])
sprintf(str, "rules%d", i); sprintf(str, "rules%d", i);
Test->set_timeout(180); Test->set_timeout(180);
copy_rules(Test, str, rules_dir); 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; int local_result = 0;
sprintf(pass_file, "%s/fw/pass%d", test_dir, i); 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_RES *res;
MYSQL_ROW row; MYSQL_ROW row;
unsigned long long int num_fields; unsigned long long int num_fields;
//unsigned long long int row_i=0;
unsigned long long int rows; unsigned long long int rows;
unsigned long long int i; unsigned long long int i;
unsigned int conn_num = 0; unsigned int conn_num = 0;
char * hostname_internal; const char * hostname_internal;
if (strcmp(ip, "127.0.0.1") == 0) if (strcmp(ip, "127.0.0.1") == 0)
{ {
hostname_internal = (char*) "localhost"; hostname_internal = "localhost";
} }
else 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) 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 // 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--; conn_num--;
} }
return conn_num; return conn_num;

View File

@ -18,13 +18,22 @@ function(add_java_test name src entry_point template)
endfunction() endfunction()
# Some constants that make changing the connector easier # 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(MXS_JAR ${CMAKE_CURRENT_BINARY_DIR}/maxscale_java.jar CACHE INTERNAL "")
set(TEST_JARPATH "${MXS_JAR}:${JDBC_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) 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 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(test1)
add_subdirectory(prep_stmt) add_subdirectory(prep_stmt)
add_subdirectory(batch) 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) 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) 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"); test->ssh_maxscale(true, "cp /etc/maxscale.cnf.backup /etc/maxscale.cnf");
/** Set router_options to a bad value */ /** 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"); // Disabled for 2.0
test->add_result(baseline == test->ssh_maxscale(true, "maxscale -c --user=maxscale"), //test->ssh_maxscale(true, "sed -i -e 's/router_options.*/router_options=bad_option=true/' /etc/maxscale.cnf");
"Bad router_options should be detected.\n"); //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"); 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->tprintf("Encrypting password: %s", pass);
Test->set_timeout(30); 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/user=.*/user=test/' /etc/maxscale.cnf && "
"sed -i \"s/passwd=.*/passwd=$(cat /tmp/pw.txt)/\" /etc/maxscale.cnf && " "sed -i \"s/passwd=.*/passwd=$(cat /tmp/pw.txt)/\" /etc/maxscale.cnf && "
"service maxscale restart && " "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->tprintf("Connecting to RWSplit %s\n", Test->maxscale_IP);
Test->connect_rwsplit(); 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); 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*) "select 1;");
Test->try_query(Test->routers[j], (char*) "set autocommit=1;"); Test->try_query(Test->routers[j], (char*) "set autocommit=1;");
Test->try_query(Test->routers[j], (char*) "select 2;"); 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); Test->tprintf("i=%d\n", i);
} }

View File

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

View File

@ -954,6 +954,44 @@ int TestConnections::start_binlog()
return global_result; 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 TestConnections::start_mm()
{ {
int i; int i;

View File

@ -442,6 +442,11 @@ public:
*/ */
int start_binlog(); 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 * @brief prepare_binlog clean up binlog directory, set proper access rights to it
* @return 0 * @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* pValue = json_object_get(pWith, KEY_VALUE);
json_t* pFill = json_object_get(pWith, KEY_FILL); json_t* pFill = json_object_get(pWith, KEY_FILL);
if ((pValue || pFill) && if (!pFill)
(!pValue || json_is_string(pValue)) &&
(!pFill || json_is_string(pFill)))
{ {
sRule = create_rule_from_elements(pColumn, pTable, pDatabase, // Allowed. Use default value for fill and add it to pWith.
pValue, pFill, pFill = json_string("X");
pApplies_to, pExempted); 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' " if ((!pValue || (json_is_string(pValue) && json_string_length(pValue))) &&
"or '%s' as keys, or their values are not strings.", (json_is_string(pFill) && json_string_length(pFill)))
KEY_WITH, KEY_VALUE, KEY_FILL); {
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 else

View File

@ -16,7 +16,6 @@
# pip install kafka-python # pip install kafka-python
# #
import json
import sys import sys
import argparse import argparse
from kafka import KafkaProducer from kafka import KafkaProducer
@ -30,8 +29,6 @@ parser.add_argument("-T", "--kafka-topic", dest="kafka_topic",
default=None, required=True) default=None, required=True)
opts = parser.parse_args(sys.argv[1:]) opts = parser.parse_args(sys.argv[1:])
decoder = json.JSONDecoder()
rbuf = bytes()
producer = KafkaProducer(bootstrap_servers=[opts.kafka_broker]) producer = KafkaProducer(bootstrap_servers=[opts.kafka_broker])
while True: while True:
@ -41,18 +38,9 @@ while True:
if len(buf) == 0: if len(buf) == 0:
break break
rbuf += buf.encode() data = buf.encode().strip()
producer.send(topic=opts.kafka_topic, value=data)
while True: producer.flush()
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
# All other errors should interrupt the processing # All other errors should interrupt the processing
except Exception as ex: except Exception as ex:

View File

@ -426,6 +426,12 @@ createInstance(SERVICE *service, char **options)
inst->block_size = config_get_integer(params, "block_size"); inst->block_size = config_get_integer(params, "block_size");
MXS_CONFIG_PARAMETER *param = config_get_param(params, "source"); 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; bool err = false;
if (param) 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_INSTANCE *router = (AVRO_INSTANCE *) router_instance;
AVRO_CLIENT *client = (AVRO_CLIENT *) router_client_ses; 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); ss_dassert(prev_val > 0);
(void) prev_val;
free(client->uuid); free(client->uuid);
maxavro_file_close(client->file_handle); 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->file_lock);
spinlock_release(&client->catch_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)) &table->avro_schema))
{ {
MXS_ERROR("Avro error: %s", avro_strerror()); MXS_ERROR("Avro error: %s", avro_strerror());
MXS_INFO("Avro schema: %s", json_schema);
MXS_FREE(table); MXS_FREE(table);
return NULL; return NULL;
} }

View File

@ -140,6 +140,7 @@ void avro_index_file(AVRO_INSTANCE *router, const char* filename)
errmsg = NULL; errmsg = NULL;
prev_gtid = gtid; prev_gtid = gtid;
} }
json_decref(row);
} }
else else
{ {
@ -205,7 +206,7 @@ void avro_update_index(AVRO_INSTANCE* router)
/** The SQL for the in-memory used_tables table */ /** The SQL for the in-memory used_tables table */
static const char *insert_sql = "INSERT OR IGNORE INTO "MEMORY_TABLE_NAME static const char *insert_sql = "INSERT OR IGNORE INTO "MEMORY_TABLE_NAME
"(domain, server_id, sequence, binlog_timestamp, 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 * @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_object_set_new(schema, "name", json_string("ChangeRecord"));
json_t *array = json_array(); json_t *array = json_array();
json_array_append(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name", json_array_append_new(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_domain, "type", "int")); avro_domain, "type", "int"));
json_array_append(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name", json_array_append_new(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_server_id, "type", "int")); avro_server_id, "type", "int"));
json_array_append(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name", json_array_append_new(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_sequence, "type", "int")); avro_sequence, "type", "int"));
json_array_append(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name", json_array_append_new(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_event_number, "type", "int")); avro_event_number, "type", "int"));
json_array_append(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name", json_array_append_new(array, json_pack_ex(&err, 0, "{s:s, s:s}", "name",
avro_timestamp, "type", "int")); avro_timestamp, "type", "int"));
/** Enums and other complex types are defined with complete JSON objects /** Enums and other complex types are defined with complete JSON objects
* instead of string values */ * instead of string values */
@ -119,16 +119,19 @@ char* json_new_schema_from_table(TABLE_MAP *map)
"name", "EVENT_TYPES", "symbols", "insert", "name", "EVENT_TYPES", "symbols", "insert",
"update_before", "update_after", "delete"); "update_before", "update_after", "delete");
json_array_append(array, json_pack_ex(&err, 0, "{s:s, s:o}", "name", avro_event_type, // Ownership of `event_types` is stolen when using the `o` format
"type", event_types)); 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++) 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}", ss_info_dassert(create->column_names[i] && *create->column_names[i],
"name", create->column_names[i], "Column name should not be empty or NULL");
"type", column_type_to_avro_type(map->column_types[i]), json_array_append_new(array, json_pack_ex(&err, 0, "{s:s, s:s, s:s, s:i}",
"real_type", create->column_types[i], "name", create->column_names[i],
"length", create->column_lengths[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); json_object_set_new(schema, "fields", array);
char* rval = json_dumps(schema, JSON_PRESERVE_ORDER); 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'; dest[bytes] = '\0';
make_valid_avro_identifier(dest); make_valid_avro_identifier(dest);
ss_dassert(strlen(dest) > 0);
} }
else 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) int extract_type_length(const char* ptr, char *dest)
{ {
/** Skip any leading whitespace */ /** Skip any leading whitespace */
while (isspace(*ptr) || *ptr == '`') while (*ptr && (isspace(*ptr) || *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 /** Skip characters until we either hit a whitespace character or the start
* of the length definition. */ * of the length definition. */
while (!isspace(*ptr) && *ptr != '(') while (*ptr && !isspace(*ptr) && *ptr != '(')
{ {
ptr++; ptr++;
} }
@ -576,7 +580,7 @@ int extract_type_length(const char* ptr, char *dest)
dest[typelen] = '\0'; dest[typelen] = '\0';
/** Skip whitespace */ /** Skip whitespace */
while (isspace(*ptr)) while (*ptr && isspace(*ptr))
{ {
ptr++; ptr++;
} }
@ -641,6 +645,7 @@ static int process_column_definition(const char *nameptr, char*** dest, char***
lengths[i] = len; lengths[i] = len;
types[i] = MXS_STRDUP_A(type); types[i] = MXS_STRDUP_A(type);
names[i] = MXS_STRDUP_A(colname); names[i] = MXS_STRDUP_A(colname);
ss_info_dassert(*names[i] && *types[i], "`name` and `type` must not be empty");
i++; i++;
} }

View File

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