MXS-497: Multi-statement queries are always routed to the master

This will prevent the routing of queries that modify data to the slaves.

In the future a more intricate solution can done where all the statements
are parsed and the destination is resolved based on the actual contents.
This commit is contained in:
Markus Makela 2016-02-18 00:17:46 +02:00
parent b88d66357e
commit ac007fa8f5
3 changed files with 38 additions and 20 deletions

View File

@ -67,5 +67,6 @@ GWBUF* modutil_create_mysql_err_msg(int packet_number,
const char *msg);
int modutil_count_signal_packets(GWBUF*,int,int,int*);
mxs_pcre2_result_t modutil_mysql_wildcard_match(const char* pattern, const char* string);
void* strnchr_esc(char* ptr, char c, int len);
#endif

View File

@ -298,6 +298,7 @@ struct router_client_session {
uint64_t rses_load_data_sent; /*< How much data has been sent */
DCB* client_dcb;
int pos_generator;
backend_ref_t *forced_node; /*< Current server where all queries should be sent */
#if defined(PREP_STMT_CACHING)
HASHTABLE* rses_prep_stmt[2];
#endif

View File

@ -102,13 +102,6 @@ static int rses_get_max_replication_lag(ROUTER_CLIENT_SES* rses);
static backend_ref_t* get_bref_from_dcb(ROUTER_CLIENT_SES* rses, DCB* dcb);
static DCB* rses_get_client_dcb(ROUTER_CLIENT_SES* rses);
static route_target_t get_route_target (
qc_query_type_t qtype,
bool trx_active,
bool load_active,
target_t use_sql_variables_in,
HINT* hint);
static backend_ref_t* check_candidate_bref(
backend_ref_t* candidate_bref,
backend_ref_t* new_bref,
@ -834,6 +827,7 @@ static void* newSession(
client_rses->rses_autocommit_enabled = true;
client_rses->rses_transaction_active = false;
client_rses->have_tmp_tables = false;
client_rses->forced_node = NULL;
router_nservers = router_get_servercount(router);
@ -1382,18 +1376,22 @@ static backend_ref_t* check_candidate_bref(
* @return bitfield including the routing target, or the target server name
* if the query would otherwise be routed to slave.
*/
static route_target_t get_route_target (
qc_query_type_t qtype,
bool trx_active,
bool load_active,
target_t use_sql_variables_in,
HINT* hint)
static route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
qc_query_type_t qtype, HINT *hint)
{
route_target_t target = TARGET_UNDEFINED;
bool trx_active = rses->rses_transaction_active;
bool load_active = rses->rses_load_active;
target_t use_sql_variables_in = rses->rses_config.rw_use_sql_variables_in;
route_target_t target = TARGET_UNDEFINED;
if (rses->forced_node == rses->rses_master_ref)
{
target = TARGET_MASTER;
}
/**
* These queries are not affected by hints
*/
if (!load_active && (QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE) ||
else if (!load_active && (QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE) ||
/** Configured to allow writing variables to all nodes */
(use_sql_variables_in == TYPE_ALL &&
QUERY_IS_TYPE(qtype, QUERY_TYPE_GSYSVAR_WRITE)) ||
@ -2150,7 +2148,29 @@ static bool route_single_stmt(
{
succp = false;
goto retblock;
}
/** Check for multi-statement queries. We assume here that the client
* protocol is MySQLClient.
* TODO: add warnings when incompatible protocols are used */
MySQLProtocol *proto = (MySQLProtocol*)rses->client_dcb->protocol;
if (proto->client_capabilities & GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS &&
packet_type == MYSQL_COM_QUERY && rses->forced_node != rses->rses_master_ref)
{
for (GWBUF *buf = querybuf; buf != NULL; buf = buf->next)
{
if (strnchr_esc(GWBUF_DATA(buf), ';', GWBUF_LENGTH(buf)))
{
/** It is possible that the session state is modified inside
* the multi-statement query which would leave any slave sessions
* in an inconsistent state. Due to this, for the duration of
* this session, all queries will be sent to the master. */
rses->forced_node = rses->rses_master_ref;
MXS_INFO("Multi-statement query, routing all future queries to master.");
}
}
}
/**
* Check if the query has anything to do with temporary tables.
*/
@ -2269,11 +2289,7 @@ static bool route_single_stmt(
* - route primarily according to the hints and if they failed,
* eventually to master
*/
route_target = get_route_target(qtype,
rses->rses_transaction_active,
rses->rses_load_active,
rses->rses_config.rw_use_sql_variables_in,
querybuf->hint);
route_target = get_route_target(rses, qtype, querybuf->hint);
if (TARGET_IS_ALL(route_target))
{