Merge branch 'develop' into binlog_server_wait_data

This commit is contained in:
MassimilianoPinto 2016-11-16 15:08:17 +01:00
commit cd31b2c2c3
32 changed files with 1454 additions and 402 deletions

View File

@ -173,8 +173,11 @@ where,
* the _op_ can be `=`, `!=`, `like` or `unlike`, and
* the _value_ a string.
If _op_ is `=` or `!=` then _value_ is used verbatim; if it is `like`
If _op_ is `=` or `!=` then _value_ is used as a string; if it is `like`
or `unlike`, then _value_ is interpreted as a _pcre2_ regular expression.
Note though that if _attribute_ is `database`, `table` or `column`, then
the string is interpreted as a name, where a dot `.` denotes qualification
or scoping.
The objects in the `store` array are processed in order. If the result
of a comparison is _true_, no further processing will be made and the
@ -206,6 +209,39 @@ select * from tbl where b = 3 and a = 2;
as well. Although they conceptually are identical, there will be two
cache entries.
### Qualified Names
When using `=` or `!=` in the rule object in conjunction with `database`,
`table` and `column`, the provided string is interpreted as a name, that is,
dot (`.`) denotes qualification or scope.
In practice that means that if _attribute_ is `database` then _value_ may
not contain a dot, if _attribute_ is `table` then _value_ may contain one
dot, used for separating the database and table names respectively, and
if _attribute_ is `column` then _value_ may contain one or two dots, used
for separating table and column names, or database, table and column names.
Note that if a qualified name is used as a _value_, then all parts of the
name must be available for a match. Currently Maria DB MaxScale may not
always be capable of deducing in what table a particular column is. If
that is the case, then a value like `tbl.field` may not necessarily
be a match even if the field is `field` and the table actually is `tbl`.
### Implication of the _default_ database.
If the rules concerns the `database`, then only if the statement refers
to *no* specific database, will the default database be considered.
### Regexp Matching
The string used for matching the regular expression contains as much
information as there is available. For instance, in a situation like
```
use somedb;
select fld from tbl;
```
the string matched against the regular expression will be `somedb.tbl.fld`.
### Examples
Cache all queries targeting a particular database.

View File

@ -233,6 +233,15 @@ connect_result_t mon_connect_to_db(MONITOR* mon, MONITOR_SERVERS *database);
void mon_log_connect_error(MONITOR_SERVERS* database, connect_result_t rval);
void mon_log_state_change(MONITOR_SERVERS *ptr);
/**
* @brief Hangup connections to failed servers
*
* Injects hangup events for DCB that are connected to servers that are down.
*
* @param monitor Monitor object
*/
void mon_hangup_failed_servers(MONITOR *monitor);
/**
* @brief Serialize a monitor to a file
*

View File

@ -115,6 +115,7 @@ typedef struct server
long persistpoolmax; /**< Maximum size of persistent connections pool */
long persistmaxtime; /**< Maximum number of seconds connection can live */
int persistmax; /**< Maximum pool size actually achieved since startup */
uint8_t charset; /**< Default server character set */
bool is_active; /**< Server is active and has not been "destroyed" */
#if defined(SS_DEBUG)
skygw_chk_t server_chk_tail;

View File

