MXS-1516: Validate the connection on each query

A subset of the checks done at connection creation time need to be done at
query routing time. This guarantees that the connection is closed if the
server no longer qualifies as a valid candidate.

Added teset case that checks that a change in the replication topology
correctly breaks the connection.
This commit is contained in:
Markus Mäkelä 2018-01-02 11:00:56 +02:00
parent 3f0ef7481e
commit bacc11d28e
3 changed files with 68 additions and 3 deletions

View File

@ -514,6 +514,10 @@ add_test_executable(mxs1476.cpp mxs1476 mxs1476 LABELS GALERA_BACKEND)
# https://jira.mariadb.org/browse/MXS-1509
add_test_executable(mxs1509.cpp mxs1509 mxs1509 LABELS REPL_BACKEND)
# MXS-1516: existing connection don't change routing, even if master switched
# https://jira.mariadb.org/browse/MXS-1516
add_test_executable(mxs1516.cpp mxs1516 replication LABELS REPL_BACKEND)
# MXS-1585: Crash in MaxScale 2.1.12
# https://jira.mariadb.org/browse/MXS-1585
add_test_executable(mxs1585.cpp mxs1585 mxs1585 LABELS REPL_BACKEND)

View File

@ -0,0 +1,26 @@
/**
* MXS-1516: existing connection don't change routing, even if master switched
*
* https://jira.mariadb.org/browse/MXS-1516
*/
#include "testconnections.h"
int main(int argc, char** argv)
{
TestConnections test(argc, argv);
test.connect_maxscale();
test.try_query(test.conn_master, "SELECT 1");
// Change master mid-session
test.repl->connect();
test.repl->change_master(1, 0);
test.add_result(execute_query_silent(test.conn_master, "SELECT 1") == 0, "Query should fail");
// Change the master back to the original one
test.repl->change_master(0, 1);
return test.global_result;
}

View File

@ -510,7 +510,7 @@ closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session)
/** Log routing failure due to closed session */
static void log_closed_session(mysql_server_cmd_t mysql_command, bool is_closed,
SERVER_REF *ref)
SERVER_REF *ref, bool valid)
{
char msg[MAX_SERVER_NAME_LEN + 200] = ""; // Extra space for message
@ -526,11 +526,44 @@ static void log_closed_session(mysql_server_cmd_t mysql_command, bool is_closed,
{
sprintf(msg, "Server '%s' is in maintenance.", ref->server->unique_name);
}
else if (!valid)
{
sprintf(msg, "Server '%s' no longer qualifies as a target server.",
ref->server->unique_name);
}
MXS_ERROR("Failed to route MySQL command %d to backend server. %s",
mysql_command, msg);
}
/**
* Check if the server we're connected to is still valid
*
* @param inst Router instance
* @param router_cli_ses Router session
*
* @return True if the backend connection is still valid
*/
static inline bool connection_is_valid(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* router_cli_ses)
{
bool rval = false;
if (SERVER_IS_RUNNING(router_cli_ses->backend->server) &&
(router_cli_ses->backend->server->status & inst->bitmask & inst->bitvalue))
{
if (inst->bitvalue & SERVER_MASTER)
{
rval = router_cli_ses->backend == get_root_master(inst->service->dbref);
}
else
{
rval = true;
}
}
return rval;
}
/**
* We have data from the client, we must route it to the backend.
* This is simply a case of sending it to the connection that was
@ -574,10 +607,12 @@ routeQuery(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queu
rses_end_locked_router_action(router_cli_ses);
}
bool valid;
if (rses_is_closed || backend_dcb == NULL ||
!SERVER_IS_RUNNING(router_cli_ses->backend->server))
(valid = !connection_is_valid(inst, router_cli_ses)))
{
log_closed_session(mysql_command, rses_is_closed, router_cli_ses->backend);
log_closed_session(mysql_command, rses_is_closed, router_cli_ses->backend, valid);
gwbuf_free(queue);
goto return_rc;