@ -97,7 +97,7 @@ static parsing_info_t* parsing_info_init(void (*donefun)(void *));
static void parsing_info_set_plain_str(void* ptr, char* str);
/** Free THD context and close MYSQL */
static void parsing_info_done(void* ptr);
static void* skygw_get_affected_tables(void* lexptr);
static TABLE_LIST* skygw_get_affected_tables(void* lexptr);
static bool ensure_query_is_parsed(GWBUF* query);
static bool parse_query(GWBUF* querybuf);
static bool query_is_parsed(GWBUF* buf);
@ -1090,7 +1090,7 @@ LEX* get_lex(GWBUF* querybuf)
* @param thd Pointer to a valid THD
* @return Pointer to the head of the TABLE_LIST chain or NULL in case of an error
*/
static void* skygw_get_affected_tables(void* lexptr)
static TABLE_LIST* skygw_get_affected_tables(void* lexptr)
{
LEX* lex = (LEX*) lexptr;
@ -1100,7 +1100,23 @@ static void* skygw_get_affected_tables(void* lexptr)
return NULL;
}
return (void*) lex->current_select->table_list.first;
TABLE_LIST *tbl = lex->current_select->table_list.first;
if (tbl && tbl->schema_select_lex && tbl->schema_select_lex->table_list.elements &&
lex->sql_command != SQLCOM_SHOW_KEYS)
{
/**
* Some statements e.g. EXPLAIN or SHOW COLUMNS give `information_schema`
* as the underlying table and the table in the query is stored in
* @c schema_select_lex.
*
* SHOW [KEYS | INDEX] does the reverse so we need to skip the
* @c schema_select_lex when processing a SHOW [KEYS | INDEX] statement.
*/
tbl = tbl->schema_select_lex->table_list.first;
}
return tbl;
}
char** qc_get_table_names(GWBUF* querybuf, int* tblsize, bool fullnames)
@ -1129,7 +1145,7 @@ char** qc_get_table_names(GWBUF* querybuf, int* tblsize, bool fullnames)
while (lex->current_select)
{
tbl = (TABLE_LIST*) skygw_get_affected_tables(lex);
tbl = skygw_get_affected_tables(lex);
while (tbl)
{

View File

@ -2417,7 +2417,7 @@ extern void maxscaleShow(Parse* pParse, MxsShow* pShow)
case MXS_SHOW_COLUMNS:
{
info->types = QUERY_TYPE_READ;
update_names(info, "information_schema", "COLUMNS");
update_names(info, zDatabase, zName);
if (pShow->data == MXS_SHOW_COLUMNS_FULL)
{
update_field_info(info, "information_schema", "COLUMNS", "COLLATION_NAME", u, NULL);

View File

@ -3022,13 +3022,17 @@ like_or_where_opt ::= WHERE expr.
%type show {MxsShow}
show(A) ::= SHOW full_opt(X) COLUMNS from_or_in nm(Y) dbnm(Z) from_or_in_db_opt like_or_where_opt . {
show(A) ::= SHOW full_opt(X) COLUMNS from_or_in nm(Y) dbnm(Z) from_or_in_db_opt(W) like_or_where_opt . {
A.what = MXS_SHOW_COLUMNS;
A.data = X;
if (Z.z) {
A.pName = &Z;
A.pDatabase = &Y;
}
else if (W.z) {
A.pName = &Y;
A.pDatabase = &W;
}
else {
A.pName = &Y;
A.pDatabase = NULL;

View File

@ -1731,7 +1731,7 @@ process_config_update(CONFIG_CONTEXT *context)
if (connection_timeout)
{
serviceSetTimeout(service, config_truth_value(connection_timeout));
serviceSetTimeout(service, atoi(connection_timeout));
}
if (strlen(max_connections))

View File

@ -1678,6 +1678,38 @@ dcb_grab_writeq(DCB *dcb, bool first_time)
return local_writeq;
}
static void log_illegal_dcb(DCB *dcb)
{
const char *connected_to;
switch (dcb->dcb_role)
{
case DCB_ROLE_BACKEND_HANDLER:
connected_to = dcb->server->unique_name;
break;
case DCB_ROLE_CLIENT_HANDLER:
connected_to = dcb->remote;
break;
case DCB_ROLE_INTERNAL:
connected_to = "Internal DCB";
break;
case DCB_ROLE_SERVICE_LISTENER:
connected_to = dcb->service->name;
break;
default:
connected_to = "Illegal DCB role";
break;
}
MXS_ERROR("[dcb_close] Error : Removing DCB %p but it is in state %s "
"which is not legal for a call to dcb_close. The DCB is connected to: %s",
dcb, STRDCBSTATE(dcb->state), connected_to);
}
/**
* Removes dcb from poll set, and adds it to zombies list. As a consequence,
* dcb first moves to DCB_STATE_NOPOLLING, and then to DCB_STATE_ZOMBIE state.
@ -1697,11 +1729,7 @@ dcb_close(DCB *dcb)
if (DCB_STATE_UNDEFINED == dcb->state
|| DCB_STATE_DISCONNECTED == dcb->state)
{
MXS_ERROR("%lu [dcb_close] Error : Removing DCB %p but was in state %s "
"which is not legal for a call to dcb_close. ",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state));
log_illegal_dcb(dcb);
raise(SIGABRT);
}

View File

@ -349,7 +349,7 @@ void monitorRemoveServer(MONITOR *mon, SERVER *server)
MONITOR_SERVERS *ptr = mon->databases;
if (ptr->server == server)
if (ptr && ptr->server == server)
{
mon->databases = mon->databases->next;
}
@ -800,8 +800,14 @@ mon_get_event_type(MONITOR_SERVERS* node)
}
else
{
/** These are used to detect whether we actually lost something or
* just transitioned from one state to another */
unsigned int prev_bits = prev & (SERVER_MASTER | SERVER_SLAVE);
unsigned int present_bits = present & (SERVER_MASTER | SERVER_SLAVE);
/* Was running and still is */
if (prev & (SERVER_MASTER | SERVER_SLAVE | SERVER_JOINED | SERVER_NDB))
if ((!prev_bits || !present_bits || prev_bits == present_bits) &&
prev & (SERVER_MASTER | SERVER_SLAVE | SERVER_JOINED | SERVER_NDB))
{
/* We used to know what kind of server it was */
event_type = LOSS_EVENT;
@ -1252,3 +1258,16 @@ bool monitor_serialize_servers(const MONITOR *monitor)
return rval;
}
void mon_hangup_failed_servers(MONITOR *monitor)
{
for (MONITOR_SERVERS *ptr = monitor->databases; ptr; ptr = ptr->next)
{
if (mon_status_changed(ptr) &&
(!(SERVER_IS_RUNNING(ptr->server)) ||
!(SERVER_IS_IN_CLUSTER(ptr->server))))
{
dcb_hangup_foreach(ptr->server);
}
}
}

View File

@ -163,8 +163,6 @@ MYSQL *mxs_mysql_real_connect(MYSQL *con, SERVER *server, const char *user, cons
if (listener)
{
GATEWAY_CONF* config = config_get_global_options();
// mysql_ssl_set always returns true.
mysql_ssl_set(con, listener->ssl_key, listener->ssl_cert, listener->ssl_ca_cert, NULL, NULL);
}

View File

@ -51,6 +51,9 @@
#include <maxscale/modules.h>
#include <maxscale/gwdirs.h>
/** The latin1 charset */
#define SERVER_DEFAULT_CHARSET 0x08
static SPINLOCK server_spin = SPINLOCK_INIT;
static SERVER *allServers = NULL;
@ -129,6 +132,7 @@ SERVER* server_alloc(const char *name, const char *address, unsigned short port,
server->monuser[0] = '\0';
server->monpw[0] = '\0';
server->is_active = true;
server->charset = SERVER_DEFAULT_CHARSET;
spinlock_init(&server->persistlock);
spinlock_acquire(&server_spin);

View File

@ -2532,6 +2532,11 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
return my_errno != ER_ACCESS_DENIED_ERROR;
}
/** Copy the server charset */
MY_CHARSET_INFO cs_info;
mysql_get_character_set_info(mysql, &cs_info);
server->charset = cs_info.number;
if (server->server_string == NULL)
{
const char *server_string = mysql_get_server_info(mysql);

File diff suppressed because it is too large Load Diff

View File

@ -48,8 +48,14 @@ typedef struct cache_rule
char *value; // The value from the rule file.
struct
{
pcre2_code* code;
pcre2_match_data* data;
char *database;
char *table;
char *column;
} simple; // Details, only for CACHE_OP_[EQ|NEQ]
struct
{
pcre2_code *code;
pcre2_match_data *data;
} regexp; // Regexp data, only for CACHE_OP_[LIKE|UNLIKE].
uint32_t debug; // The debug level.
struct cache_rule *next;

View File

@ -132,8 +132,46 @@ struct store_test_case
// false: The query should NOT match the rule.
const struct store_test_case store_test_cases[] =
{
STORE_TEST_CASE("column", "=", "a", true, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("column", "=", "b", false, NULL, "SELECT a FROM tbl")
STORE_TEST_CASE("column", "=", "a", true, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("column", "!=", "a", false, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("column", "=", "b", false, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("column", "!=", "b", true, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("column", "=", "tbl.a", true, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("column", "=", "tbl.a", true, NULL, "SELECT tbl.a FROM tbl"),
STORE_TEST_CASE("column", "like", ".*a", true, NULL, "SELECT a from tbl"),
STORE_TEST_CASE("column", "like", ".*a", true, NULL, "SELECT tbl.a from tbl"),
STORE_TEST_CASE("column", "like", ".*a", true, NULL, "SELECT db.tbl.a from tbl"),
STORE_TEST_CASE("column", "like", ".*aa", false, NULL, "SELECT a from tbl"),
STORE_TEST_CASE("column", "like", ".*aa", false, NULL, "SELECT tbl.a from tbl"),
STORE_TEST_CASE("column", "like", ".*aa", false, NULL, "SELECT db.tbl.a from tbl"),
STORE_TEST_CASE("column", "unlike", ".*aa", true, NULL, "SELECT a from tbl"),
STORE_TEST_CASE("column", "unlike", ".*aa", true, NULL, "SELECT tbl.a from tbl"),
STORE_TEST_CASE("column", "unlike", ".*aa", true, NULL, "SELECT db.tbl.a from tbl"),
STORE_TEST_CASE("table", "=", "tbl", true, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("table", "!=", "tbl", false, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("table", "=", "tbl2", false, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("table", "!=", "tbl2", true, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("table", "=", "db.tbl", true, NULL, "SELECT a from db.tbl"),
STORE_TEST_CASE("table", "=", "db.tbl", true, "db", "SELECT a from tbl"),
STORE_TEST_CASE("table", "!=", "db.tbl", false, NULL, "SELECT a from db.tbl"),
STORE_TEST_CASE("table", "!=", "db.tbl", false, "db", "SELECT a from tbl"),
STORE_TEST_CASE("database", "=", "db", false, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("database", "!=", "db", true, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("database", "=", "db1", true, NULL, "SELECT a FROM db1.tbl"),
STORE_TEST_CASE("database", "!=", "db1", false, NULL, "SELECT a FROM db1.tbl"),
STORE_TEST_CASE("database", "=", "db1", true, "db1", "SELECT a FROM tbl"),
STORE_TEST_CASE("database", "!=", "db1", false, "db1", "SELECT a FROM tbl"),
STORE_TEST_CASE("query", "=", "SELECT a FROM tbl", true, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("query", "!=", "SELECT a FROM tbl", false, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("query", "=", "SELECT b FROM tbl", false, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("query", "!=", "SELECT b FROM tbl", true, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("column", "=", "a", false, NULL, "SELECT b FROM tbl WHERE a = 5"),
STORE_TEST_CASE("column", "=", "a", true, NULL, "SELECT a, b FROM tbl WHERE a = 5"),
};
const size_t n_store_test_cases = sizeof(store_test_cases) / sizeof(store_test_cases[0]);
@ -144,6 +182,7 @@ int test_store()
for (int i = 0; i < n_store_test_cases; ++i)
{
printf("TC : %d\n", i + 1);
const struct store_test_case *test_case = &store_test_cases[i];
CACHE_RULES *rules = cache_rules_parse(test_case->rule, 0);
@ -160,10 +199,12 @@ int test_store()
{
printf("Query : %s\n"
"Rule : %s\n"
"Def-db : %s\n"
"Expected: %s\n"
"Result : %s\n\n",
test_case->query,
test_case->rule,
test_case->default_db,
test_case->matches ? "A match" : "Not a match",
matches ? "A match" : "Not a match");
}

View File

@ -535,16 +535,9 @@ monitorMain(void *arg)
STRSRVSTATUS(ptr->server));
}
if (!(SERVER_IS_RUNNING(ptr->server)) ||
!(SERVER_IS_IN_CLUSTER(ptr->server)))
{
dcb_hangup_foreach(ptr->server);
}
if (SERVER_IS_DOWN(ptr->server))
{
/** Increase this server'e error count */
dcb_hangup_foreach(ptr->server);
ptr->mon_err_count += 1;
}
@ -650,6 +643,8 @@ monitorMain(void *arg)
}
ptr = ptr->next;
}
mon_hangup_failed_servers(mon);
}
}

View File

@ -596,15 +596,6 @@ monitorMain(void *arg)
/* monitor current node */
monitorDatabase(mon, ptr);
if (mon_status_changed(ptr))
{
if (!(SERVER_IS_RUNNING(ptr->server)) ||
!(SERVER_IS_IN_CLUSTER(ptr->server)))
{
dcb_hangup_foreach(ptr->server);
}
}
if (mon_status_changed(ptr) ||
mon_print_fail_status(ptr))
{
@ -676,6 +667,8 @@ monitorMain(void *arg)
}
ptr = ptr->next;
}
mon_hangup_failed_servers(mon);
}
}

View File

@ -1210,18 +1210,6 @@ monitorMain(void *arg)
ptr->server->name,
ptr->server->port);
}
/**
* Here we say: If the server's state changed
* so that it isn't running or some other way
* lost cluster membership, call call-back function
* of every DCB for which such callback was
* registered for this kind of issue (DCB_REASON_...)
*/
if (!(SERVER_IS_RUNNING(ptr->server)) ||
!(SERVER_IS_IN_CLUSTER(ptr->server)))
{
dcb_hangup_foreach(ptr->server);
}
}
if (mon_status_changed(ptr))
@ -1483,6 +1471,8 @@ monitorMain(void *arg)
ptr = ptr->next;
}
}
mon_hangup_failed_servers(mon);
} /*< while (1) */
}

View File

@ -444,6 +444,8 @@ monitorMain(void *arg)
}
ptr = ptr->next;
}
mon_hangup_failed_servers(mon);
}
}

View File

@ -1112,6 +1112,12 @@ static int gw_error_backend_event(DCB *dcb)
CHK_SESSION(session);
if (SESSION_STATE_DUMMY == session->state)
{
if (dcb->persistentstart == 0)
{
/** Not a persistent connection, something is wrong. */
MXS_ERROR("EPOLLERR event on a non-persistent DCB with no session. "
"Closing connection.");
}
dcb_close(dcb);
return 1;
}
@ -1629,13 +1635,19 @@ static int gw_change_user(DCB *backend,
}
else
{
rv = gw_send_change_user_to_backend(database, username, client_sha1, backend_protocol);
/*
* Now copy new data into user session
*/
/** This assumes that authentication will succeed. If authentication fails,
* the internal session will represent the wrong user. This is wrong and
* a check whether the COM_CHANGE_USER succeeded should be done in the
* backend protocol reply handling.
*
* For the time being, it is simpler to assume a COM_CHANGE_USER will always
* succeed if the authentication in MaxScale is successful. In practice this
* might not be true but these cases are handled by the router modules
* and the servers that fail to execute the COM_CHANGE_USER are discarded. */
strcpy(current_session->user, username);
strcpy(current_session->db, database);
memcpy(current_session->client_sha1, client_sha1, sizeof(current_session->client_sha1));
rv = gw_send_change_user_to_backend(database, username, client_sha1, backend_protocol);
}
retblock:

View File

@ -191,6 +191,11 @@ int MySQLSendHandshake(DCB* dcb)
int len_version_string = 0;
int id_num;
if (dcb->service->dbref)
{
mysql_server_language = dcb->service->dbref->server->charset;
}
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
GWBUF *buf;

View File

@ -412,8 +412,8 @@ blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint32_t size,
* Fill the gap with a self generated ignorable event
* Binlog file position is incremented by blr_write_special_event()
*/
if (hdr->next_pos && (hdr->next_pos > (file_offset + size)))
if (router->master_event_state == BLR_EVENT_DONE &&
hdr->next_pos && (hdr->next_pos > (file_offset + size)))
{
uint64_t hole_size = hdr->next_pos - file_offset - size;
if (!blr_write_special_event(router, file_offset, hole_size, hdr, BLRM_IGNORABLE))

View File

@ -1229,7 +1229,14 @@ blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt)
}
else
{
/* Terminate replication and exit from main loop */
blr_terminate_master_replication(router, ptr, len);
gwbuf_free(pkt);
pkt = NULL;
pkt_length = 0;
break;
}
if (hdr.ok == 0)

View File

@ -87,6 +87,7 @@
#include <zlib.h>
#include <maxscale/alloc.h>
static char* get_next_token(char *str, const char* delim, char **saveptr);
extern int load_mysql_users(SERV_LISTENER *listener);
extern void blr_master_close(ROUTER_INSTANCE* router);
extern int blr_file_new_binlog(ROUTER_INSTANCE *router, char *file);
@ -884,10 +885,16 @@ blr_slave_query(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue)
spinlock_acquire(&router->lock);
/* Set the BLRM_UNCONFIGURED state */
router->master_state = BLRM_UNCONFIGURED;
blr_master_set_empty_config(router);
blr_master_free_config(current_master);
/* Remove any error message and errno */
free(router->m_errmsg);
router->m_errmsg = NULL;
router->m_errno = 0;
spinlock_release(&router->lock);
if (removed_cfg == -1)
@ -1031,11 +1038,19 @@ blr_slave_query(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue)
router->master_state = BLRM_SLAVE_STOPPED;
spinlock_release(&router->lock);
}
if (!router->trx_safe)
{
/*
* The binlog server has just been configured
* master.ini file written in router->binlogdir.
* Now create the binlogfile specified in MASTER_LOG_FILE
*/
if (blr_file_new_binlog(router, router->binlog_name))
{
MXS_INFO("%s: 'master.ini' created, binlog file '%s' created", router->service->name, router->binlog_name);
}
blr_master_free_config(current_master);
return blr_slave_send_ok(router, slave);
}
if (router->trx_safe && router->pending_transaction)
@ -1051,17 +1066,24 @@ blr_slave_query(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue)
return blr_slave_send_warning_message(router, slave, message);
}
else
{
blr_master_free_config(current_master);
return blr_slave_send_ok(router, slave);
}
}
}
else
blr_master_free_config(current_master);
/*
* The CHAMGE MASTER command might specify a new binlog file.
* Let's create the binlogfile specified in MASTER_LOG_FILE
*/
if (strlen(router->prevbinlog) && strcmp(router->prevbinlog, router->binlog_name))
{
return blr_slave_send_ok(router, slave);
}
if (blr_file_new_binlog(router, router->binlog_name))
{
MXS_INFO("%s: created new binlog file '%s' by 'CHANGE MASTER TO' command",
router->service->name, router->binlog_name);
}
}
return blr_slave_send_ok(router, slave);
}
}
}
@ -3422,22 +3444,20 @@ blr_start_slave(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave)
/* Send warning message to mysql command */
blr_slave_send_warning_message(router, slave, msg);
}
}
/* create new one */
/* No file has beem opened, create a new binlog file */
if (router->binlog_fd == -1)
{
blr_file_new_binlog(router, router->binlog_name);
}
else
{
if (router->binlog_fd == -1)
{
/* create new one */
blr_file_new_binlog(router, router->binlog_name);
}
else
{
/* use existing one */
blr_file_append(router, router->binlog_name);
}
/* A new binlog file has been created by CHANGE MASTER TO
* if no pending transaction is detected.
* use the existing one.
*/
blr_file_append(router, router->binlog_name);
}
/** Initialise SSL: exit on error */
@ -4328,6 +4348,148 @@ blr_set_master_password(ROUTER_INSTANCE *router, char *password)
return 0;
}
/**
* Get next token
*
* Works exactly like strtok_t except that a delim character which appears
* anywhere within quotes is ignored. For instance, if delim is "," then
* a string like "MASTER_USER='maxscale_repl_user',MASTER_PASSWORD='a,a'"
* will be tokenized into the following two tokens:
*
* MASTER_USER='maxscale_repl_user'
* MASTER_PASSWORD='a,a'
*
* @see strtok_r
*/
static char* get_next_token(char *str, const char* delim, char **saveptr)
{
if (str)
{
*saveptr = str;
}
if (!*saveptr)
{
return NULL;
}
bool delim_found = true;
// Skip any delims in the beginning.
while (**saveptr && delim_found)
{
const char* d = delim;
while (*d)
{
if (*d == **saveptr)
{
break;
}
++d;
}
if (*d == 0)
{
delim_found = false;
}
else
{
++*saveptr;
}
}
if (!**saveptr)
{
return NULL;
}
delim_found = false;
char *token = *saveptr;
char *p = *saveptr;
char quote = 0;
while (*p && !delim_found)
{
switch (*p)
{
case '\'':
case '"':
case '`':
if (!quote)
{
quote = *p;
}
else if (quote == *p)
{
quote = 0;
}
break;
default:
if (!quote)
{
const char *d = delim;
while (*d && !delim_found)
{
if (*p == *d)
{
delim_found = true;
*p = 0;
}
else
{
++d;
}
}
}
}
++p;
}
if (*p == 0)
{
*saveptr = NULL;
}
else if (delim_found)
{
*saveptr = p;
delim_found = true;
while (**saveptr && delim_found)
{
const char *d = delim;
while (*d)
{
if (**saveptr == *d)
{
break;
}
else
{
++d;
}
}
if (*d == 0)
{
delim_found = false;
}
else
{
++*saveptr;
}
}
}
return token;
}
/**
* Parse a CHANGE MASTER TO SQL command
*
@ -4342,7 +4504,7 @@ blr_parse_change_master_command(char *input, char *error_string, CHANGE_MASTER_O
char *sep = ",";
char *word, *brkb;
if ((word = strtok_r(input, sep, &brkb)) == NULL)
if ((word = get_next_token(input, sep, &brkb)) == NULL)
{
snprintf(error_string, BINLOG_ERROR_MSG_LEN, "Unable to parse query [%s]", input);
return 1;
@ -4356,7 +4518,7 @@ blr_parse_change_master_command(char *input, char *error_string, CHANGE_MASTER_O
}
}
while ((word = strtok_r(NULL, sep, &brkb)) != NULL)
while ((word = get_next_token(NULL, sep, &brkb)) != NULL)
{
/* parse options key=val */
if (blr_handle_change_master_token(word, error_string, config))
@ -4380,12 +4542,12 @@ static int
blr_handle_change_master_token(char *input, char *error, CHANGE_MASTER_OPTIONS *config)
{
/* space+TAB+= */
char *sep = " =";
char *sep = " \t=";
char *word, *brkb;
char *value = NULL;
char **option_field = NULL;
if ((word = strtok_r(input, sep, &brkb)) == NULL)
if ((word = get_next_token(input, sep, &brkb)) == NULL)
{
snprintf(error, BINLOG_ERROR_MSG_LEN, "error parsing %s", brkb);
return 1;
@ -4424,7 +4586,7 @@ static char *
blr_get_parsed_command_value(char *input)
{
/* space+TAB+= */
char *sep = " =";
char *sep = " \t=";
char *ret = NULL;
char *word;
char *value = NULL;
@ -4438,7 +4600,7 @@ blr_get_parsed_command_value(char *input)
return ret;
}
if ((word = strtok_r(NULL, sep, &input)) != NULL)
if ((word = get_next_token(NULL, sep, &input)) != NULL)
{
char *ptr;

View File

@ -527,8 +527,6 @@ exec_clear(DCB *dcb, MAXINFO_TREE *tree)
MXS_ERROR("%s", errmsg);
}
extern void shutdown_server();
/**
* MaxScale shutdown
* @param dcb Client DCB
@ -536,7 +534,7 @@ extern void shutdown_server();
*/
void exec_shutdown_maxscale(DCB *dcb, MAXINFO_TREE *tree)
{
shutdown_server();
maxscale_shutdown();
maxinfo_send_ok(dcb);
}

View File

@ -327,7 +327,7 @@ static void *newSession(ROUTER *router_inst, SESSION *session)
if (!select_connect_backend_servers(&master_ref, backend_ref, router_nservers,
max_nslaves, max_slave_rlag,
client_rses->rses_config.rw_slave_select_criteria,
session, router))
session, router, false))
{
/**
* Master and at least <min_nslaves> slaves must be found if the router is
@ -458,6 +458,40 @@ static void freeSession(ROUTER *router_instance, void *router_client_session)
return;
}
/**
* @brief Mark a backend reference as failed
*
* @param bref Backend reference to close
* @param fatal Whether the failure was fatal
*/
void close_failed_bref(backend_ref_t *bref, bool fatal)
{
if (BREF_IS_WAITING_RESULT(bref))
{
bref_clear_state(bref, BREF_WAITING_RESULT);
}
bref_clear_state(bref, BREF_QUERY_ACTIVE);
bref_clear_state(bref, BREF_IN_USE);
bref_set_state(bref, BREF_CLOSED);
if (fatal)
{
bref_set_state(bref, BREF_FATAL_FAILURE);
}
if (sescmd_cursor_is_active(&bref->bref_sescmd_cur))
{
sescmd_cursor_set_active(&bref->bref_sescmd_cur, false);
}
if (bref->bref_pending_cmd)
{
gwbuf_free(bref->bref_pending_cmd);
bref->bref_pending_cmd = NULL;
}
}
/**
* @brief The main routing entry point for a query (API)
*
@ -652,7 +686,8 @@ static void clientReply(ROUTER *instance, void *router_session, GWBUF *writebuf,
router_cli_ses->rses_config.rw_max_slave_replication_lag,
router_cli_ses->rses_config.rw_slave_select_criteria,
router_cli_ses->rses_master_ref->bref_dcb->session,
router_cli_ses->router);
router_cli_ses->router,
true);
}
}
/**
@ -1296,29 +1331,11 @@ static void handleError(ROUTER *instance, void *router_session,
* If master has lost its Master status error can't be
* handled so that session could continue.
*/
if (rses->rses_master_ref && rses->rses_master_ref->bref_dcb == problem_dcb &&
!SERVER_IS_MASTER(rses->rses_master_ref->ref->server))
if (rses->rses_master_ref && rses->rses_master_ref->bref_dcb == problem_dcb)
{
SERVER *srv = rses->rses_master_ref->ref->server;
backend_ref_t *bref;
bref = get_bref_from_dcb(rses, problem_dcb);
if (bref != NULL)
{
CHK_BACKEND_REF(bref);
if (BREF_IS_WAITING_RESULT(bref))
{
bref_clear_state(bref, BREF_WAITING_RESULT);
}
bref_clear_state(bref, BREF_IN_USE);
bref_set_state(bref, BREF_CLOSED);
}
else
{
MXS_ERROR("server %s:%d lost the "
"master status but could not locate the "
"corresponding backend ref.",
srv->name, srv->port);
}
backend_ref_t *bref = get_bref_from_dcb(rses, problem_dcb);
bool can_continue = false;
if (rses->rses_config.rw_master_failure_mode != RW_FAIL_INSTANTLY &&
(bref == NULL || !BREF_IS_WAITING_RESULT(bref)))
@ -1332,38 +1349,41 @@ static void handleError(ROUTER *instance, void *router_session,
* can't be sure whether it was executed or not. In this
* case the safest thing to do is to close the client
* connection. */
*succp = true;
can_continue = true;
}
else if (!SERVER_IS_MASTER(srv) && !srv->master_err_is_logged)
{
MXS_ERROR("Server %s:%d lost the master status. Readwritesplit "
"service can't locate the master. Client sessions "
"will be closed.", srv->name, srv->port);
srv->master_err_is_logged = true;
}
*succp = can_continue;
if (bref != NULL)
{
CHK_BACKEND_REF(bref);
close_failed_bref(bref, true);
}
else
{
if (!srv->master_err_is_logged)
{
MXS_ERROR("server %s:%d lost the "
"master status. Readwritesplit "
"service can't locate the master. "
"Client sessions will be closed.",
srv->name, srv->port);
srv->master_err_is_logged = true;
}
*succp = false;
}
MXS_ERROR("Server %s:%d lost the master status but could not locate the "
"corresponding backend ref.", srv->name, srv->port);
}
}
else
{
/**
* This is called in hope of getting replacement for
* failed slave(s). This call may free rses.
* failed slave(s).
*/
*succp = handle_error_new_connection(inst, &rses, problem_dcb, errmsgbuf);
}
dcb_close(problem_dcb);
close_dcb = false;
/* Free the lock if rses still exists */
if (rses)
{
rses_end_locked_router_action(rses);
}
rses_end_locked_router_action(rses);
break;
}
@ -1419,13 +1439,7 @@ static void handle_error_reply_client(SESSION *ses, ROUTER_CLIENT_SES *rses,
if (BREF_IS_IN_USE(bref))
{
bref_clear_state(bref, BREF_IN_USE);
bref_set_state(bref, BREF_CLOSED);
if (BREF_IS_WAITING_RESULT(bref))
{
bref_clear_state(bref, BREF_WAITING_RESULT);
}
close_failed_bref(bref, false);
dcb_close(backend_dcb);
}
}
@ -1499,13 +1513,11 @@ static bool handle_error_new_connection(ROUTER_INSTANCE *inst,
*/
if (BREF_IS_WAITING_RESULT(bref))
{
DCB *client_dcb;
client_dcb = ses->client_dcb;
DCB *client_dcb = ses->client_dcb;
client_dcb->func.write(client_dcb, gwbuf_clone(errmsg));
bref_clear_state(bref, BREF_WAITING_RESULT);
}
bref_clear_state(bref, BREF_IN_USE);
bref_set_state(bref, BREF_CLOSED);
close_failed_bref(bref, false);
/**
* Error handler is already called for this DCB because
@ -1542,7 +1554,7 @@ static bool handle_error_new_connection(ROUTER_INSTANCE *inst,
myrses->rses_nbackends,
max_nslaves, max_slave_rlag,
myrses->rses_config.rw_slave_select_criteria,
ses, inst);
ses, inst, true);
}
return_succp:

View File

@ -38,7 +38,7 @@ typedef enum bref_state
BREF_WAITING_RESULT = 0x02, /*< for session commands only */
BREF_QUERY_ACTIVE = 0x04, /*< for other queries */
BREF_CLOSED = 0x08,
BREF_SESCMD_FAILED = 0x10 /*< Backend references that should be dropped */
BREF_FATAL_FAILURE = 0x10 /*< Backend references that should be dropped */
} bref_state_t;
#define BREF_IS_NOT_USED(s) ((s)->bref_state & ~BREF_IN_USE)
@ -46,7 +46,7 @@ typedef enum bref_state
#define BREF_IS_WAITING_RESULT(s) ((s)->bref_num_result_wait > 0)
#define BREF_IS_QUERY_ACTIVE(s) ((s)->bref_state & BREF_QUERY_ACTIVE)
#define BREF_IS_CLOSED(s) ((s)->bref_state & BREF_CLOSED)
#define BREF_HAS_FAILED(s) ((s)->bref_state & BREF_SESCMD_FAILED)
#define BREF_HAS_FAILED(s) ((s)->bref_state & BREF_FATAL_FAILURE)
typedef enum backend_type_t
{

View File

@ -121,10 +121,11 @@ GWBUF *sescmd_cursor_process_replies(GWBUF *replybuf,
bool select_connect_backend_servers(backend_ref_t **p_master_ref,
backend_ref_t *backend_ref,
int router_nservers, int max_nslaves,
int max_rlag,
int max_slave_rlag,
select_criteria_t select_criteria,
SESSION *session,
ROUTER_INSTANCE *router);
ROUTER_INSTANCE *router,
bool active_session);
/*
* The following are implemented in rwsplit_tmp_table_multi.c
@ -132,13 +133,14 @@ bool select_connect_backend_servers(backend_ref_t **p_master_ref,
void check_drop_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
GWBUF *querybuf,
mysql_server_cmd_t packet_type);
qc_query_type_t is_read_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
bool is_read_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
GWBUF *querybuf,
qc_query_type_t type);
void check_create_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
GWBUF *querybuf, qc_query_type_t type);
bool check_for_multi_stmt(GWBUF *buf, void *protocol, mysql_server_cmd_t packet_type);
qc_query_type_t determine_query_type(GWBUF *querybuf, int packet_type, bool non_empty_packet);
void close_failed_bref(backend_ref_t *bref, bool fatal);
#ifdef __cplusplus
}

View File

@ -974,9 +974,9 @@ handle_multi_temp_and_load(ROUTER_CLIENT_SES *rses, GWBUF *querybuf,
if (rses->have_tmp_tables)
{
check_drop_tmp_table(rses, querybuf, packet_type);
if (is_packet_a_query(packet_type))
if (is_packet_a_query(packet_type) && is_read_tmp_table(rses, querybuf, *qtype))
{
*qtype = is_read_tmp_table(rses, querybuf, *qtype);
*qtype |= QUERY_TYPE_MASTER_READ;
}
}
check_create_tmp_table(rses, querybuf, *qtype);
@ -1116,6 +1116,64 @@ bool handle_slave_is_target(ROUTER_INSTANCE *inst, ROUTER_CLIENT_SES *rses,
}
}
/**
* @brief Log master write failure
*
* @param rses Router session
*/
static void log_master_routing_failure(ROUTER_CLIENT_SES *rses, bool found,
DCB *master_dcb, DCB *curr_master_dcb)
{
char errmsg[MAX_SERVER_NAME_LEN * 2 + 100]; // Extra space for error message
if (!found)
{
sprintf(errmsg, "Could not find a valid master connection");
}
else if (master_dcb && curr_master_dcb)
{
/** We found a master but it's not the same connection */
ss_dassert(master_dcb != curr_master_dcb);
if (master_dcb->server != curr_master_dcb->server)
{
sprintf(errmsg, "Master server changed from '%s' to '%s'",
master_dcb->server->unique_name,
curr_master_dcb->server->unique_name);
}
else
{
ss_dassert(false); // Currently we don't reconnect to the master
sprintf(errmsg, "Connection to master '%s' was recreated",
curr_master_dcb->server->unique_name);
}
}
else if (master_dcb)
{
/** We have an original master connection but we couldn't find it */
sprintf(errmsg, "The connection to master server '%s' is not available",
master_dcb->server->unique_name);
}
else
{
/** We never had a master connection, the session must be in read-only mode */
if (rses->rses_config.rw_master_failure_mode != RW_FAIL_INSTANTLY)
{
sprintf(errmsg, "Session is in read-only mode because it was created "
"when no master was available");
}
else
{
ss_dassert(false); // A session should always have a master reference
sprintf(errmsg, "Was supposed to route to master but couldn't "
"find master in a suitable state");
}
}
MXS_WARNING("[%s] Write query received from %s@%s. %s. Closing client connection.",
rses->router->service->name, rses->client_dcb->user,
rses->client_dcb->remote, errmsg);
}
/**
* @brief Handle master is the target
*
@ -1128,7 +1186,7 @@ bool handle_slave_is_target(ROUTER_INSTANCE *inst, ROUTER_CLIENT_SES *rses,
* @return bool - true if succeeded, false otherwise
*/
bool handle_master_is_target(ROUTER_INSTANCE *inst, ROUTER_CLIENT_SES *rses,
DCB **target_dcb)
DCB **target_dcb)
{
DCB *master_dcb = rses->rses_master_ref ? rses->rses_master_ref->bref_dcb : NULL;
DCB *curr_master_dcb = NULL;
@ -1141,29 +1199,26 @@ bool handle_master_is_target(ROUTER_INSTANCE *inst, ROUTER_CLIENT_SES *rses,
}
else
{
if (succp && master_dcb != curr_master_dcb)
if (succp && master_dcb == curr_master_dcb)
{
MXS_INFO("Was supposed to route to master but master has changed.");
atomic_add(&inst->stats.n_master, 1);
*target_dcb = master_dcb;
}
else
{
MXS_INFO("Was supposed to route to master but couldn't find master"
" in a suitable state.");
}
if (rses->rses_config.rw_master_failure_mode == RW_ERROR_ON_WRITE)
{
/** Old master is no longer available */
succp = send_readonly_error(rses->client_dcb);
}
else
{
MXS_WARNING("[%s] Write query received from %s@%s when no master is "
"available, closing client connection.", inst->service->name,
rses->client_dcb->user, rses->client_dcb->remote);
succp = false;
/** The original master is not available, we can't route the write */
if (rses->rses_config.rw_master_failure_mode == RW_ERROR_ON_WRITE)
{
succp = send_readonly_error(rses->client_dcb);
}
else
{
log_master_routing_failure(rses, succp, master_dcb, curr_master_dcb);
succp = false;
}
}
}
return succp;
}
@ -1369,6 +1424,7 @@ static backend_ref_t *get_root_master_bref(ROUTER_CLIENT_SES *rses)
bref = &rses->rses_backend_ref[i];
if (bref && BREF_IS_IN_USE(bref))
{
ss_dassert(!BREF_IS_CLOSED(bref) && !BREF_HAS_FAILED(bref));
if (bref == rses->rses_master_ref)
{
/** Store master state for better error reporting */
@ -1386,13 +1442,11 @@ static backend_ref_t *get_root_master_bref(ROUTER_CLIENT_SES *rses)
}
}
if (candidate_bref == NULL && rses->rses_config.rw_master_failure_mode == RW_FAIL_INSTANTLY)
if (candidate_bref == NULL && rses->rses_config.rw_master_failure_mode == RW_FAIL_INSTANTLY &&
rses->rses_master_ref && BREF_IS_IN_USE(rses->rses_master_ref))
{
MXS_ERROR("Could not find master among the backend "
"servers. Previous master's state : %s",
rses->rses_master_ref == NULL ? "No master found" :
(!BREF_IS_IN_USE(rses->rses_master_ref) ? "Master is not in use" :
STRSRVSTATUS(&master)));
MXS_ERROR("Could not find master among the backend servers. "
"Previous master's state : %s", STRSRVSTATUS(&master));
}
return candidate_bref;

View File

@ -61,12 +61,71 @@ int (*criteria_cmpfun[LAST_CRITERIA])(const void *, const void *) =
bref_cmp_current_load
};
/*
* The following function is the only one that is called from elsewhere in
* the read write split router. It is not intended for use from outside this
* router. Other functions in this module are internal and are called
* directly or indirectly by this function.
/**
* @brief Check whether it's possible to connect to this server
*
* @param bref Backend reference
* @return True if a connection to this server can be attempted
*/
static bool bref_valid_for_connect(const backend_ref_t *bref)
{
return !BREF_HAS_FAILED(bref) && SERVER_IS_RUNNING(bref->ref->server);
}
/**
* Check whether it's possible to use this server as a slave
*
* @param bref Backend reference
* @param master_host The master server
* @return True if this server is a valid slave candidate
*/
static bool bref_valid_for_slave(const backend_ref_t *bref, const SERVER *master_host)
{
SERVER *server = bref->ref->server;
return (SERVER_IS_SLAVE(server) || SERVER_IS_RELAY_SERVER(server)) &&
(master_host == NULL || (server != master_host));
}
/**
* @brief Find the best slave candidate
*
* This function iterates through @c bref and tries to find the best backend
* reference that is not in use. @c cmpfun will be called to compare the backends.
*
* @param bref Backend reference
* @param n Size of @c bref
* @param master The master server
* @param cmpfun qsort() compatible comparison function
* @return The best slave backend reference or NULL if no candidates could be found
*/
backend_ref_t* get_slave_candidate(backend_ref_t *bref, int n, const SERVER *master,
int (*cmpfun)(const void *, const void *))
{
backend_ref_t *candidate = NULL;
for (int i = 0; i < n; i++)
{
if (!BREF_IS_IN_USE(&bref[i]) &&
bref_valid_for_connect(&bref[i]) &&
bref_valid_for_slave(&bref[i], master))
{
if (candidate)
{
if (cmpfun(candidate, &bref[i]) > 0)
{
candidate = &bref[i];
}
}
else
{
candidate = &bref[i];
}
}
}
return candidate;
}
/**
* @brief Search suitable backend servers from those of router instance
@ -92,7 +151,8 @@ bool select_connect_backend_servers(backend_ref_t **p_master_ref,
int max_slave_rlag,
select_criteria_t select_criteria,
SESSION *session,
ROUTER_INSTANCE *router)
ROUTER_INSTANCE *router,
bool active_session)
{
if (p_master_ref == NULL || backend_ref == NULL)
{
@ -103,31 +163,33 @@ bool select_connect_backend_servers(backend_ref_t **p_master_ref,
}
/* get the root Master */
SERVER_REF *master_host = get_root_master(backend_ref, router_nservers);
SERVER_REF *master_backend = get_root_master(backend_ref, router_nservers);
SERVER *master_host = master_backend ? master_backend->server : NULL;
if (router->rwsplit_config.rw_master_failure_mode == RW_FAIL_INSTANTLY &&
(master_host == NULL || !SERVER_REF_IS_ACTIVE(master_host) ||
SERVER_IS_DOWN(master_host->server)))
(master_host == NULL || SERVER_IS_DOWN(master_host)))
{
MXS_ERROR("Couldn't find suitable Master from %d candidates.", router_nservers);
return false;
}
/**
* Existing session : master is already chosen and connected.
* The function was called because new slave must be selected to replace
* failed one.
* New session:
*
* Connect to both master and slaves
*
* Existing session:
*
* Master is already connected or we don't have a master. The function was
* called because new slaves must be selected to replace failed ones.
*/
bool master_connected = *p_master_ref != NULL;
bool master_connected = active_session || *p_master_ref != NULL;
/** Check slave selection criteria and set compare function */
int (*p)(const void *, const void *) = criteria_cmpfun[select_criteria];
ss_dassert(p);
/** Sort the pointer list to servers according to slave selection criteria.
* The servers that match the criteria the best are at the beginning of
* the list. */
qsort(backend_ref, (size_t) router_nservers, sizeof(backend_ref_t), p);
SERVER *old_master = *p_master_ref ? (*p_master_ref)->ref->server : NULL;
if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO))
{
@ -139,53 +201,59 @@ bool select_connect_backend_servers(backend_ref_t **p_master_ref,
const int min_nslaves = 0; /*< not configurable at the time */
bool succp = false;
/**
* Choose at least 1+min_nslaves (master and slave) and at most 1+max_nslaves
* servers from the sorted list. First master found is selected.
*/
for (int i = 0; i < router_nservers &&
(slaves_connected < max_nslaves || !master_connected); i++)
if (!master_connected)
{
SERVER *serv = backend_ref[i].ref->server;
if (!BREF_HAS_FAILED(&backend_ref[i]) &&
SERVER_REF_IS_ACTIVE(backend_ref[i].ref) &&
SERVER_IS_RUNNING(serv))
/** Find a master server */
for (int i = 0; i < router_nservers; i++)
{
/* check also for relay servers and don't take the master_host */
if (slaves_found < max_nslaves &&
(max_slave_rlag == MAX_RLAG_UNDEFINED ||
(serv->rlag != MAX_RLAG_NOT_AVAILABLE &&
serv->rlag <= max_slave_rlag)) &&
(SERVER_IS_SLAVE(serv) || SERVER_IS_RELAY_SERVER(serv)) &&
(master_host == NULL || (serv != master_host->server)))
{
slaves_found += 1;
SERVER *serv = backend_ref[i].ref->server;
if (BREF_IS_IN_USE((&backend_ref[i])) ||
connect_server(&backend_ref[i], session, true))
{
slaves_connected += 1;
}
}
/* take the master_host for master */
else if (master_host && (serv == master_host->server))
if (bref_valid_for_connect(&backend_ref[i]) &&
master_host && serv == master_host)
{
/** p_master_ref must be assigned with this backend_ref pointer
* because its original value may have been lost when backend
* references were sorted with qsort. */
*p_master_ref = &backend_ref[i];
if (!master_connected)
if (connect_server(&backend_ref[i], session, false))
{
if (connect_server(&backend_ref[i], session, false))
{
master_connected = true;
}
*p_master_ref = &backend_ref[i];
break;
}
}
}
} /*< for */
}
/** Calculate how many connections we already have */
for (int i = 0; i < router_nservers; i++)
{
if (bref_valid_for_connect(&backend_ref[i]) &&
bref_valid_for_slave(&backend_ref[i], master_host))
{
slaves_found += 1;
if (BREF_IS_IN_USE(&backend_ref[i]))
{
slaves_connected += 1;
}
}
}
ss_dassert(slaves_connected < max_nslaves);
backend_ref_t *bref = get_slave_candidate(backend_ref, router_nservers, master_host, p);
/** Connect to all possible slaves */
while (bref && slaves_connected < max_nslaves)
{
if (connect_server(bref, session, true))
{
slaves_connected += 1;
}
else
{
/** Failed to connect, mark server as failed */
bref_set_state(bref, BREF_FATAL_FAILURE);
}
bref = get_slave_candidate(backend_ref, router_nservers, master_host, p);
}
/**
* Successful cases
@ -218,11 +286,9 @@ bool select_connect_backend_servers(backend_ref_t **p_master_ref,
/** Failure cases */
else
{
if (slaves_connected < min_nslaves)
{
MXS_ERROR("Couldn't establish required amount of "
"slave connections for router session.");
}
MXS_ERROR("Couldn't establish required amount of slave connections for "
"router session. Would need between %d and %d slaves but only have %d.",
min_nslaves, max_nslaves, slaves_connected);
/** Clean up connections */
for (int i = 0; i < router_nservers; i++)
@ -231,8 +297,8 @@ bool select_connect_backend_servers(backend_ref_t **p_master_ref,
{
ss_dassert(backend_ref[i].ref->connections > 0);
/** disconnect opened connections */
bref_clear_state(&backend_ref[i], BREF_IN_USE);
close_failed_bref(&backend_ref[i], true);
/** Decrease backend's connection counter. */
atomic_add(&backend_ref[i].ref->connections, -1);
dcb_close(backend_ref[i].bref_dcb);

View File

@ -169,16 +169,13 @@ GWBUF *sescmd_cursor_process_replies(GWBUF *replybuf,
/** Set response status received */
bref_clear_state(bref, BREF_WAITING_RESULT);
if (bref->reply_cmd != scmd->reply_cmd)
if (bref->reply_cmd != scmd->reply_cmd && BREF_IS_IN_USE(bref))
{
MXS_ERROR("Slave server '%s': response differs from master's response. "
"Closing connection due to inconsistent session state.",
bref->ref->server->unique_name);
sescmd_cursor_set_active(scur, false);
bref_clear_state(bref, BREF_QUERY_ACTIVE);
bref_clear_state(bref, BREF_IN_USE);
bref_set_state(bref, BREF_CLOSED);
bref_set_state(bref, BREF_SESCMD_FAILED);
close_failed_bref(bref, true);
if (bref->bref_dcb)
{
dcb_close(bref->bref_dcb);
@ -213,12 +210,11 @@ GWBUF *sescmd_cursor_process_replies(GWBUF *replybuf,
{
/** This backend has already received a response */
if (ses->rses_backend_ref[i].reply_cmd != scmd->reply_cmd &&
!BREF_IS_CLOSED(&ses->rses_backend_ref[i]))
!BREF_IS_CLOSED(&ses->rses_backend_ref[i]) &&
BREF_IS_IN_USE(&ses->rses_backend_ref[i]))
{
bref_clear_state(&ses->rses_backend_ref[i], BREF_QUERY_ACTIVE);
bref_clear_state(&ses->rses_backend_ref[i], BREF_IN_USE);
bref_set_state(&ses->rses_backend_ref[i], BREF_CLOSED);
bref_set_state(bref, BREF_SESCMD_FAILED);
close_failed_bref(&ses->rses_backend_ref[i], true);
if (ses->rses_backend_ref[i].bref_dcb)
{
dcb_close(ses->rses_backend_ref[i].bref_dcb);

View File

@ -109,9 +109,9 @@ void check_drop_tmp_table(ROUTER_CLIENT_SES *router_cli_ses, GWBUF *querybuf,
* @param type The type of the query resolved so far
* @return The type of the query
*/
qc_query_type_t is_read_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
bool is_read_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
GWBUF *querybuf,
qc_query_type_t type)
qc_query_type_t qtype)
{
bool target_tmp_table = false;
@ -120,20 +120,20 @@ qc_query_type_t is_read_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
char *dbname;
char hkey[MYSQL_DATABASE_MAXLEN + MYSQL_TABLE_MAXLEN + 2];
MYSQL_session *data;
qc_query_type_t qtype = type;
bool rval = false;
rses_property_t *rses_prop_tmp;
if (router_cli_ses == NULL || querybuf == NULL)
{
MXS_ERROR("[%s] Error: NULL parameters passed: %p %p", __FUNCTION__,
router_cli_ses, querybuf);
return type;
return false;
}
if (router_cli_ses->client_dcb == NULL)
{
MXS_ERROR("[%s] Error: Client DCB is NULL.", __FUNCTION__);
return type;
return false;
}
rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
@ -142,7 +142,7 @@ qc_query_type_t is_read_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
if (data == NULL)
{
MXS_ERROR("[%s] Error: User data in client DBC is NULL.", __FUNCTION__);
return qtype;
return false;
}
dbname = (char *)data->db;
@ -166,7 +166,7 @@ qc_query_type_t is_read_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
if (hashtable_fetch(rses_prop_tmp->rses_prop_data.temp_tables, hkey))
{
/**Query target is a temporary table*/
qtype = QUERY_TYPE_READ_TMP_TABLE;
rval = true;
MXS_INFO("Query targets a temporary table: %s", hkey);
break;
}
@ -184,7 +184,7 @@ qc_query_type_t is_read_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
MXS_FREE(tbl);
}
return qtype;
return rval;
}
/**