Merge branch 'develop' into mon_script_test
This commit is contained in:
@ -86,6 +86,14 @@ MODULE_INFO info = {
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
|
||||
static char* required_rules[] = {
|
||||
"wildcard",
|
||||
"columns",
|
||||
"regex",
|
||||
"limit_queries",
|
||||
"no_where_clause",
|
||||
NULL
|
||||
};
|
||||
/*
|
||||
* The filter entry points
|
||||
*/
|
||||
@ -456,9 +464,9 @@ bool check_time(char* str)
|
||||
|
||||
|
||||
#ifdef SS_DEBUG
|
||||
#define CHK_TIMES(t)(ss_dassert(t->tm_sec > -1 && t->tm_sec < 62 \
|
||||
#define CHK_TIMES(t) ss_dassert(t->tm_sec > -1 && t->tm_sec < 62 \
|
||||
&& t->tm_min > -1 && t->tm_min < 60 \
|
||||
&& t->tm_hour > -1 && t->tm_hour < 24))
|
||||
&& t->tm_hour > -1 && t->tm_hour < 24)
|
||||
#else
|
||||
#define CHK_TIMES(t)
|
||||
#endif
|
||||
@ -861,6 +869,10 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
bool allow,deny,mode;
|
||||
RULE* ruledef = NULL;
|
||||
bool rval = true;
|
||||
bool req_defined,oq_def,at_def;
|
||||
int i;
|
||||
|
||||
req_defined = oq_def = at_def = false;
|
||||
|
||||
if(tok == NULL)
|
||||
{
|
||||
@ -920,7 +932,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
}
|
||||
else
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error : Unknown token in rule file: %s",tok);
|
||||
skygw_log_write(LOGFILE_ERROR,"Error : Unknown token in rule '%s': %s",rule,tok);
|
||||
rval = false;
|
||||
goto retblock;
|
||||
}
|
||||
@ -946,6 +958,25 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
|
||||
while(tok)
|
||||
{
|
||||
reparse_rule:
|
||||
|
||||
for(i = 0;required_rules[i] != NULL;i++)
|
||||
{
|
||||
if(strcmp(tok,required_rules[i]) == 0)
|
||||
{
|
||||
if(req_defined)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, Multiple non-optional rules: %s",rule);
|
||||
rval = false;
|
||||
goto retblock;
|
||||
}
|
||||
else
|
||||
{
|
||||
req_defined = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(strcmp(tok,"wildcard") == 0)
|
||||
{
|
||||
ruledef->type = RT_WILDCARD;
|
||||
@ -970,15 +1001,20 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
}
|
||||
else if(strcmp(tok,"at_times") == 0)
|
||||
{
|
||||
|
||||
if(at_def)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, multiple 'at_times' tokens: %s",rule);
|
||||
rval = false;
|
||||
goto retblock;
|
||||
}
|
||||
at_def = true;
|
||||
tok = strtok_r(NULL, " ,",&saveptr);
|
||||
TIMERANGE *tr = NULL;
|
||||
bool not_valid = false;
|
||||
while(tok){
|
||||
|
||||
if(strcmp(tok,"on_queries") == 0)
|
||||
break;
|
||||
if(!check_time(tok))
|
||||
{
|
||||
not_valid = true;
|
||||
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, malformed time definition: %s",tok);
|
||||
rval = false;
|
||||
goto retblock;
|
||||
@ -1002,12 +1038,11 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
tr = tmp;
|
||||
tok = strtok_r(NULL, " ,",&saveptr);
|
||||
}
|
||||
|
||||
ruledef->active = tr;
|
||||
|
||||
if(not_valid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(tok && strcmp(tok,"on_queries") == 0)
|
||||
goto reparse_rule;
|
||||
|
||||
}
|
||||
else if(strcmp(tok,"regex") == 0)
|
||||
@ -1126,14 +1161,24 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule parsing failed, not a number: '%s'.", tok);
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if(qs->limit < 1){
|
||||
free(qs);
|
||||
rval = false;
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Bad query amount: %s", tok);
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
errptr = NULL;
|
||||
tok = strtok_r(NULL," ",&saveptr);
|
||||
|
||||
if(tok == NULL){
|
||||
free(qs);
|
||||
rval = false;
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Missing parameter in limit_queries: '%s'.", rule);
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
qs->period = strtod(tok,&errptr);
|
||||
|
||||
if(errptr && *errptr != '\0')
|
||||
@ -1143,9 +1188,17 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule parsing failed, not a number: '%s'.", tok);
|
||||
goto retblock;
|
||||
}
|
||||
errptr = NULL;
|
||||
|
||||
if(qs->period < 1){
|
||||
free(qs);
|
||||
rval = false;
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Bad time period: %s", tok);
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
errptr = NULL;
|
||||
tok = strtok_r(NULL," ",&saveptr);
|
||||
|
||||
if(tok == NULL){
|
||||
free(qs);
|
||||
rval = false;
|
||||
@ -1162,6 +1215,13 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if(qs->cooldown < 1){
|
||||
free(qs);
|
||||
rval = false;
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Bad blocking period: %s", tok);
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
ruledef->type = RT_THROTTLE;
|
||||
ruledef->data = (void*)qs;
|
||||
}
|
||||
@ -1172,6 +1232,13 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
}
|
||||
else if(strcmp(tok,"on_queries") == 0)
|
||||
{
|
||||
if(oq_def)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, multiple 'on_queries' tokens: %s",rule);
|
||||
rval = false;
|
||||
goto retblock;
|
||||
}
|
||||
oq_def = true;
|
||||
tok = strtok_r(NULL," ",&saveptr);
|
||||
|
||||
if(tok == NULL)
|
||||
@ -1657,29 +1724,36 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
|
||||
|
||||
case RT_COLUMN:
|
||||
|
||||
if(is_sql && is_real){
|
||||
|
||||
strln = (STRLINK*)rulelist->rule->data;
|
||||
if(is_sql && is_real)
|
||||
{
|
||||
where = skygw_get_affected_fields(queue);
|
||||
|
||||
if(where != NULL){
|
||||
char* saveptr;
|
||||
char* tok = strtok_r(where," ",&saveptr);
|
||||
while(tok)
|
||||
{
|
||||
strln = (STRLINK*)rulelist->rule->data;
|
||||
while(strln)
|
||||
{
|
||||
if(strcasecmp(tok,strln->value) == 0)
|
||||
{
|
||||
matches = true;
|
||||
|
||||
while(strln){
|
||||
if(strstr(where,strln->value)){
|
||||
|
||||
matches = true;
|
||||
|
||||
if(!rulelist->rule->allow){
|
||||
sprintf(emsg,"Permission denied to column '%s'.",strln->value);
|
||||
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query targets forbidden column: %s",rulelist->rule->name,strln->value);
|
||||
msg = strdup(emsg);
|
||||
goto queryresolved;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
strln = strln->next;
|
||||
}
|
||||
if(!rulelist->rule->allow)
|
||||
{
|
||||
sprintf(emsg,"Permission denied to column '%s'.",strln->value);
|
||||
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query targets forbidden column: %s",rulelist->rule->name,strln->value);
|
||||
msg = strdup(emsg);
|
||||
goto queryresolved;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
strln = strln->next;
|
||||
}
|
||||
tok = strtok_r(NULL,",",&saveptr);
|
||||
}
|
||||
free(where);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1694,16 +1768,16 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
|
||||
|
||||
if(where != NULL){
|
||||
strptr = where;
|
||||
}else{
|
||||
strptr = query;
|
||||
}
|
||||
if(strchr(strptr,'*')){
|
||||
|
||||
matches = true;
|
||||
msg = strdup("Usage of wildcard denied.");
|
||||
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query contains a wildcard.",rulelist->rule->name);
|
||||
goto queryresolved;
|
||||
}
|
||||
if(strchr(strptr,'*')){
|
||||
|
||||
matches = true;
|
||||
msg = strdup("Usage of wildcard denied.");
|
||||
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query contains a wildcard.",rulelist->rule->name);
|
||||
goto queryresolved;
|
||||
}
|
||||
free(where);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -95,7 +95,7 @@ static FILTER_OBJECT MyObject = {
|
||||
* are logged.
|
||||
*
|
||||
* To this base a session number is attached such that each session will
|
||||
* have a nique name.
|
||||
* have a unique name.
|
||||
*/
|
||||
typedef struct {
|
||||
int sessions; /* The count of sessions */
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include <string.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include "maxconfig.h"
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
extern size_t log_ses_count[];
|
||||
@ -86,6 +88,8 @@ typedef struct {
|
||||
char *match; /* Regular expression to match */
|
||||
char *replace; /* Replacement text */
|
||||
regex_t re; /* Compiled regex text */
|
||||
FILE* logfile;
|
||||
bool log_trace;
|
||||
} REGEX_INSTANCE;
|
||||
|
||||
/**
|
||||
@ -98,6 +102,9 @@ typedef struct {
|
||||
int active; /* Is filter active */
|
||||
} REGEX_SESSION;
|
||||
|
||||
void log_match(REGEX_INSTANCE* inst,char* re, char* old, char* new);
|
||||
void log_nomatch(REGEX_INSTANCE* inst, char* re, char* old);
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
@ -146,7 +153,7 @@ createInstance(char **options, FILTER_PARAMETER **params)
|
||||
{
|
||||
REGEX_INSTANCE *my_instance;
|
||||
int i, cflags = REG_ICASE;
|
||||
|
||||
char *logfile = NULL;
|
||||
if ((my_instance = calloc(1, sizeof(REGEX_INSTANCE))) != NULL)
|
||||
{
|
||||
my_instance->match = NULL;
|
||||
@ -162,6 +169,10 @@ int i, cflags = REG_ICASE;
|
||||
my_instance->source = strdup(params[i]->value);
|
||||
else if (!strcmp(params[i]->name, "user"))
|
||||
my_instance->user = strdup(params[i]->value);
|
||||
else if (!strcmp(params[i]->name, "log_trace"))
|
||||
my_instance->log_trace = config_truth_value(params[i]->value);
|
||||
else if (!strcmp(params[i]->name, "log_file"))
|
||||
logfile = strdup(params[i]->value);
|
||||
else if (!filter_standard_parameter(params[i]->name))
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -209,6 +220,25 @@ int i, cflags = REG_ICASE;
|
||||
free(my_instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(logfile != NULL)
|
||||
{
|
||||
if((my_instance->logfile = fopen(logfile,"a")) == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"regexfilter: Failed to open file '%s'.\n",
|
||||
logfile)));
|
||||
free(my_instance->match);
|
||||
free(my_instance->replace);
|
||||
free(my_instance);
|
||||
free(logfile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fprintf(my_instance->logfile,"\nOpened regex filter log\n");
|
||||
fflush(my_instance->logfile);
|
||||
}
|
||||
free(logfile);
|
||||
}
|
||||
return (FILTER *)my_instance;
|
||||
}
|
||||
@ -320,11 +350,15 @@ char *sql, *newsql;
|
||||
{
|
||||
queue = modutil_replace_SQL(queue, newsql);
|
||||
queue = gwbuf_make_contiguous(queue);
|
||||
log_match(my_instance,my_instance->match,sql,newsql);
|
||||
free(newsql);
|
||||
my_session->replacements++;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_nomatch(my_instance,my_instance->match,sql);
|
||||
my_session->no_change++;
|
||||
}
|
||||
free(sql);
|
||||
}
|
||||
|
||||
@ -441,3 +475,43 @@ regmatch_t match[10];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a matching query to either MaxScale's trace log or a separate log file.
|
||||
* The old SQL and the new SQL statements are printed in the log.
|
||||
* @param inst Regex filter instance
|
||||
* @param re Regular expression
|
||||
* @param old Old SQL statement
|
||||
* @param new New SQL statement
|
||||
*/
|
||||
void log_match(REGEX_INSTANCE* inst, char* re, char* old, char* new)
|
||||
{
|
||||
if(inst->logfile)
|
||||
{
|
||||
fprintf(inst->logfile,"Matched %s: [%s] -> [%s]\n",re,old,new);
|
||||
fflush(inst->logfile);
|
||||
}
|
||||
if(inst->log_trace)
|
||||
{
|
||||
LOGIF(LT,(skygw_log_write(LT,"Match %s: [%s] -> [%s]",re,old,new)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a non-matching query to either MaxScale's trace log or a separate log file.
|
||||
* @param inst Regex filter instance
|
||||
* @param re Regular expression
|
||||
* @param old SQL statement
|
||||
*/
|
||||
void log_nomatch(REGEX_INSTANCE* inst, char* re, char* old)
|
||||
{
|
||||
if(inst->logfile)
|
||||
{
|
||||
fprintf(inst->logfile,"No match %s: [%s]\n",re,old);
|
||||
fflush(inst->logfile);
|
||||
}
|
||||
if(inst->log_trace)
|
||||
{
|
||||
LOGIF(LT,(skygw_log_write(LT,"No match %s: [%s]",re,old)));
|
||||
}
|
||||
}
|
@ -41,10 +41,10 @@ extern __thread log_info_t tls_log_info;
|
||||
* Two optional parameters that define the behavior after a data modifying query
|
||||
* is executed:
|
||||
*
|
||||
* count=<number of queries> Queries to route to master after data modification.
|
||||
* time=<time period> Seconds to wait before queries are routed to slaves.
|
||||
* match=<regex> Regex for matching
|
||||
* ignore=<regex> Regex for ignoring
|
||||
* count=<number of queries> Queries to route to master after data modification.
|
||||
* time=<time period> Seconds to wait before queries are routed to slaves.
|
||||
* match=<regex> Regex for matching
|
||||
* ignore=<regex> Regex for ignoring
|
||||
*
|
||||
* The filter also has two options: @c case, which makes the regex case-sensitive, and @c ignorecase, which does the opposite.
|
||||
* Date Who Description
|
||||
|
@ -179,13 +179,10 @@ typedef struct {
|
||||
int residual; /* Any outstanding SQL text */
|
||||
GWBUF* tee_replybuf; /* Buffer for reply */
|
||||
GWBUF* tee_partials[2];
|
||||
GWBUF* querybuf;
|
||||
GWBUF* queue;
|
||||
SPINLOCK tee_lock;
|
||||
DCB* client_dcb;
|
||||
int statements; /*< Number of statements in the query,
|
||||
* used to identify and track multi-statement
|
||||
* queries and that both the parent and the child
|
||||
* branch are in sync. */
|
||||
|
||||
#ifdef SS_DEBUG
|
||||
long d_id;
|
||||
#endif
|
||||
@ -209,30 +206,12 @@ static SPINLOCK orphanLock;
|
||||
static int packet_is_required(GWBUF *queue);
|
||||
static int detect_loops(TEE_INSTANCE *instance, HASHTABLE* ht, SERVICE* session);
|
||||
int internal_route(DCB* dcb);
|
||||
|
||||
static int hkfn(
|
||||
void* key)
|
||||
{
|
||||
if(key == NULL){
|
||||
return 0;
|
||||
}
|
||||
unsigned int hash = 0,c = 0;
|
||||
char* ptr = (char*)key;
|
||||
while((c = *ptr++)){
|
||||
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||
}
|
||||
return *(int *)key;
|
||||
}
|
||||
|
||||
static int hcfn(
|
||||
void* v1,
|
||||
void* v2)
|
||||
{
|
||||
char* i1 = (char*) v1;
|
||||
char* i2 = (char*) v2;
|
||||
|
||||
return strcmp(i1,i2);
|
||||
}
|
||||
GWBUF* clone_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF* buffer);
|
||||
int route_single_query(TEE_INSTANCE* my_instance,
|
||||
TEE_SESSION* my_session,
|
||||
GWBUF* buffer,
|
||||
GWBUF* clone);
|
||||
int reset_session_state(TEE_SESSION* my_session, GWBUF* buffer);
|
||||
|
||||
static void
|
||||
orphan_free(void* data)
|
||||
@ -492,7 +471,7 @@ char *remote, *userName;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
HASHTABLE* ht = hashtable_alloc(100,hkfn,hcfn);
|
||||
HASHTABLE* ht = hashtable_alloc(100,simple_str_hash,strcmp);
|
||||
bool is_loop = detect_loops(my_instance,ht,session->service);
|
||||
hashtable_free(ht);
|
||||
|
||||
@ -509,12 +488,11 @@ char *remote, *userName;
|
||||
{
|
||||
my_session->active = 1;
|
||||
my_session->residual = 0;
|
||||
my_session->statements = 0;
|
||||
my_session->tee_replybuf = NULL;
|
||||
my_session->client_dcb = session->client;
|
||||
my_session->instance = my_instance;
|
||||
my_session->client_multistatement = false;
|
||||
|
||||
my_session->queue = NULL;
|
||||
spinlock_init(&my_session->tee_lock);
|
||||
if (my_instance->source &&
|
||||
(remote = session_get_remote(session)) != NULL)
|
||||
@ -812,155 +790,61 @@ setUpstream(FILTER *instance, void *session, UPSTREAM *upstream)
|
||||
static int
|
||||
routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
||||
{
|
||||
TEE_INSTANCE *my_instance = (TEE_INSTANCE *)instance;
|
||||
TEE_SESSION *my_session = (TEE_SESSION *)session;
|
||||
char *ptr;
|
||||
int length, rval, residual = 0;
|
||||
GWBUF *clone = NULL;
|
||||
unsigned char command = *((unsigned char*)queue->start + 4);
|
||||
TEE_INSTANCE *my_instance = (TEE_INSTANCE *)instance;
|
||||
TEE_SESSION *my_session = (TEE_SESSION *)session;
|
||||
char *ptr;
|
||||
int rval;
|
||||
GWBUF *buffer = NULL, *clone = NULL;
|
||||
unsigned char command = gwbuf_length(queue) >= 5 ?
|
||||
*((unsigned char*)queue->start + 4) : 1;
|
||||
|
||||
#ifdef SS_DEBUG
|
||||
skygw_log_write(LOGFILE_TRACE,"Tee routeQuery: %d : %s",
|
||||
atomic_add(&debug_seq,1),
|
||||
((char*)queue->start + 5));
|
||||
skygw_log_write(LOGFILE_TRACE,"Tee routeQuery: %d : %s",
|
||||
atomic_add(&debug_seq,1),
|
||||
((char*)queue->start + 5));
|
||||
#endif
|
||||
|
||||
|
||||
spinlock_acquire(&my_session->tee_lock);
|
||||
|
||||
if(!my_session->active)
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE, "Tee: Received a reply when the session was closed.");
|
||||
gwbuf_free(queue);
|
||||
rval = 0;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if (my_session->branch_session &&
|
||||
my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
if (my_session->residual)
|
||||
{
|
||||
clone = gwbuf_clone_all(queue);
|
||||
|
||||
if (my_session->residual < GWBUF_LENGTH(clone))
|
||||
{
|
||||
GWBUF_RTRIM(clone, GWBUF_LENGTH(clone) - residual);
|
||||
}
|
||||
my_session->residual -= GWBUF_LENGTH(clone);
|
||||
|
||||
if (my_session->residual < 0)
|
||||
{
|
||||
my_session->residual = 0;
|
||||
}
|
||||
}
|
||||
else if (my_session->active && (ptr = modutil_get_SQL(queue)) != NULL)
|
||||
{
|
||||
if ((my_instance->match == NULL ||
|
||||
regexec(&my_instance->re, ptr, 0, NULL, 0) == 0) &&
|
||||
(my_instance->nomatch == NULL ||
|
||||
regexec(&my_instance->nore,ptr,0,NULL, 0) != 0))
|
||||
{
|
||||
length = modutil_MySQL_query_len(queue, &residual);
|
||||
clone = gwbuf_clone_all(queue);
|
||||
my_session->residual = residual;
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
else if (packet_is_required(queue))
|
||||
{
|
||||
clone = gwbuf_clone_all(queue);
|
||||
}
|
||||
}
|
||||
spinlock_acquire(&my_session->tee_lock);
|
||||
|
||||
if(!my_session->active)
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE, "Tee: Received a reply when the session was closed.");
|
||||
gwbuf_free(queue);
|
||||
spinlock_release(&my_session->tee_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pass the query downstream */
|
||||
if(my_session->queue)
|
||||
{
|
||||
my_session->queue = gwbuf_append(my_session->queue,queue);
|
||||
buffer = modutil_get_next_MySQL_packet(&my_session->queue);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = modutil_get_next_MySQL_packet(&queue);
|
||||
my_session->queue = queue;
|
||||
}
|
||||
|
||||
switch(command)
|
||||
{
|
||||
case 0x1b:
|
||||
my_session->client_multistatement = *((unsigned char*) queue->start + 5);
|
||||
case 0x03:
|
||||
case 0x16:
|
||||
case 0x17:
|
||||
case 0x04:
|
||||
case 0x0a:
|
||||
memset(my_session->multipacket,(char)true,2*sizeof(bool));
|
||||
break;
|
||||
default:
|
||||
memset(my_session->multipacket,(char)false,2*sizeof(bool));
|
||||
break;
|
||||
}
|
||||
|
||||
memset(my_session->replies,0,2*sizeof(int));
|
||||
memset(my_session->reply_packets,0,2*sizeof(int));
|
||||
memset(my_session->eof,0,2*sizeof(int));
|
||||
memset(my_session->waiting,1,2*sizeof(bool));
|
||||
my_session->statements = modutil_count_statements(queue);
|
||||
my_session->command = command;
|
||||
#ifdef SS_DEBUG
|
||||
spinlock_acquire(&debug_lock);
|
||||
my_session->d_id = ++debug_id;
|
||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c [%d] command [%x]",
|
||||
my_session->d_id,
|
||||
my_session->command);
|
||||
if(command == 0x03)
|
||||
{
|
||||
char* tmpstr = modutil_get_SQL(queue);
|
||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c query: '%s'",
|
||||
tmpstr);
|
||||
free(tmpstr);
|
||||
}
|
||||
spinlock_release(&debug_lock);
|
||||
#endif
|
||||
spinlock_acquire(&my_session->tee_lock);
|
||||
if(buffer == NULL)
|
||||
{
|
||||
spinlock_release(&my_session->tee_lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
clone = clone_query(my_instance, my_session,buffer);
|
||||
spinlock_release(&my_session->tee_lock);
|
||||
|
||||
if(!my_session->active ||
|
||||
my_session->branch_session == NULL ||
|
||||
my_session->branch_session->state != SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
rval = 0;
|
||||
my_session->active = 0;
|
||||
goto retblock;
|
||||
}
|
||||
rval = my_session->down.routeQuery(my_session->down.instance,
|
||||
my_session->down.session,
|
||||
queue);
|
||||
if (clone)
|
||||
{
|
||||
my_session->n_duped++;
|
||||
|
||||
if (my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
SESSION_ROUTE_QUERY(my_session->branch_session, clone);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Close tee session */
|
||||
my_session->active = 0;
|
||||
rval = 0;
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Closed tee filter session: Child session in invalid state.")));
|
||||
gwbuf_free(clone);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (my_session->active)
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Closed tee filter session: Child session is NULL.")));
|
||||
my_session->active = 0;
|
||||
rval = 0;
|
||||
}
|
||||
my_session->n_rejected++;
|
||||
}
|
||||
retblock:
|
||||
spinlock_release(&my_session->tee_lock);
|
||||
return rval;
|
||||
/* Reset session state */
|
||||
if(!reset_session_state(my_session,buffer))
|
||||
return 0;
|
||||
|
||||
/** Route query downstream */
|
||||
spinlock_acquire(&my_session->tee_lock);
|
||||
rval = route_single_query(my_instance,my_session,buffer,clone);
|
||||
spinlock_release(&my_session->tee_lock);
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
int count_replies(GWBUF* buffer)
|
||||
@ -1043,41 +927,42 @@ uint16_t get_response_flags(uint8_t* datastart, bool ok_packet)
|
||||
static int
|
||||
clientReply (FILTER* instance, void *session, GWBUF *reply)
|
||||
{
|
||||
int rc, branch, eof;
|
||||
TEE_SESSION *my_session = (TEE_SESSION *) session;
|
||||
bool route = false,mpkt;
|
||||
GWBUF *complete = NULL;
|
||||
unsigned char *ptr;
|
||||
uint16_t flags = 0;
|
||||
int min_eof = my_session->command != 0x04 ? 2 : 1;
|
||||
int more_results = 0;
|
||||
int rc, branch, eof;
|
||||
TEE_SESSION *my_session = (TEE_SESSION *) session;
|
||||
bool route = false,mpkt;
|
||||
GWBUF *complete = NULL;
|
||||
unsigned char *ptr;
|
||||
uint16_t flags = 0;
|
||||
int min_eof = my_session->command != 0x04 ? 2 : 1;
|
||||
int more_results = 0;
|
||||
#ifdef SS_DEBUG
|
||||
ptr = (unsigned char*) reply->start;
|
||||
skygw_log_write(LOGFILE_TRACE,"Tee clientReply [%s] [%s] [%s]: %d",
|
||||
instance ? "parent":"child",
|
||||
my_session->active ? "open" : "closed",
|
||||
PTR_IS_ERR(ptr) ? "ERR" : PTR_IS_OK(ptr) ? "OK" : "RSET",
|
||||
atomic_add(&debug_seq,1));
|
||||
ptr = (unsigned char*) reply->start;
|
||||
skygw_log_write(LOGFILE_TRACE,"Tee clientReply [%s] [%s] [%s]: %d",
|
||||
instance ? "parent":"child",
|
||||
my_session->active ? "open" : "closed",
|
||||
PTR_IS_ERR(ptr) ? "ERR" : PTR_IS_OK(ptr) ? "OK" : "RSET",
|
||||
atomic_add(&debug_seq,1));
|
||||
#endif
|
||||
spinlock_acquire(&my_session->tee_lock);
|
||||
|
||||
if(!my_session->active)
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"Tee: Failed to return reply, session is closed");
|
||||
gwbuf_free(reply);
|
||||
rc = 0;
|
||||
if(my_session->waiting[PARENT])
|
||||
{
|
||||
GWBUF* errbuf = modutil_create_mysql_err_msg(1,0,1,"0000","Session closed.");
|
||||
my_session->waiting[PARENT] = false;
|
||||
my_session->up.clientReply (my_session->up.instance,
|
||||
my_session->up.session,
|
||||
errbuf);
|
||||
}
|
||||
goto retblock;
|
||||
}
|
||||
spinlock_acquire(&my_session->tee_lock);
|
||||
|
||||
branch = instance == NULL ? CHILD : PARENT;
|
||||
if(!my_session->active)
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"Tee: Failed to return reply, session is closed");
|
||||
gwbuf_free(reply);
|
||||
rc = 0;
|
||||
if(my_session->waiting[PARENT])
|
||||
{
|
||||
GWBUF* errbuf = modutil_create_mysql_err_msg(1,0,1,"0000","Session closed.");
|
||||
my_session->waiting[PARENT] = false;
|
||||
my_session->up.clientReply (my_session->up.instance,
|
||||
my_session->up.session,
|
||||
errbuf);
|
||||
}
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
branch = instance == NULL ? CHILD : PARENT;
|
||||
|
||||
my_session->tee_partials[branch] = gwbuf_append(my_session->tee_partials[branch], reply);
|
||||
my_session->tee_partials[branch] = gwbuf_make_contiguous(my_session->tee_partials[branch]);
|
||||
@ -1106,131 +991,145 @@ clientReply (FILTER* instance, void *session, GWBUF *reply)
|
||||
if(my_session->replies[branch] == 0)
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"Tee: First reply to a query for [%s].",branch == PARENT ? "PARENT":"CHILD");
|
||||
/* Reply is in a single packet if it is an OK, ERR or LOCAL_INFILE packet.
|
||||
* Otherwise the reply is a result set and the amount of packets is unknown.
|
||||
*/
|
||||
if(PTR_IS_ERR(ptr) || PTR_IS_LOCAL_INFILE(ptr) ||
|
||||
/* Reply is in a single packet if it is an OK, ERR or LOCAL_INFILE packet.
|
||||
* Otherwise the reply is a result set and the amount of packets is unknown.
|
||||
*/
|
||||
if(PTR_IS_ERR(ptr) || PTR_IS_LOCAL_INFILE(ptr) ||
|
||||
PTR_IS_OK(ptr) || !my_session->multipacket[branch] )
|
||||
{
|
||||
my_session->waiting[branch] = false;
|
||||
my_session->multipacket[branch] = false;
|
||||
if(PTR_IS_OK(ptr))
|
||||
{
|
||||
flags = get_response_flags(ptr,true);
|
||||
more_results = (flags & 0x08) && my_session->client_multistatement;
|
||||
if(more_results)
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,
|
||||
"Tee: [%s] waiting for more results.",branch == PARENT ? "PARENT":"CHILD");
|
||||
}
|
||||
}
|
||||
my_session->waiting[branch] = false;
|
||||
my_session->multipacket[branch] = false;
|
||||
if(PTR_IS_OK(ptr))
|
||||
{
|
||||
flags = get_response_flags(ptr,true);
|
||||
more_results = (flags & 0x08) && my_session->client_multistatement;
|
||||
if(more_results)
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,
|
||||
"Tee: [%s] waiting for more results.",branch == PARENT ? "PARENT":"CHILD");
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef SS_DEBUG
|
||||
else
|
||||
else
|
||||
{
|
||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c: [%d] Waiting for a result set from %s session.",
|
||||
my_session->d_id,
|
||||
branch == PARENT?"parent":"child");
|
||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c: [%d] Waiting for a result set from %s session.",
|
||||
my_session->d_id,
|
||||
branch == PARENT?"parent":"child");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if(my_session->waiting[branch])
|
||||
if(my_session->waiting[branch])
|
||||
{
|
||||
eof = modutil_count_signal_packets(complete,my_session->use_ok,my_session->eof[branch] > 0,&more_results);
|
||||
more_results &= my_session->client_multistatement;
|
||||
my_session->eof[branch] += eof;
|
||||
eof = modutil_count_signal_packets(complete,my_session->use_ok,my_session->eof[branch] > 0,&more_results);
|
||||
more_results &= my_session->client_multistatement;
|
||||
my_session->eof[branch] += eof;
|
||||
|
||||
if(my_session->eof[branch] >= min_eof)
|
||||
if(my_session->eof[branch] >= min_eof)
|
||||
{
|
||||
#ifdef SS_DEBUG
|
||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c [%d] %s received last EOF packet",
|
||||
my_session->d_id,
|
||||
branch == PARENT?"parent":"child");
|
||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c [%d] %s received last EOF packet",
|
||||
my_session->d_id,
|
||||
branch == PARENT?"parent":"child");
|
||||
#endif
|
||||
my_session->waiting[branch] = more_results;
|
||||
if(more_results)
|
||||
{
|
||||
my_session->eof[branch] = 0;
|
||||
}
|
||||
my_session->waiting[branch] = more_results;
|
||||
if(more_results)
|
||||
{
|
||||
my_session->eof[branch] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(branch == PARENT)
|
||||
if(branch == PARENT)
|
||||
{
|
||||
my_session->tee_replybuf = gwbuf_append(my_session->tee_replybuf,complete);
|
||||
my_session->tee_replybuf = gwbuf_append(my_session->tee_replybuf,complete);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
if(complete)
|
||||
gwbuf_free(complete);
|
||||
gwbuf_free(complete);
|
||||
}
|
||||
|
||||
my_session->replies[branch]++;
|
||||
rc = 1;
|
||||
mpkt = my_session->multipacket[PARENT] || my_session->multipacket[CHILD];
|
||||
|
||||
if(my_session->tee_replybuf != NULL)
|
||||
my_session->replies[branch]++;
|
||||
rc = 1;
|
||||
mpkt = my_session->multipacket[PARENT] || my_session->multipacket[CHILD];
|
||||
|
||||
if(my_session->tee_replybuf != NULL)
|
||||
{
|
||||
|
||||
if(my_session->branch_session == NULL)
|
||||
|
||||
if(my_session->branch_session == NULL)
|
||||
{
|
||||
rc = 0;
|
||||
gwbuf_free(my_session->tee_replybuf);
|
||||
my_session->tee_replybuf = NULL;
|
||||
skygw_log_write_flush(LOGFILE_ERROR,"Error : Tee child session was closed.");
|
||||
rc = 0;
|
||||
gwbuf_free(my_session->tee_replybuf);
|
||||
my_session->tee_replybuf = NULL;
|
||||
skygw_log_write_flush(LOGFILE_ERROR,"Error : Tee child session was closed.");
|
||||
}
|
||||
|
||||
if(mpkt)
|
||||
|
||||
if(mpkt)
|
||||
{
|
||||
|
||||
if(my_session->waiting[PARENT])
|
||||
if(my_session->waiting[PARENT])
|
||||
{
|
||||
route = true;
|
||||
route = true;
|
||||
|
||||
}
|
||||
else if(my_session->eof[PARENT] >= min_eof &&
|
||||
my_session->eof[CHILD] >= min_eof)
|
||||
else if(my_session->eof[PARENT] >= min_eof &&
|
||||
my_session->eof[CHILD] >= min_eof)
|
||||
{
|
||||
route = true;
|
||||
route = true;
|
||||
#ifdef SS_DEBUG
|
||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing final packet of response set.",my_session->d_id);
|
||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing final packet of response set.",my_session->d_id);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if(!my_session->waiting[PARENT] &&
|
||||
!my_session->waiting[CHILD])
|
||||
else if(!my_session->waiting[PARENT] &&
|
||||
!my_session->waiting[CHILD])
|
||||
{
|
||||
#ifdef SS_DEBUG
|
||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing single packet response.",my_session->d_id);
|
||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing single packet response.",my_session->d_id);
|
||||
#endif
|
||||
route = true;
|
||||
route = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(route)
|
||||
if(route)
|
||||
{
|
||||
#ifdef SS_DEBUG
|
||||
skygw_log_write_flush(LOGFILE_DEBUG, "tee.c:[%d] Routing buffer '%p' parent(waiting [%s] replies [%d] eof[%d])"
|
||||
" child(waiting [%s] replies[%d] eof [%d])",
|
||||
my_session->d_id,
|
||||
my_session->tee_replybuf,
|
||||
my_session->waiting[PARENT] ? "true":"false",
|
||||
my_session->replies[PARENT],
|
||||
my_session->eof[PARENT],
|
||||
my_session->waiting[CHILD]?"true":"false",
|
||||
my_session->replies[CHILD],
|
||||
my_session->eof[CHILD]);
|
||||
skygw_log_write_flush(LOGFILE_DEBUG, "tee.c:[%d] Routing buffer '%p' parent(waiting [%s] replies [%d] eof[%d])"
|
||||
" child(waiting [%s] replies[%d] eof [%d])",
|
||||
my_session->d_id,
|
||||
my_session->tee_replybuf,
|
||||
my_session->waiting[PARENT] ? "true":"false",
|
||||
my_session->replies[PARENT],
|
||||
my_session->eof[PARENT],
|
||||
my_session->waiting[CHILD]?"true":"false",
|
||||
my_session->replies[CHILD],
|
||||
my_session->eof[CHILD]);
|
||||
#endif
|
||||
|
||||
rc = my_session->up.clientReply (my_session->up.instance,
|
||||
my_session->up.session,
|
||||
my_session->tee_replybuf);
|
||||
my_session->tee_replybuf = NULL;
|
||||
rc = my_session->up.clientReply (my_session->up.instance,
|
||||
my_session->up.session,
|
||||
my_session->tee_replybuf);
|
||||
my_session->tee_replybuf = NULL;
|
||||
}
|
||||
retblock:
|
||||
spinlock_release(&my_session->tee_lock);
|
||||
return rc;
|
||||
|
||||
if(my_session->queue &&
|
||||
!my_session->waiting[PARENT] &&
|
||||
!my_session->waiting[CHILD])
|
||||
{
|
||||
GWBUF* buffer = modutil_get_next_MySQL_packet(&my_session->queue);
|
||||
GWBUF* clone = clone_query(my_session->instance,my_session,buffer);
|
||||
reset_session_state(my_session,buffer);
|
||||
route_single_query(my_session->instance,my_session,buffer,clone);
|
||||
LOGIF(LT,(skygw_log_write(LT,"tee: routing queued query")));
|
||||
|
||||
}
|
||||
|
||||
retblock:
|
||||
|
||||
spinlock_release(&my_session->tee_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1352,4 +1251,154 @@ int internal_route(DCB* dcb)
|
||||
TEE_SESSION* session = dcb->data;
|
||||
|
||||
return routeQuery((FILTER*)session->instance,session,buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param my_instance
|
||||
* @param my_session
|
||||
* @param buffer
|
||||
* @return
|
||||
*/
|
||||
GWBUF* clone_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF* buffer)
|
||||
{
|
||||
GWBUF* clone = NULL;
|
||||
int length, residual = 0;
|
||||
char* ptr;
|
||||
|
||||
if (my_session->branch_session &&
|
||||
my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
if (my_session->residual)
|
||||
{
|
||||
clone = gwbuf_clone_all(buffer);
|
||||
|
||||
if (my_session->residual < GWBUF_LENGTH(clone))
|
||||
{
|
||||
GWBUF_RTRIM(clone, GWBUF_LENGTH(clone) - residual);
|
||||
}
|
||||
my_session->residual -= GWBUF_LENGTH(clone);
|
||||
|
||||
if (my_session->residual < 0)
|
||||
{
|
||||
my_session->residual = 0;
|
||||
}
|
||||
}
|
||||
else if (my_session->active && (ptr = modutil_get_SQL(buffer)) != NULL)
|
||||
{
|
||||
if ((my_instance->match == NULL ||
|
||||
regexec(&my_instance->re, ptr, 0, NULL, 0) == 0) &&
|
||||
(my_instance->nomatch == NULL ||
|
||||
regexec(&my_instance->nore,ptr,0,NULL, 0) != 0))
|
||||
{
|
||||
length = modutil_MySQL_query_len(buffer, &residual);
|
||||
clone = gwbuf_clone_all(buffer);
|
||||
my_session->residual = residual;
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
else if (packet_is_required(buffer))
|
||||
{
|
||||
clone = gwbuf_clone_all(buffer);
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route the main query downstream along the main filter chain and possibly route
|
||||
* a clone of the buffer to the branch session. If the clone buffer is NULL, nothing
|
||||
* is routed to the branch session.
|
||||
* @param my_instance Tee instance
|
||||
* @param my_session Tee session
|
||||
* @param buffer Main buffer
|
||||
* @param clone Cloned buffer
|
||||
* @return 1 on success, 0 on failure.
|
||||
*/
|
||||
int route_single_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF* buffer, GWBUF* clone)
|
||||
{
|
||||
int rval = 0;
|
||||
if(!my_session->active ||
|
||||
my_session->branch_session == NULL ||
|
||||
my_session->branch_session->state != SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
rval = 0;
|
||||
my_session->active = 0;
|
||||
return rval;
|
||||
}
|
||||
rval = my_session->down.routeQuery(my_session->down.instance,
|
||||
my_session->down.session,
|
||||
buffer);
|
||||
if (clone)
|
||||
{
|
||||
my_session->n_duped++;
|
||||
|
||||
if (my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
SESSION_ROUTE_QUERY(my_session->branch_session, clone);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Close tee session */
|
||||
my_session->active = 0;
|
||||
rval = 0;
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Closed tee filter session: Child session in invalid state.")));
|
||||
gwbuf_free(clone);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (my_session->active)
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Closed tee filter session: Child session is NULL.")));
|
||||
my_session->active = 0;
|
||||
rval = 0;
|
||||
}
|
||||
my_session->n_rejected++;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the session's internal counters.
|
||||
* @param my_session Tee session
|
||||
* @param buffer Buffer with the query of the main branch in it
|
||||
* @return 1 on success, 0 on error
|
||||
*/
|
||||
int reset_session_state(TEE_SESSION* my_session, GWBUF* buffer)
|
||||
{
|
||||
if(gwbuf_length(buffer) < 5)
|
||||
return 0;
|
||||
|
||||
unsigned char command = *((unsigned char*)buffer->start + 4);
|
||||
|
||||
switch(command)
|
||||
{
|
||||
case 0x1b:
|
||||
my_session->client_multistatement = *((unsigned char*) buffer->start + 5);
|
||||
LOGIF(LT,(skygw_log_write(LT,"Tee: client %s multistatements",
|
||||
my_session->client_multistatement ? "enabled":"disabled")));
|
||||
case 0x03:
|
||||
case 0x16:
|
||||
case 0x17:
|
||||
case 0x04:
|
||||
case 0x0a:
|
||||
memset(my_session->multipacket,(char)true,2*sizeof(bool));
|
||||
break;
|
||||
default:
|
||||
memset(my_session->multipacket,(char)false,2*sizeof(bool));
|
||||
break;
|
||||
}
|
||||
|
||||
memset(my_session->replies,0,2*sizeof(int));
|
||||
memset(my_session->reply_packets,0,2*sizeof(int));
|
||||
memset(my_session->eof,0,2*sizeof(int));
|
||||
memset(my_session->waiting,1,2*sizeof(bool));
|
||||
my_session->command = command;
|
||||
|
||||
return 1;
|
||||
}
|
@ -1,13 +1,7 @@
|
||||
aux_source_directory(${CMAKE_SOURCE_DIR}/server/core CORE_ALL)
|
||||
foreach(VAR ${CORE_ALL})
|
||||
if(NOT( (${VAR} MATCHES "max[a-z_]*.c") OR (${VAR} MATCHES "gateway.c")))
|
||||
list(APPEND CORE ${VAR})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_executable(harness_ui harness_ui.c harness_common.c)
|
||||
add_executable(harness harness_util.c harness_common.c ${CORE})
|
||||
add_executable(harness harness_util.c harness_common.c)
|
||||
target_link_libraries(harness_ui fullcore log_manager utils)
|
||||
target_link_libraries(harness fullcore)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${ERRMSG} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
@ -150,6 +150,7 @@ typedef struct mysql_sescmd_st {
|
||||
unsigned char reply_cmd; /*< The reply command. One of OK, ERR, RESULTSET or
|
||||
* LOCAL_INFILE. Slave servers are compared to this
|
||||
* when they return session command replies.*/
|
||||
int position; /*< Position of this command */
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t my_sescmd_chk_tail;
|
||||
#endif
|
||||
@ -184,6 +185,7 @@ typedef struct sescmd_cursor_st {
|
||||
rses_property_t** scmd_cur_ptr_property; /*< address of pointer to owner property */
|
||||
mysql_sescmd_t* scmd_cur_cmd; /*< pointer to current session command */
|
||||
bool scmd_cur_active; /*< true if command is being executed */
|
||||
int position; /*< Position of this cursor */
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t scmd_cur_chk_tail;
|
||||
#endif
|
||||
@ -248,6 +250,8 @@ typedef struct rwsplit_config_st {
|
||||
int rw_max_slave_replication_lag;
|
||||
target_t rw_use_sql_variables_in;
|
||||
int rw_max_sescmd_history_size;
|
||||
bool disable_sescmd_hist;
|
||||
bool disable_slave_recovery;
|
||||
} rwsplit_config_t;
|
||||
|
||||
|
||||
@ -291,6 +295,7 @@ struct router_client_session {
|
||||
bool rses_autocommit_enabled;
|
||||
bool rses_transaction_active;
|
||||
DCB* client_dcb;
|
||||
int pos_generator;
|
||||
#if defined(PREP_STMT_CACHING)
|
||||
HASHTABLE* rses_prep_stmt[2];
|
||||
#endif
|
||||
|
@ -136,6 +136,7 @@ typedef struct mysql_sescmd_st {
|
||||
GWBUF* my_sescmd_buf; /*< Query buffer */
|
||||
unsigned char my_sescmd_packet_type;/*< Packet type */
|
||||
bool my_sescmd_is_replied; /*< Is cmd replied to client */
|
||||
int position; /*< Position of this command */
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t my_sescmd_chk_tail;
|
||||
#endif
|
||||
@ -170,6 +171,7 @@ typedef struct sescmd_cursor_st {
|
||||
rses_property_t** scmd_cur_ptr_property; /*< address of pointer to owner property */
|
||||
mysql_sescmd_t* scmd_cur_cmd; /*< pointer to current session command */
|
||||
bool scmd_cur_active; /*< true if command is being executed */
|
||||
int position; /*< Position of this cursor */
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t scmd_cur_chk_tail;
|
||||
#endif
|
||||
@ -215,10 +217,13 @@ typedef struct backend_ref_st {
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t bref_chk_top;
|
||||
#endif
|
||||
int n_mapping_eof;
|
||||
GWBUF* map_queue;
|
||||
BACKEND* bref_backend; /*< Backend server */
|
||||
DCB* bref_dcb; /*< Backend DCB */
|
||||
bref_state_t bref_state; /*< State of the backend */
|
||||
bool bref_mapped; /*< Whether the backend has been mapped */
|
||||
bool last_sescmd_replied;
|
||||
int bref_num_result_wait; /*< Number of not yet received results */
|
||||
sescmd_cursor_t bref_sescmd_cur; /*< Session command cursor */
|
||||
GWBUF* bref_pending_cmd; /*< For stmt which can't be routed due active sescmd execution */
|
||||
@ -233,9 +238,25 @@ typedef struct backend_ref_st {
|
||||
typedef struct schemarouter_config_st {
|
||||
int rw_max_slave_conn_percent;
|
||||
int rw_max_slave_conn_count;
|
||||
target_t rw_use_sql_variables_in;
|
||||
target_t rw_use_sql_variables_in;
|
||||
int max_sescmd_hist;
|
||||
bool disable_sescmd_hist;
|
||||
} schemarouter_config_t;
|
||||
|
||||
|
||||
/**
|
||||
* The statistics for this router instance
|
||||
*/
|
||||
typedef struct {
|
||||
int n_queries; /*< Number of queries forwarded */
|
||||
int n_sescmd; /*< Number of session commands */
|
||||
int longest_sescmd; /*< Longest chain of stored session commands */
|
||||
int n_hist_exceeded;/*< Number of sessions that exceeded session
|
||||
* command history limit */
|
||||
int sessions;
|
||||
double ses_longest; /*< Longest session */
|
||||
double ses_shortest; /*< Shortest session */
|
||||
double ses_average; /*< Average session length */
|
||||
} ROUTER_STATS;
|
||||
|
||||
/**
|
||||
* The client session structure used within this router.
|
||||
@ -266,22 +287,14 @@ struct router_client_session {
|
||||
GWBUF* queue; /*< Query that was received before the session was ready */
|
||||
DCB* dcb_route; /*< Internal DCB used to trigger re-routing of buffers */
|
||||
DCB* dcb_reply; /*< Internal DCB used to send replies to the client */
|
||||
ROUTER_STATS stats; /*< Statistics for this router */
|
||||
int n_sescmd;
|
||||
int pos_generator;
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t rses_chk_tail;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* The statistics for this router instance
|
||||
*/
|
||||
typedef struct {
|
||||
int n_sessions; /*< Number sessions created */
|
||||
int n_queries; /*< Number of queries forwarded */
|
||||
int n_master; /*< Number of stmts sent to master */
|
||||
int n_slave; /*< Number of stmts sent to slave */
|
||||
int n_all; /*< Number of stmts sent to all */
|
||||
} ROUTER_STATS;
|
||||
|
||||
|
||||
/**
|
||||
* The per instance data for the router.
|
||||
@ -299,15 +312,10 @@ typedef struct router_instance {
|
||||
ROUTER_STATS stats; /*< Statistics for this router */
|
||||
struct router_instance* next; /*< Next router on the list */
|
||||
bool available_slaves; /*< The router has some slaves available */
|
||||
//HASHTABLE* dbnames_hash; /** Hashtable containing the database names and where to find them */
|
||||
//char** ignore_list;
|
||||
|
||||
} ROUTER_INSTANCE;
|
||||
|
||||
#define BACKEND_TYPE(b) (SERVER_IS_MASTER((b)->backend_server) ? BE_MASTER : \
|
||||
(SERVER_IS_SLAVE((b)->backend_server) ? BE_SLAVE : BE_UNDEFINED));
|
||||
#if 0
|
||||
void* dbnames_hash_init(ROUTER_INSTANCE* inst,BACKEND** backends);
|
||||
bool update_dbnames_hash(ROUTER_INSTANCE* inst,BACKEND** backends, HASHTABLE* hashtable);
|
||||
#endif
|
||||
|
||||
#endif /*< _SCHEMAROUTER_H */
|
||||
|
18
server/modules/include/sharding_common.h
Normal file
18
server/modules/include/sharding_common.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef _SHARDING_COMMON_HG
|
||||
#define _SHARDING_COMMON_HG
|
||||
|
||||
#include <poll.h>
|
||||
#include <buffer.h>
|
||||
#include <modutil.h>
|
||||
#include <mysql_client_server_protocol.h>
|
||||
#include <hashtable.h>
|
||||
#include <log_manager.h>
|
||||
#include <query_classifier.h>
|
||||
|
||||
bool extract_database(GWBUF* buf, char* str);
|
||||
void create_error_reply(char* fail_str,DCB* dcb);
|
||||
bool change_current_db(MYSQL_session* mysql_session,
|
||||
HASHTABLE* dbhash,
|
||||
GWBUF* buf);
|
||||
|
||||
#endif
|
@ -32,6 +32,7 @@
|
||||
* 24/06/14 Massimiliano Pinto Added depth level 0 for each node
|
||||
* 30/10/14 Massimiliano Pinto Added disableMasterFailback feature
|
||||
* 10/11/14 Massimiliano Pinto Added setNetworkTimeout for connect,read,write
|
||||
* 20/05/15 Guillaume Lefranc Added availableWhenDonor feature
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -158,6 +159,7 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
handle->id = MONITOR_DEFAULT_ID;
|
||||
handle->interval = MONITOR_INTERVAL;
|
||||
handle->disableMasterFailback = 0;
|
||||
handle->availableWhenDonor = 0;
|
||||
handle->master = NULL;
|
||||
handle->connect_timeout=DEFAULT_CONNECT_TIMEOUT;
|
||||
handle->read_timeout=DEFAULT_READ_TIMEOUT;
|
||||
@ -170,6 +172,8 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
{
|
||||
if(!strcmp(params->name,"disable_master_failback"))
|
||||
handle->disableMasterFailback = config_truth_value(params->value);
|
||||
if(!strcmp(params->name,"available_when_donor"))
|
||||
handle->availableWhenDonor = config_truth_value(params->value);
|
||||
params = params->next;
|
||||
}
|
||||
|
||||
@ -289,6 +293,7 @@ char *sep;
|
||||
|
||||
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval);
|
||||
dcb_printf(dcb,"\tMaster Failback:\t%s\n", (handle->disableMasterFailback == 1) ? "off" : "on");
|
||||
dcb_printf(dcb,"\tAvailable when Donor:\t%s\n", (handle->availableWhenDonor == 1) ? "on" : "off");
|
||||
dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", handle->connect_timeout);
|
||||
dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", handle->read_timeout);
|
||||
dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", handle->write_timeout);
|
||||
@ -428,14 +433,28 @@ char *server_string;
|
||||
}
|
||||
|
||||
/* Check if the the Galera FSM shows this node is joined to the cluster */
|
||||
if (mysql_query(database->con, "SHOW STATUS LIKE 'wsrep_local_state_comment'") == 0
|
||||
if (mysql_query(database->con, "SHOW STATUS LIKE 'wsrep_local_state'") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
if (strncasecmp(row[1], "SYNCED", 3) == 0)
|
||||
if (strcmp(row[1], "4") == 0)
|
||||
isjoined = 1;
|
||||
|
||||
/* Check if the node is a donor and is using xtrabackup, in this case it can stay alive */
|
||||
else if (strcmp(row[1], "2") == 0 && handle->availableWhenDonor == 1) {
|
||||
if (mysql_query(database->con, "SHOW VARIABLES LIKE 'wsrep_sst_method'") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
if (strncmp(row[1], "xtrabackup", 10) == 0)
|
||||
isjoined = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
@ -732,6 +751,22 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
memcpy(&handle->disableMasterFailback, &disable, sizeof(int));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow a Galera node to be in sync when Donor.
|
||||
*
|
||||
* When enabled, the monitor will check if the node is using xtrabackup or xtrabackup-v2
|
||||
* as SST method. In that case, node will stay as synced.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param disable To allow sync status use 1, 0 for traditional behavior
|
||||
*/
|
||||
static void
|
||||
availableWhenDonor(void *arg, int disable)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
memcpy(&handle->availableWhenDonor, &disable, sizeof(int));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timeouts to use in the monitor.
|
||||
*
|
||||
|
@ -531,18 +531,20 @@ char *server_string;
|
||||
}
|
||||
|
||||
/* get variable 'read_only' set by an external component */
|
||||
if (mysql_query(database->con, "SHOW GLOBAL VARIABLES LIKE 'read_only'") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
if (strncasecmp(row[1], "OFF", 3) == 0) {
|
||||
ismaster = 1;
|
||||
}
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
if (mysql_query(database->con, "SHOW GLOBAL VARIABLES LIKE 'read_only'") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
if (strncasecmp(row[1], "OFF", 3) == 0) {
|
||||
ismaster = 1;
|
||||
} else {
|
||||
isslave = 1;
|
||||
}
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
|
||||
/* Remove addition info */
|
||||
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
|
||||
@ -563,7 +565,7 @@ char *server_string;
|
||||
}
|
||||
|
||||
/* Set the Master role */
|
||||
if (isslave && ismaster)
|
||||
if (ismaster)
|
||||
{
|
||||
monitor_clear_pending_status(database, SERVER_SLAVE);
|
||||
monitor_set_pending_status(database, SERVER_MASTER);
|
||||
|
@ -820,7 +820,7 @@ int log_no_master = 1;
|
||||
mon_status_changed(root_master) &&
|
||||
!(root_master->server->status & SERVER_STALE_STATUS))
|
||||
{
|
||||
if (root_master->pending_status & (SERVER_MASTER)) {
|
||||
if (root_master->pending_status & (SERVER_MASTER) && SERVER_IS_RUNNING(root_master->server)) {
|
||||
if (!(root_master->mon_prev_status & SERVER_STALE_STATUS) &&
|
||||
!(root_master->server->status & SERVER_MAINT))
|
||||
{
|
||||
|
@ -35,6 +35,7 @@
|
||||
* 28/08/14 Massimiliano Pinto Addition of detectStaleMaster
|
||||
* 30/10/14 Massimiliano Pinto Addition of disableMasterFailback
|
||||
* 07/11/14 Massimiliano Pinto Addition of NetworkTimeout: connect, read, write
|
||||
* 20/05/15 Guillaume Lefranc Addition of availableWhenDonor
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -68,6 +69,7 @@ typedef struct {
|
||||
int replicationHeartbeat; /**< Monitor flag for MySQL replication heartbeat */
|
||||
int detectStaleMaster; /**< Monitor flag for MySQL replication Stale Master detection */
|
||||
int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */
|
||||
int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */
|
||||
MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */
|
||||
MONITOR_SERVERS *databases; /**< Linked list of servers to monitor */
|
||||
int connect_timeout; /**< Connect timeout in seconds for mysql_real_connect */
|
||||
|
@ -1015,6 +1015,9 @@ int gw_MySQLListener(
|
||||
int one = 1;
|
||||
int rc;
|
||||
|
||||
memset(&serv_addr,0,sizeof(serv_addr));
|
||||
memset(&local_addr,0,sizeof(local_addr));
|
||||
|
||||
if (strchr(config_bind, '/')) {
|
||||
char *tmp = strrchr(config_bind, ':');
|
||||
if (tmp)
|
||||
|
@ -1524,7 +1524,19 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password,
|
||||
dcb->remote)));
|
||||
|
||||
user_password = mysql_users_fetch(service->users, &key);
|
||||
|
||||
|
||||
if (user_password)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/** See if ANYDB == Y */
|
||||
if(key.resource)
|
||||
{
|
||||
key.resource = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!user_password) {
|
||||
/*
|
||||
* user@% not found.
|
||||
@ -1546,7 +1558,6 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password,
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,14 +5,6 @@ if(BUILD_TESTS)
|
||||
install(TARGETS testroute DESTINATION modules)
|
||||
endif()
|
||||
|
||||
add_library(schemarouter SHARED schemarouter/schemarouter.c)
|
||||
target_link_libraries(schemarouter log_manager utils query_classifier)
|
||||
install(TARGETS schemarouter DESTINATION modules)
|
||||
|
||||
add_library(shardrouter SHARED schemarouter/shardrouter.c)
|
||||
target_link_libraries(shardrouter log_manager utils query_classifier)
|
||||
install(TARGETS shardrouter DESTINATION modules)
|
||||
|
||||
add_library(readconnroute SHARED readconnroute.c)
|
||||
target_link_libraries(readconnroute log_manager utils)
|
||||
install(TARGETS readconnroute DESTINATION modules)
|
||||
@ -30,7 +22,7 @@ target_link_libraries(cli log_manager utils)
|
||||
install(TARGETS cli DESTINATION modules)
|
||||
|
||||
add_subdirectory(readwritesplit)
|
||||
add_subdirectory(schemarouter/test)
|
||||
add_subdirectory(schemarouter)
|
||||
if(BUILD_BINLOG)
|
||||
add_subdirectory(binlog)
|
||||
endif()
|
||||
|
@ -164,6 +164,24 @@ blr_file_rotate(ROUTER_INSTANCE *router, char *file, uint64_t pos)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* binlog files need an initial 4 magic bytes at the start. blr_file_add_magic()
|
||||
* adds them.
|
||||
*
|
||||
* @param router The router instance
|
||||
* @param fd file descriptor to the open binlog file
|
||||
* @return Nothing
|
||||
*/
|
||||
static void
|
||||
blr_file_add_magic(ROUTER_INSTANCE *router, int fd)
|
||||
{
|
||||
unsigned char magic[] = BINLOG_MAGIC;
|
||||
|
||||
write(fd, magic, 4);
|
||||
router->binlog_position = 4; /* Initial position after the magic number */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new binlog file for the router to use.
|
||||
*
|
||||
@ -176,7 +194,6 @@ blr_file_create(ROUTER_INSTANCE *router, char *file)
|
||||
{
|
||||
char path[1024];
|
||||
int fd;
|
||||
unsigned char magic[] = BINLOG_MAGIC;
|
||||
|
||||
strcpy(path, router->binlogdir);
|
||||
strcat(path, "/");
|
||||
@ -184,7 +201,7 @@ unsigned char magic[] = BINLOG_MAGIC;
|
||||
|
||||
if ((fd = open(path, O_RDWR|O_CREAT, 0666)) != -1)
|
||||
{
|
||||
write(fd, magic, 4);
|
||||
blr_file_add_magic(router,fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -197,7 +214,7 @@ unsigned char magic[] = BINLOG_MAGIC;
|
||||
close(router->binlog_fd);
|
||||
spinlock_acquire(&router->binlog_lock);
|
||||
strncpy(router->binlog_name, file,BINLOG_FNAMELEN);
|
||||
router->binlog_position = 4; /* Initial position after the magic number */
|
||||
blr_file_add_magic(router, fd);
|
||||
spinlock_release(&router->binlog_lock);
|
||||
router->binlog_fd = fd;
|
||||
return 1;
|
||||
@ -232,6 +249,19 @@ int fd;
|
||||
spinlock_acquire(&router->binlog_lock);
|
||||
strncpy(router->binlog_name, file,BINLOG_FNAMELEN);
|
||||
router->binlog_position = lseek(fd, 0L, SEEK_END);
|
||||
if (router->binlog_position < 4) {
|
||||
if (router->binlog_position == 0) {
|
||||
blr_file_add_magic(router, fd);
|
||||
} else {
|
||||
/* If for any reason the file's length is between 1 and 3 bytes
|
||||
* then report an error. */
|
||||
LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
|
||||
"%s: binlog file %s has an invalid length %d.",
|
||||
router->service->name, path, router->binlog_position)));
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
spinlock_release(&router->binlog_lock);
|
||||
router->binlog_fd = fd;
|
||||
}
|
||||
|
@ -1356,7 +1356,7 @@ int action;
|
||||
"Slave %d is ahead of expected position %s@%d. "
|
||||
"Expected position %d",
|
||||
slave->serverid, slave->binlogfile,
|
||||
slave->binlog_pos,
|
||||
(unsigned long)slave->binlog_pos,
|
||||
hdr->next_pos - hdr->event_size)));
|
||||
spinlock_acquire(&slave->catch_lock);
|
||||
slave->cstate &= ~(CS_UPTODATE|CS_BUSY);
|
||||
|
@ -1203,7 +1203,7 @@ uint32_t chksum;
|
||||
"%s: COM_BINLOG_DUMP: binlog name '%s', length %d, "
|
||||
"from position %lu.", router->service->name,
|
||||
slave->binlogfile, binlognamelen,
|
||||
slave->binlog_pos)));
|
||||
(unsigned long)slave->binlog_pos)));
|
||||
|
||||
slave->seqno = 1;
|
||||
|
||||
@ -1261,7 +1261,7 @@ uint32_t chksum;
|
||||
"%s: New slave %s, server id %d, requested binlog file %s from position %lu",
|
||||
router->service->name, slave->dcb->remote,
|
||||
slave->serverid,
|
||||
slave->binlogfile, slave->binlog_pos)));
|
||||
slave->binlogfile, (unsigned long)slave->binlog_pos)));
|
||||
|
||||
if (slave->binlog_pos != router->binlog_position ||
|
||||
strcmp(slave->binlogfile, router->binlog_name) != 0)
|
||||
@ -1532,7 +1532,7 @@ if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write(
|
||||
"%s: Slave %s is up to date %s, %lu.",
|
||||
router->service->name,
|
||||
slave->dcb->remote,
|
||||
slave->binlogfile, slave->binlog_pos)));
|
||||
slave->binlogfile, (unsigned long)slave->binlog_pos)));
|
||||
}
|
||||
else if ((slave->stats.n_caughtup % 50) == 0)
|
||||
{
|
||||
@ -1540,7 +1540,7 @@ if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write(
|
||||
"%s: Slave %s is up to date %s, %lu.",
|
||||
router->service->name,
|
||||
slave->dcb->remote,
|
||||
slave->binlogfile, slave->binlog_pos)));
|
||||
slave->binlogfile, (unsigned long)slave->binlog_pos)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1565,7 +1565,7 @@ if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write(
|
||||
"which is not the file currently being downloaded. "
|
||||
"Master binlog is %s, %lu. This may be caused by a "
|
||||
"previous failure of the master.",
|
||||
slave->binlogfile, slave->binlog_pos,
|
||||
slave->binlogfile, (unsigned long)slave->binlog_pos,
|
||||
router->binlog_name, router->binlog_position)));
|
||||
if (blr_slave_fake_rotate(router, slave))
|
||||
{
|
||||
|
@ -755,20 +755,10 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
break;
|
||||
}
|
||||
|
||||
CHK_PROTOCOL(((MySQLProtocol*)backend_dcb->protocol));
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [readconnroute:routeQuery] Routed command %d to dcb %p "
|
||||
"with return value %d.",
|
||||
pthread_self(),
|
||||
mysql_command,
|
||||
backend_dcb,
|
||||
rc)));
|
||||
|
||||
LOGIF(LOGFILE_TRACE,skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Routed command [%#x] to '%s'%s%s",
|
||||
mysql_command,
|
||||
LOGFILE_DEBUG|LOGFILE_TRACE,
|
||||
"Routed [%s] to '%s'%s%s",
|
||||
STRPACKETTYPE(mysql_command),
|
||||
backend_dcb->server->unique_name,
|
||||
trc?": ":".",
|
||||
trc?trc:""));
|
||||
|
@ -628,7 +628,7 @@ createInstance(SERVICE *service, char **options)
|
||||
* If server weighting has been defined calculate the percentage
|
||||
* of load that will be sent to each server. This is only used for
|
||||
* calculating the least connections, either globally or within a
|
||||
* service, or the numebr of current operations on a server.
|
||||
* service, or the number of current operations on a server.
|
||||
*/
|
||||
if ((weightby = serviceGetWeightingParameter(service)) != NULL)
|
||||
{
|
||||
@ -698,6 +698,13 @@ createInstance(SERVICE *service, char **options)
|
||||
{
|
||||
rwsplit_process_router_options(router, options);
|
||||
}
|
||||
|
||||
/** These options cancel each other out */
|
||||
if(router->rwsplit_config.disable_sescmd_hist && router->rwsplit_config.rw_max_sescmd_history_size > 0)
|
||||
{
|
||||
router->rwsplit_config.rw_max_sescmd_history_size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default value for max_slave_connections and for slave selection
|
||||
* criteria. If parameter is set in config file max_slave_connections
|
||||
@ -807,7 +814,7 @@ static void* newSession(
|
||||
rwsplit_process_router_options(router, router->service->routerOptions);
|
||||
}
|
||||
/** Copy config struct from router instance */
|
||||
client_rses->rses_config = router->rwsplit_config;
|
||||
memcpy(&client_rses->rses_config,&router->rwsplit_config,sizeof(rwsplit_config_t));
|
||||
|
||||
spinlock_release(&router->lock);
|
||||
/**
|
||||
@ -1876,7 +1883,6 @@ static int routeQuery(
|
||||
bool succp = false;
|
||||
|
||||
CHK_CLIENT_RSES(router_cli_ses);
|
||||
|
||||
/**
|
||||
* GWBUF is called "type undefined" when the incoming data isn't parsed
|
||||
* and MySQL packets haven't been extracted to separate buffers.
|
||||
@ -2782,16 +2788,16 @@ static void clientReply (
|
||||
bool rconn = false;
|
||||
writebuf = sescmd_cursor_process_replies(writebuf, bref, &rconn);
|
||||
|
||||
if(rconn)
|
||||
if(rconn && !router_inst->rwsplit_config.disable_slave_recovery)
|
||||
{
|
||||
select_connect_backend_servers(&router_cli_ses->rses_master_ref,
|
||||
router_cli_ses->rses_backend_ref,
|
||||
router_cli_ses->rses_nbackends,
|
||||
router_cli_ses->rses_config.rw_max_slave_conn_count,
|
||||
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);
|
||||
select_connect_backend_servers(&router_cli_ses->rses_master_ref,
|
||||
router_cli_ses->rses_backend_ref,
|
||||
router_cli_ses->rses_nbackends,
|
||||
router_cli_ses->rses_config.rw_max_slave_conn_count,
|
||||
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);
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -3667,7 +3673,8 @@ static mysql_sescmd_t* mysql_sescmd_init (
|
||||
/** Set session command buffer */
|
||||
sescmd->my_sescmd_buf = sescmd_buf;
|
||||
sescmd->my_sescmd_packet_type = packet_type;
|
||||
|
||||
sescmd->position = atomic_add(&rses->pos_generator,1);
|
||||
|
||||
return sescmd;
|
||||
}
|
||||
|
||||
@ -3708,13 +3715,11 @@ static GWBUF* sescmd_cursor_process_replies(
|
||||
mysql_sescmd_t* scmd;
|
||||
sescmd_cursor_t* scur;
|
||||
ROUTER_CLIENT_SES* ses;
|
||||
ROUTER_INSTANCE* router;
|
||||
|
||||
scur = &bref->bref_sescmd_cur;
|
||||
ss_dassert(SPINLOCK_IS_LOCKED(&(scur->scmd_cur_rses->rses_lock)));
|
||||
scmd = sescmd_cursor_get_command(scur);
|
||||
ses = (*scur->scmd_cur_ptr_property)->rses_prop_rsession;
|
||||
router = ses->router;
|
||||
CHK_GWBUF(replybuf);
|
||||
|
||||
/**
|
||||
@ -3724,6 +3729,7 @@ static GWBUF* sescmd_cursor_process_replies(
|
||||
while (scmd != NULL && replybuf != NULL)
|
||||
{
|
||||
bref->reply_cmd = *((unsigned char*)replybuf->start + 4);
|
||||
scur->position = scmd->position;
|
||||
/** Faster backend has already responded to client : discard */
|
||||
if (scmd->my_sescmd_is_replied)
|
||||
{
|
||||
@ -4361,6 +4367,43 @@ static bool route_session_write(
|
||||
goto return_succp;
|
||||
}
|
||||
|
||||
if(router_cli_ses->rses_config.disable_sescmd_hist)
|
||||
{
|
||||
rses_property_t *prop, *tmp;
|
||||
backend_ref_t* bref;
|
||||
bool conflict;
|
||||
|
||||
prop = router_cli_ses->rses_properties[RSES_PROP_TYPE_SESCMD];
|
||||
while(prop)
|
||||
{
|
||||
conflict = false;
|
||||
|
||||
for(i = 0;i<router_cli_ses->rses_nbackends;i++)
|
||||
{
|
||||
bref = &backend_ref[i];
|
||||
if(BREF_IS_IN_USE(bref))
|
||||
{
|
||||
|
||||
if(bref->bref_sescmd_cur.position <= prop->rses_prop_data.sescmd.position)
|
||||
{
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(conflict)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
tmp = prop;
|
||||
router_cli_ses->rses_properties[RSES_PROP_TYPE_SESCMD] = prop->rses_prop_next;
|
||||
rses_property_done(tmp);
|
||||
prop = router_cli_ses->rses_properties[RSES_PROP_TYPE_SESCMD];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional reference is created to querybuf to
|
||||
* prevent it from being released before properties
|
||||
@ -4538,6 +4581,14 @@ static void rwsplit_process_router_options(
|
||||
{
|
||||
router->rwsplit_config.rw_max_sescmd_history_size = atoi(value);
|
||||
}
|
||||
else if(strcmp(options[i],"disable_sescmd_history") == 0)
|
||||
{
|
||||
router->rwsplit_config.disable_sescmd_hist = config_truth_value(value);
|
||||
}
|
||||
else if(strcmp(options[i],"disable_slave_recovery") == 0)
|
||||
{
|
||||
router->rwsplit_config.disable_slave_recovery = config_truth_value(value);
|
||||
}
|
||||
}
|
||||
} /*< for */
|
||||
}
|
||||
@ -4780,6 +4831,12 @@ static bool handle_error_new_connection(
|
||||
* Try to get replacement slave or at least the minimum
|
||||
* number of slave connections for router session.
|
||||
*/
|
||||
if(inst->rwsplit_config.disable_slave_recovery)
|
||||
{
|
||||
succp = have_enough_servers(&rses,1,router_nservers,inst) ? true : false;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = select_connect_backend_servers(
|
||||
&rses->rses_master_ref,
|
||||
rses->rses_backend_ref,
|
||||
@ -4789,6 +4846,7 @@ static bool handle_error_new_connection(
|
||||
rses->rses_config.rw_slave_select_criteria,
|
||||
ses,
|
||||
inst);
|
||||
}
|
||||
|
||||
return_succp:
|
||||
return succp;
|
||||
|
11
server/modules/routing/schemarouter/CMakeLists.txt
Normal file
11
server/modules/routing/schemarouter/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
add_library(schemarouter SHARED schemarouter.c sharding_common.c)
|
||||
target_link_libraries(schemarouter log_manager utils query_classifier)
|
||||
install(TARGETS schemarouter DESTINATION modules)
|
||||
|
||||
add_library(shardrouter SHARED shardrouter.c svcconn.c sharding_common.c)
|
||||
target_link_libraries(shardrouter log_manager utils query_classifier)
|
||||
install(TARGETS shardrouter DESTINATION modules)
|
||||
|
||||
if(BUILD_TESTS)
|
||||
add_subdirectory(test)
|
||||
endif()
|
@ -23,6 +23,7 @@
|
||||
#include <stdint.h>
|
||||
#include <router.h>
|
||||
#include <schemarouter.h>
|
||||
#include <sharding_common.h>
|
||||
#include <secrets.h>
|
||||
#include <mysql.h>
|
||||
#include <skygw_utils.h>
|
||||
@ -208,12 +209,6 @@ static ROUTER_INSTANCE* instances;
|
||||
static int hashkeyfun(void* key);
|
||||
static int hashcmpfun (void *, void *);
|
||||
|
||||
static bool change_current_db(
|
||||
ROUTER_INSTANCE* inst,
|
||||
ROUTER_CLIENT_SES* rses,
|
||||
GWBUF* buf);
|
||||
|
||||
|
||||
static int hashkeyfun(void* key)
|
||||
{
|
||||
if(key == NULL){
|
||||
@ -250,7 +245,7 @@ char* get_lenenc_str(void* data, int* len)
|
||||
{
|
||||
unsigned char* ptr = (unsigned char*)data;
|
||||
char* rval;
|
||||
long size, offset;
|
||||
unsigned long size, offset;
|
||||
|
||||
if(data == NULL || len == NULL)
|
||||
{
|
||||
@ -279,9 +274,14 @@ char* get_lenenc_str(void* data, int* len)
|
||||
offset = 3;
|
||||
break;
|
||||
case 0xfe:
|
||||
size = *ptr + ((*(ptr + 2) << 8)) + (*(ptr + 3) << 16) +
|
||||
(*(ptr + 4) << 24) + (*(ptr + 5) << 32) + (*(ptr + 6) << 40) +
|
||||
(*(ptr + 7) << 48) + (*(ptr + 8) << 56);
|
||||
size = *ptr +
|
||||
((*(ptr + 2) << 8)) +
|
||||
(*(ptr + 3) << 16) +
|
||||
(*(ptr + 4) << 24) +
|
||||
((unsigned long)*(ptr + 5) << 32) +
|
||||
((unsigned long)*(ptr + 6) << 40) +
|
||||
((unsigned long)*(ptr + 7) << 48) +
|
||||
((unsigned long)*(ptr + 8) << 56);
|
||||
offset = 8;
|
||||
break;
|
||||
default:
|
||||
@ -311,32 +311,52 @@ char* get_lenenc_str(void* data, int* len)
|
||||
* @param buf GWBUF containing the result set
|
||||
* @return True if the buffer contained a result set with a single column. All other responses return false.
|
||||
*/
|
||||
bool parse_showdb_response(ROUTER_CLIENT_SES* rses, char* target, GWBUF* buf)
|
||||
bool parse_showdb_response(ROUTER_CLIENT_SES* rses, backend_ref_t* bref, GWBUF** buffer)
|
||||
{
|
||||
bool rval = false;
|
||||
unsigned char* ptr;
|
||||
int more = 0;
|
||||
if(PTR_IS_RESULTSET(((unsigned char*)buf->start)) &&
|
||||
modutil_count_signal_packets(buf,0,0,&more) == 2)
|
||||
char* target = bref->bref_backend->backend_server->unique_name;
|
||||
GWBUF* buf;
|
||||
|
||||
if(buffer == NULL || *buffer == NULL)
|
||||
return false;
|
||||
|
||||
buf = modutil_get_complete_packets(buffer);
|
||||
|
||||
if(buf == NULL)
|
||||
return false;
|
||||
|
||||
ptr = (unsigned char*)buf->start;
|
||||
|
||||
if(PTR_IS_ERR(ptr))
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"schemarouter: SHOW DATABASES returned an error.");
|
||||
gwbuf_free(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(bref->n_mapping_eof == 0)
|
||||
{
|
||||
ptr = (unsigned char*)buf->start;
|
||||
|
||||
if(ptr[4] != 1)
|
||||
{
|
||||
/** Something else came back, discard and return with an error*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Skip column definitions */
|
||||
while(!PTR_IS_EOF(ptr))
|
||||
while(ptr < (unsigned char*)buf->end && !PTR_IS_EOF(ptr))
|
||||
{
|
||||
ptr += gw_mysql_get_byte3(ptr) + 4;
|
||||
}
|
||||
|
||||
if(ptr >= (unsigned char*)buf->end)
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"schemarouter: Malformed packet for SHOW DATABASES.");
|
||||
*buffer = gwbuf_append(buf,*buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
atomic_add(&bref->n_mapping_eof,1);
|
||||
/** Skip first EOF packet */
|
||||
ptr += gw_mysql_get_byte3(ptr) + 4;
|
||||
|
||||
while(!PTR_IS_EOF(ptr))
|
||||
}
|
||||
|
||||
if(bref->n_mapping_eof == 1)
|
||||
{
|
||||
while(ptr < (unsigned char*)buf->end && !PTR_IS_EOF(ptr))
|
||||
{
|
||||
int payloadlen = gw_mysql_get_byte3(ptr);
|
||||
int packetlen = payloadlen + 4;
|
||||
@ -353,11 +373,23 @@ bool parse_showdb_response(ROUTER_CLIENT_SES* rses, char* target, GWBUF* buf)
|
||||
}
|
||||
ptr += packetlen;
|
||||
}
|
||||
rval = true;
|
||||
|
||||
}
|
||||
|
||||
return rval;
|
||||
if(ptr < (unsigned char*)buf->end && PTR_IS_EOF(ptr) &&
|
||||
bref->n_mapping_eof == 1)
|
||||
{
|
||||
atomic_add(&bref->n_mapping_eof,1);
|
||||
skygw_log_write(LOGFILE_TRACE,"schemarouter: SHOW DATABASES fully received from %s.",
|
||||
bref->bref_backend->backend_server->unique_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"schemarouter: SHOW DATABASES partially received from %s.",
|
||||
bref->bref_backend->backend_server->unique_name);
|
||||
}
|
||||
|
||||
gwbuf_free(buf);
|
||||
|
||||
return bref->n_mapping_eof == 2;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -372,14 +404,14 @@ bool parse_showdb_response(ROUTER_CLIENT_SES* rses, char* target, GWBUF* buf)
|
||||
int gen_databaselist(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* session)
|
||||
{
|
||||
DCB* dcb;
|
||||
const char* query = "SHOW DATABASES;";
|
||||
const char* query = "SHOW DATABASES";
|
||||
GWBUF *buffer,*clone;
|
||||
int i,rval = 0;
|
||||
unsigned int len;
|
||||
|
||||
session->init |= INIT_MAPPING;
|
||||
session->init &= ~INIT_UNINT;
|
||||
len = strlen(query);
|
||||
len = strlen(query) + 1;
|
||||
buffer = gwbuf_alloc(len + 4);
|
||||
*((unsigned char*)buffer->start) = len;
|
||||
*((unsigned char*)buffer->start + 1) = len>>8;
|
||||
@ -391,7 +423,8 @@ int gen_databaselist(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* session)
|
||||
for(i = 0;i<session->rses_nbackends;i++)
|
||||
{
|
||||
if(BREF_IS_IN_USE(&session->rses_backend_ref[i]) &&
|
||||
!BREF_IS_CLOSED(&session->rses_backend_ref[i]))
|
||||
!BREF_IS_CLOSED(&session->rses_backend_ref[i]) &&
|
||||
SERVER_IS_RUNNING(session->rses_backend_ref[i].bref_backend->backend_server))
|
||||
{
|
||||
clone = gwbuf_clone(buffer);
|
||||
dcb = session->rses_backend_ref[i].bref_dcb;
|
||||
@ -687,6 +720,13 @@ createInstance(SERVICE *service, char **options)
|
||||
return NULL;
|
||||
}
|
||||
router->service = service;
|
||||
router->schemarouter_config.max_sescmd_hist = 0;
|
||||
router->stats.longest_sescmd = 0;
|
||||
router->stats.n_hist_exceeded = 0;
|
||||
router->stats.n_queries = 0;
|
||||
router->stats.n_sescmd = 0;
|
||||
router->stats.ses_longest = 0;
|
||||
router->stats.ses_shortest = (double)((unsigned long)(~0));
|
||||
spinlock_init(&router->lock);
|
||||
|
||||
/** Calculate number of servers */
|
||||
@ -701,6 +741,45 @@ createInstance(SERVICE *service, char **options)
|
||||
service->users_from_all = true;
|
||||
}
|
||||
|
||||
bool failure = false;
|
||||
|
||||
for(i=0;options && options[i];i++)
|
||||
{
|
||||
char* value;
|
||||
if((value = strchr(options[i],'=')) == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: Unknown router options for Schemarouter: %s",options[i]);
|
||||
failure = true;
|
||||
break;
|
||||
}
|
||||
*value = '\0';
|
||||
value++;
|
||||
if(strcmp(options[i],"max_sescmd_history") == 0)
|
||||
{
|
||||
router->schemarouter_config.max_sescmd_hist = atoi(value);
|
||||
}
|
||||
else if(strcmp(options[i],"disable_sescmd_history") == 0)
|
||||
{
|
||||
router->schemarouter_config.disable_sescmd_hist = config_truth_value(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: Unknown router options for Schemarouter: %s",options[i]);
|
||||
failure = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Setting a limit to the history size is not needed if it is disabled.*/
|
||||
if(router->schemarouter_config.disable_sescmd_hist && router->schemarouter_config.max_sescmd_hist > 0)
|
||||
router->schemarouter_config.max_sescmd_hist = 0;
|
||||
|
||||
if(failure)
|
||||
{
|
||||
free(router);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (server != NULL)
|
||||
{
|
||||
nservers++;
|
||||
@ -730,6 +809,7 @@ createInstance(SERVICE *service, char **options)
|
||||
router->servers[nservers]->backend_conn_count = 0;
|
||||
router->servers[nservers]->weight = 1;
|
||||
router->servers[nservers]->be_valid = false;
|
||||
router->servers[nservers]->stats.queries = 0;
|
||||
if(server->server->monuser == NULL && service->credentials.name != NULL)
|
||||
{
|
||||
router->servers[nservers]->backend_server->monuser =
|
||||
@ -824,7 +904,7 @@ static void* newSession(
|
||||
{
|
||||
|
||||
protocol->client_capabilities &= ~GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB;
|
||||
strncpy(db,data->db,MYSQL_DATABASE_MAXLEN+1);
|
||||
strncpy(db,data->db,MYSQL_DATABASE_MAXLEN);
|
||||
memset(data->db,0,MYSQL_DATABASE_MAXLEN+1);
|
||||
using_db = true;
|
||||
skygw_log_write(LOGFILE_TRACE,"schemarouter: Client logging in directly to a database '%s', "
|
||||
@ -858,7 +938,8 @@ static void* newSession(
|
||||
client_rses->dcb_reply->func.read = internalReply;
|
||||
client_rses->dcb_reply->state = DCB_STATE_POLLING;
|
||||
client_rses->dcb_reply->session = session;
|
||||
|
||||
memcpy(&client_rses->rses_config,&router->schemarouter_config,sizeof(schemarouter_config_t));
|
||||
client_rses->n_sescmd = 0;
|
||||
client_rses->dcb_route = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
|
||||
client_rses->dcb_route->func.read = internalRoute;
|
||||
client_rses->dcb_route->state = DCB_STATE_POLLING;
|
||||
@ -905,6 +986,8 @@ static void* newSession(
|
||||
backend_ref[i].bref_sescmd_cur.scmd_cur_chk_tail = CHK_NUM_SESCMD_CUR;
|
||||
#endif
|
||||
backend_ref[i].bref_state = 0;
|
||||
backend_ref[i].n_mapping_eof = 0;
|
||||
backend_ref[i].map_queue = NULL;
|
||||
backend_ref[i].bref_backend = router->servers[i];
|
||||
/** store pointers to sescmd list to both cursors */
|
||||
backend_ref[i].bref_sescmd_cur.scmd_cur_rses = client_rses;
|
||||
@ -957,7 +1040,6 @@ static void* newSession(
|
||||
client_rses->rses_capabilities = RCAP_TYPE_STMT_INPUT;
|
||||
client_rses->rses_backend_ref = backend_ref;
|
||||
client_rses->rses_nbackends = router_nservers; /*< # of backend servers */
|
||||
router->stats.n_sessions += 1;
|
||||
|
||||
if (!(succp = rses_begin_locked_router_action(client_rses)))
|
||||
{
|
||||
@ -977,7 +1059,8 @@ static void* newSession(
|
||||
|
||||
|
||||
rses_end_locked_router_action(client_rses);
|
||||
|
||||
|
||||
atomic_add(&router->stats.sessions, 1);
|
||||
|
||||
/**
|
||||
* Version is bigger than zero once initialized.
|
||||
@ -1016,6 +1099,7 @@ static void closeSession(
|
||||
void* router_session)
|
||||
{
|
||||
ROUTER_CLIENT_SES* router_cli_ses;
|
||||
ROUTER_INSTANCE* inst;
|
||||
backend_ref_t* backend_ref;
|
||||
|
||||
LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG,
|
||||
@ -1032,7 +1116,8 @@ static void closeSession(
|
||||
}
|
||||
router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
|
||||
CHK_CLIENT_RSES(router_cli_ses);
|
||||
|
||||
|
||||
inst = router_cli_ses->router;
|
||||
backend_ref = router_cli_ses->rses_backend_ref;
|
||||
/**
|
||||
* Lock router client session for secure read and update.
|
||||
@ -1087,9 +1172,28 @@ static void closeSession(
|
||||
router_cli_ses->dcb_route->session = NULL;
|
||||
dcb_close(router_cli_ses->dcb_reply);
|
||||
dcb_close(router_cli_ses->dcb_route);
|
||||
|
||||
|
||||
if(router_cli_ses->queue)
|
||||
router_cli_ses->queue = gwbuf_consume(
|
||||
router_cli_ses->queue,gwbuf_length(router_cli_ses->queue));
|
||||
|
||||
/** Unlock */
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
if(inst->stats.longest_sescmd < router_cli_ses->stats.longest_sescmd)
|
||||
inst->stats.longest_sescmd = router_cli_ses->stats.longest_sescmd;
|
||||
double ses_time = difftime(time(NULL),router_cli_ses->rses_client_dcb->session->stats.connect);
|
||||
if(inst->stats.ses_longest < ses_time)
|
||||
inst->stats.ses_longest = ses_time;
|
||||
if(inst->stats.ses_shortest > ses_time && inst->stats.ses_shortest > 0)
|
||||
inst->stats.ses_shortest = ses_time;
|
||||
|
||||
inst->stats.ses_average =
|
||||
(ses_time + ((inst->stats.sessions - 1) * inst->stats.ses_average)) /
|
||||
(inst->stats.sessions);
|
||||
|
||||
spinlock_release(&inst->lock);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1511,6 +1615,12 @@ void check_create_tmp_table(
|
||||
free(tblname);
|
||||
}
|
||||
}
|
||||
|
||||
int cmpfn(const void* a, const void *b)
|
||||
{
|
||||
return strcmp(*(char**)a,*(char**)b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a custom SHOW DATABASES result set from all the databases in the
|
||||
* hashtable. Only backend servers that are up and in a proper state are listed
|
||||
@ -1621,37 +1731,71 @@ gen_show_dbs_response(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client)
|
||||
memcpy(ptr, eof, sizeof(eof));
|
||||
|
||||
unsigned int packet_num = 4;
|
||||
int j = 0,ndbs = 0, bufsz = 10;
|
||||
char** dbs;
|
||||
|
||||
if((dbs = malloc(sizeof(char*)*bufsz)) == NULL)
|
||||
{
|
||||
gwbuf_free(rval);
|
||||
hashtable_iterator_free(iter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while((value = (char*) hashtable_next(iter)))
|
||||
{
|
||||
char* bend = hashtable_fetch(ht, value);
|
||||
|
||||
for(i = 0; backends[i]; i++)
|
||||
{
|
||||
if(strcmp(bref[i].bref_backend->backend_server->unique_name, bend) == 0 &&
|
||||
BREF_IS_IN_USE(&bref[i]) && !BREF_IS_CLOSED(&bref[i]))
|
||||
{
|
||||
ndbs++;
|
||||
|
||||
GWBUF* temp;
|
||||
int plen = strlen(value) + 1;
|
||||
if(ndbs >= bufsz)
|
||||
{
|
||||
bufsz += bufsz / 2;
|
||||
char** tmp = realloc(dbs,sizeof(char*)*bufsz);
|
||||
if(tmp == NULL)
|
||||
{
|
||||
gwbuf_free(rval);
|
||||
hashtable_iterator_free(iter);
|
||||
for(i=0;i<ndbs-1;i++)free(dbs[i]);
|
||||
free(dbs);
|
||||
return NULL;
|
||||
}
|
||||
dbs = tmp;
|
||||
}
|
||||
|
||||
sprintf(dbname, "%s", value);
|
||||
temp = gwbuf_alloc(plen + 4);
|
||||
|
||||
ptr = temp->start;
|
||||
*ptr++ = plen;
|
||||
*ptr++ = plen >> 8;
|
||||
*ptr++ = plen >> 16;
|
||||
*ptr++ = packet_num++;
|
||||
*ptr++ = plen - 1;
|
||||
memcpy(ptr, dbname, plen - 1);
|
||||
|
||||
/** Append the row*/
|
||||
rval = gwbuf_append(rval, temp);
|
||||
|
||||
dbs[j++] = strdup(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qsort(&dbs[0],(size_t)ndbs,sizeof(char*),cmpfn);
|
||||
|
||||
for(j = 0;j<ndbs;j++)
|
||||
{
|
||||
|
||||
GWBUF* temp;
|
||||
int plen = strlen(dbs[j]) + 1;
|
||||
|
||||
sprintf(dbname, "%s", dbs[j]);
|
||||
temp = gwbuf_alloc(plen + 4);
|
||||
|
||||
ptr = temp->start;
|
||||
*ptr++ = plen;
|
||||
*ptr++ = plen >> 8;
|
||||
*ptr++ = plen >> 16;
|
||||
*ptr++ = packet_num++;
|
||||
*ptr++ = plen - 1;
|
||||
memcpy(ptr, dbname, plen - 1);
|
||||
|
||||
/** Append the row*/
|
||||
rval = gwbuf_append(rval, temp);
|
||||
free(dbs[j]);
|
||||
}
|
||||
|
||||
eof[3] = packet_num;
|
||||
|
||||
GWBUF* last_packet = gwbuf_alloc(sizeof(eof));
|
||||
@ -1660,6 +1804,7 @@ gen_show_dbs_response(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client)
|
||||
|
||||
rval = gwbuf_make_contiguous(rval);
|
||||
hashtable_iterator_free(iter);
|
||||
free(dbs);
|
||||
return rval;
|
||||
}
|
||||
|
||||
@ -1687,12 +1832,12 @@ gen_show_dbs_response(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client)
|
||||
static int routeQuery(
|
||||
ROUTER* instance,
|
||||
void* router_session,
|
||||
GWBUF* querybuf)
|
||||
GWBUF* qbuf)
|
||||
{
|
||||
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
|
||||
mysql_server_cmd_t packet_type;
|
||||
uint8_t* packet;
|
||||
int ret = 0;
|
||||
int i,ret = 0;
|
||||
DCB* target_dcb = NULL;
|
||||
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;
|
||||
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
|
||||
@ -1701,8 +1846,9 @@ static int routeQuery(
|
||||
route_target_t route_target = TARGET_UNDEFINED;
|
||||
bool succp = false;
|
||||
char* tname = NULL;
|
||||
|
||||
|
||||
GWBUF* querybuf = qbuf;
|
||||
char db[MYSQL_DATABASE_MAXLEN + 1];
|
||||
char errbuf[26+MYSQL_DATABASE_MAXLEN];
|
||||
CHK_CLIENT_RSES(router_cli_ses);
|
||||
|
||||
/** Dirty read for quick check if router is closed. */
|
||||
@ -1734,11 +1880,11 @@ static int routeQuery(
|
||||
{
|
||||
|
||||
char* querystr = modutil_get_SQL(querybuf);
|
||||
skygw_log_write(LOGFILE_DEBUG,"schemarouter: Storing query for session %p: %s",
|
||||
skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"schemarouter: Storing query for session %p: %s",
|
||||
router_cli_ses->rses_client_dcb->session,
|
||||
querystr);
|
||||
free(querystr);
|
||||
gwbuf_make_contiguous(querybuf);
|
||||
querybuf = gwbuf_make_contiguous(querybuf);
|
||||
GWBUF* ptr = router_cli_ses->queue;
|
||||
|
||||
while(ptr && ptr->next)
|
||||
@ -1875,12 +2021,25 @@ static int routeQuery(
|
||||
if (packet_type == MYSQL_COM_INIT_DB ||
|
||||
op == QUERY_OP_CHANGE_DB)
|
||||
{
|
||||
if (!(change_successful = change_current_db(inst, router_cli_ses, querybuf)))
|
||||
if (!(change_successful = change_current_db(router_cli_ses->rses_mysql_session,
|
||||
router_cli_ses->dbhash,
|
||||
querybuf)))
|
||||
{
|
||||
extract_database(querybuf,db);
|
||||
snprintf(errbuf,25+MYSQL_DATABASE_MAXLEN,"Unknown database: %s",db);
|
||||
for(i = 0;i<router_cli_ses->rses_nbackends;i++)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Changing database failed.")));
|
||||
if(SERVER_IS_RUNNING(router_cli_ses->rses_backend_ref[i].bref_backend->backend_server))
|
||||
{
|
||||
create_error_reply(errbuf,router_cli_ses->rses_backend_ref[i].bref_dcb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Changing database failed.")));
|
||||
}
|
||||
}
|
||||
|
||||
if(QUERY_IS_TYPE(qtype, QUERY_TYPE_SHOW_DATABASES))
|
||||
@ -1997,7 +2156,8 @@ static int routeQuery(
|
||||
|
||||
if (succp)
|
||||
{
|
||||
atomic_add(&inst->stats.n_all, 1);
|
||||
atomic_add(&inst->stats.n_sescmd, 1);
|
||||
atomic_add(&inst->stats.n_queries, 1);
|
||||
ret = 1;
|
||||
}
|
||||
goto retblock;
|
||||
@ -2198,6 +2358,9 @@ diagnostic(ROUTER *instance, DCB *dcb)
|
||||
ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance;
|
||||
int i = 0;
|
||||
|
||||
double sescmd_pct = router->stats.n_sescmd != 0 ?
|
||||
100.0*((double)router->stats.n_sescmd / (double)router->stats.n_queries) :
|
||||
0.0;
|
||||
|
||||
dcb_printf(dcb,"\33[1;4m%-16s%-16s%-16s\33[0m\n","Server","Queries","State");
|
||||
for(i=0;router->servers[i];i++)
|
||||
@ -2210,6 +2373,45 @@ diagnostic(ROUTER *instance, DCB *dcb)
|
||||
"\33[30;41mDOWN\33[0m");
|
||||
}
|
||||
|
||||
/** Session command statistics */
|
||||
dcb_printf(dcb,"\n\33[1;4mSession Commands\33[0m\n");
|
||||
dcb_printf(dcb,"Total number of queries: %d\n",
|
||||
router->stats.n_queries);
|
||||
dcb_printf(dcb,"Percentage of session commands: %.2f\n",
|
||||
sescmd_pct);
|
||||
dcb_printf(dcb,"Longest chain of stored session commands: %d\n",
|
||||
router->stats.longest_sescmd);
|
||||
dcb_printf(dcb,"Session command history limit exceeded: %d times\n",
|
||||
router->stats.n_hist_exceeded);
|
||||
if(!router->schemarouter_config.disable_sescmd_hist)
|
||||
{
|
||||
dcb_printf(dcb,"Session command history: enabled\n");
|
||||
if(router->schemarouter_config.max_sescmd_hist == 0)
|
||||
{
|
||||
dcb_printf(dcb,"Session command history limit: unlimited\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb,"Session command history limit: %d\n",
|
||||
router->schemarouter_config.max_sescmd_hist);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
dcb_printf(dcb,"Session command history: disabled\n");
|
||||
}
|
||||
|
||||
/** Session time statistics */
|
||||
|
||||
if(router->stats.sessions > 0)
|
||||
{
|
||||
dcb_printf(dcb,"\n\33[1;4mSession Time Statistics\33[0m\n");
|
||||
dcb_printf(dcb,"Longest session: %.2lf seconds\n",router->stats.ses_longest);
|
||||
dcb_printf(dcb,"Shortest session: %.2lf seconds\n",router->stats.ses_shortest);
|
||||
dcb_printf(dcb,"Average session length: %.2lf seconds\n",router->stats.ses_average);
|
||||
}
|
||||
dcb_printf(dcb,"\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2225,14 +2427,15 @@ diagnostic(ROUTER *instance, DCB *dcb)
|
||||
static void clientReply (
|
||||
ROUTER* instance,
|
||||
void* router_session,
|
||||
GWBUF* writebuf,
|
||||
GWBUF* buffer,
|
||||
DCB* backend_dcb)
|
||||
{
|
||||
DCB* client_dcb;
|
||||
ROUTER_CLIENT_SES* router_cli_ses;
|
||||
sescmd_cursor_t* scur = NULL;
|
||||
backend_ref_t* bref;
|
||||
|
||||
GWBUF* writebuf = buffer;
|
||||
|
||||
router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
|
||||
CHK_CLIENT_RSES(router_cli_ses);
|
||||
|
||||
@ -2303,16 +2506,31 @@ static void clientReply (
|
||||
|
||||
for(i = 0; i < router_cli_ses->rses_nbackends; i++)
|
||||
{
|
||||
|
||||
if(bref->bref_dcb == bkrf[i].bref_dcb && !BREF_IS_MAPPED(&bkrf[i]))
|
||||
{
|
||||
router_cli_ses->rses_backend_ref[i].bref_mapped = true;
|
||||
parse_showdb_response(router_cli_ses,
|
||||
router_cli_ses->rses_backend_ref[i].bref_backend->backend_server->unique_name,
|
||||
writebuf);
|
||||
skygw_log_write(LOGFILE_DEBUG,"schemarouter: Received SHOW DATABASES reply from %s for session %p",
|
||||
if(bref->map_queue)
|
||||
{
|
||||
writebuf = gwbuf_append(bref->map_queue,writebuf);
|
||||
bref->map_queue = NULL;
|
||||
}
|
||||
|
||||
if(parse_showdb_response(router_cli_ses,
|
||||
&router_cli_ses->rses_backend_ref[i],
|
||||
&writebuf))
|
||||
{
|
||||
router_cli_ses->rses_backend_ref[i].bref_mapped = true;
|
||||
skygw_log_write(LOGFILE_DEBUG,"schemarouter: Received SHOW DATABASES reply from %s for session %p",
|
||||
router_cli_ses->rses_backend_ref[i].bref_backend->backend_server->unique_name,
|
||||
router_cli_ses->rses_client_dcb->session);
|
||||
}
|
||||
else
|
||||
{
|
||||
bref->map_queue = writebuf;
|
||||
writebuf = NULL;
|
||||
skygw_log_write(LOGFILE_DEBUG,"schemarouter: Received partial SHOW DATABASES reply from %s for session %p",
|
||||
router_cli_ses->rses_backend_ref[i].bref_backend->backend_server->unique_name,
|
||||
router_cli_ses->rses_client_dcb->session);
|
||||
}
|
||||
}
|
||||
|
||||
if(BREF_IS_IN_USE(&bkrf[i]) &&
|
||||
@ -2329,7 +2547,7 @@ static void clientReply (
|
||||
}
|
||||
}
|
||||
|
||||
while((writebuf = gwbuf_consume(writebuf,gwbuf_length(writebuf))));
|
||||
while(writebuf && (writebuf = gwbuf_consume(writebuf,gwbuf_length(writebuf))));
|
||||
|
||||
if(mapped)
|
||||
{
|
||||
@ -2551,8 +2769,7 @@ static void clientReply (
|
||||
/** There is one pending session command to be executed. */
|
||||
if (sescmd_cursor_is_active(scur))
|
||||
{
|
||||
bool succp;
|
||||
|
||||
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Backend %s:%d processed reply and starts to execute "
|
||||
@ -2560,9 +2777,7 @@ static void clientReply (
|
||||
bref->bref_backend->backend_server->name,
|
||||
bref->bref_backend->backend_server->port)));
|
||||
|
||||
succp = execute_sescmd_in_backend(bref);
|
||||
|
||||
ss_dassert(succp);
|
||||
execute_sescmd_in_backend(bref);
|
||||
}
|
||||
else if (bref->bref_pending_cmd != NULL) /*< non-sescmd is waiting to be routed */
|
||||
{
|
||||
@ -3038,7 +3253,7 @@ static mysql_sescmd_t* mysql_sescmd_init (
|
||||
/** Set session command buffer */
|
||||
sescmd->my_sescmd_buf = sescmd_buf;
|
||||
sescmd->my_sescmd_packet_type = packet_type;
|
||||
|
||||
sescmd->position = atomic_add(&rses->pos_generator,1);
|
||||
return sescmd;
|
||||
}
|
||||
|
||||
@ -3090,6 +3305,7 @@ static GWBUF* sescmd_cursor_process_replies(
|
||||
*/
|
||||
while (scmd != NULL && replybuf != NULL)
|
||||
{
|
||||
scur->position = scmd->position;
|
||||
/** Faster backend has already responded to client : discard */
|
||||
if (scmd->my_sescmd_is_replied)
|
||||
{
|
||||
@ -3641,17 +3857,74 @@ static bool route_session_write(
|
||||
succp = false;
|
||||
goto return_succp;
|
||||
}
|
||||
/**
|
||||
|
||||
if(router_cli_ses->rses_config.max_sescmd_hist > 0 &&
|
||||
router_cli_ses->n_sescmd >= router_cli_ses->rses_config.max_sescmd_hist)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR|LOGFILE_TRACE,
|
||||
"Router session exceeded session command history limit of %d. "
|
||||
"Closing router session.",
|
||||
router_cli_ses->rses_config.max_sescmd_hist)));
|
||||
gwbuf_free(querybuf);
|
||||
atomic_add(&router_cli_ses->router->stats.n_hist_exceeded,1);
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
router_cli_ses->rses_client_dcb->func.hangup(router_cli_ses->rses_client_dcb);
|
||||
|
||||
goto return_succp;
|
||||
}
|
||||
|
||||
if(router_cli_ses->rses_config.disable_sescmd_hist)
|
||||
{
|
||||
rses_property_t *prop, *tmp;
|
||||
backend_ref_t* bref;
|
||||
bool conflict;
|
||||
|
||||
prop = router_cli_ses->rses_properties[RSES_PROP_TYPE_SESCMD];
|
||||
while(prop)
|
||||
{
|
||||
conflict = false;
|
||||
|
||||
for(i = 0;i<router_cli_ses->rses_nbackends;i++)
|
||||
{
|
||||
bref = &backend_ref[i];
|
||||
if(BREF_IS_IN_USE(bref))
|
||||
{
|
||||
|
||||
if(bref->bref_sescmd_cur.position <= prop->rses_prop_data.sescmd.position)
|
||||
{
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(conflict)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
tmp = prop;
|
||||
router_cli_ses->rses_properties[RSES_PROP_TYPE_SESCMD] = prop->rses_prop_next;
|
||||
rses_property_done(tmp);
|
||||
prop = router_cli_ses->rses_properties[RSES_PROP_TYPE_SESCMD];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Additional reference is created to querybuf to
|
||||
* prevent it from being released before properties
|
||||
* are cleaned up as a part of router sessionclean-up.
|
||||
* are cleaned up as a part of router session clean-up.
|
||||
*/
|
||||
prop = rses_property_init(RSES_PROP_TYPE_SESCMD);
|
||||
mysql_sescmd_init(prop, querybuf, packet_type, router_cli_ses);
|
||||
|
||||
/** Add sescmd property to router client session */
|
||||
rses_property_add(router_cli_ses, prop);
|
||||
|
||||
atomic_add(&router_cli_ses->stats.longest_sescmd,1);
|
||||
atomic_add(&router_cli_ses->n_sescmd,1);
|
||||
|
||||
for (i=0; i<router_cli_ses->rses_nbackends; i++)
|
||||
{
|
||||
if (BREF_IS_IN_USE((&backend_ref[i])))
|
||||
@ -3706,6 +3979,10 @@ static bool route_session_write(
|
||||
backend_ref[i].bref_backend->backend_server->name,
|
||||
backend_ref[i].bref_backend->backend_server->port)));
|
||||
}
|
||||
else
|
||||
{
|
||||
atomic_add(&backend_ref[i].bref_backend->stats.queries,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -3885,7 +4162,7 @@ static bool handle_error_new_connection(
|
||||
{
|
||||
SESSION* ses;
|
||||
int router_nservers,i;
|
||||
|
||||
unsigned char cmd = *((unsigned char*)errmsg->start + 4);
|
||||
|
||||
backend_ref_t* bref;
|
||||
bool succp;
|
||||
@ -3960,10 +4237,11 @@ static bool handle_error_new_connection(
|
||||
}
|
||||
|
||||
rses->init |= INIT_MAPPING;
|
||||
|
||||
|
||||
for(i = 0;i<rses->rses_nbackends;i++)
|
||||
{
|
||||
bref_clear_state(&rses->rses_backend_ref[i],BREF_DB_MAPPED);
|
||||
bref_clear_state(&rses->rses_backend_ref[i],BREF_DB_MAPPED);
|
||||
rses->rses_backend_ref[i].n_mapping_eof = 0;
|
||||
}
|
||||
|
||||
HASHITERATOR* iter = hashtable_iterator(rses->dbhash);
|
||||
@ -4053,8 +4331,6 @@ router_handle_state_switch(
|
||||
{
|
||||
backend_ref_t* bref;
|
||||
int rc = 1;
|
||||
ROUTER_CLIENT_SES* rses;
|
||||
SESSION* ses;
|
||||
SERVER* srv;
|
||||
|
||||
CHK_DCB(dcb);
|
||||
@ -4067,12 +4343,7 @@ router_handle_state_switch(
|
||||
{
|
||||
goto return_rc;
|
||||
}
|
||||
ses = dcb->session;
|
||||
CHK_SESSION(ses);
|
||||
|
||||
rses = (ROUTER_CLIENT_SES *) dcb->session->router_session;
|
||||
CHK_CLIENT_RSES(rses);
|
||||
|
||||
switch(reason)
|
||||
{
|
||||
case DCB_REASON_NOT_RESPONDING:
|
||||
@ -4102,172 +4373,4 @@ static sescmd_cursor_t* backend_ref_get_sescmd_cursor (
|
||||
return scur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read new database name from MYSQL_COM_INIT_DB packet, check that it exists
|
||||
* in the hashtable and copy its name to MYSQL_session.
|
||||
*
|
||||
* @param inst Router instance
|
||||
* @param rses Router client session
|
||||
* @param buf Query buffer
|
||||
*
|
||||
* @return true if new database is set, false if non-existent database was tried
|
||||
* to be set
|
||||
*/
|
||||
static bool change_current_db(
|
||||
ROUTER_INSTANCE* inst,
|
||||
ROUTER_CLIENT_SES* rses,
|
||||
GWBUF* buf)
|
||||
{
|
||||
bool succp;
|
||||
uint8_t* packet;
|
||||
unsigned int plen;
|
||||
int message_len;
|
||||
char* fail_str,*target;
|
||||
|
||||
if(GWBUF_LENGTH(buf) <= MYSQL_DATABASE_MAXLEN - 5)
|
||||
{
|
||||
packet = GWBUF_DATA(buf);
|
||||
plen = gw_mysql_get_byte3(packet) - 1;
|
||||
|
||||
/** Copy database name from MySQL packet to session */
|
||||
if(query_classifier_get_operation(buf) == QUERY_OP_CHANGE_DB)
|
||||
{
|
||||
char* query = modutil_get_SQL(buf);
|
||||
char *saved,*tok;
|
||||
|
||||
tok = strtok_r(query," ;",&saved);
|
||||
if(tok == NULL || strcasecmp(tok,"use") != 0)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet.");
|
||||
free(query);
|
||||
message_len = 25 + MYSQL_DATABASE_MAXLEN;
|
||||
fail_str = calloc(1, message_len+1);
|
||||
snprintf(fail_str,
|
||||
message_len,
|
||||
"Unknown database '%s'",
|
||||
(char*)rses->rses_mysql_session->db);
|
||||
rses->rses_mysql_session->db[0] = '\0';
|
||||
succp = false;
|
||||
goto reply_error;
|
||||
}
|
||||
|
||||
tok = strtok_r(NULL," ;",&saved);
|
||||
if(tok == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet.");
|
||||
free(query);
|
||||
message_len = 25 + MYSQL_DATABASE_MAXLEN;
|
||||
fail_str = calloc(1, message_len+1);
|
||||
snprintf(fail_str,
|
||||
message_len,
|
||||
"Unknown database '%s'",
|
||||
(char*)rses->rses_mysql_session->db);
|
||||
rses->rses_mysql_session->db[0] = '\0';
|
||||
succp = false;
|
||||
goto reply_error;
|
||||
}
|
||||
|
||||
strncpy(rses->rses_mysql_session->db,tok,MYSQL_DATABASE_MAXLEN);
|
||||
free(query);
|
||||
query = NULL;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(rses->rses_mysql_session->db,packet + 5,plen);
|
||||
memset(rses->rses_mysql_session->db + plen,0,1);
|
||||
}
|
||||
skygw_log_write(LOGFILE_TRACE,"schemarouter: INIT_DB with database '%s'",
|
||||
rses->rses_mysql_session->db);
|
||||
/**
|
||||
* Update the session's active database only if it's in the hashtable.
|
||||
* If it isn't found, send a custom error packet to the client.
|
||||
*/
|
||||
|
||||
|
||||
if((target = (char*)hashtable_fetch(
|
||||
rses->dbhash,
|
||||
(char*)rses->rses_mysql_session->db)) == NULL)
|
||||
{
|
||||
|
||||
/** Create error message */
|
||||
message_len = 25 + MYSQL_DATABASE_MAXLEN;
|
||||
fail_str = calloc(1, message_len+1);
|
||||
snprintf(fail_str,
|
||||
message_len,
|
||||
"Unknown database '%s'",
|
||||
(char*)rses->rses_mysql_session->db);
|
||||
rses->rses_mysql_session->db[0] = '\0';
|
||||
succp = false;
|
||||
goto reply_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"schemarouter: database is on server: '%s'.",target);
|
||||
succp = true;
|
||||
goto retblock;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Create error message */
|
||||
skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"schemarouter: failed to change database: Query buffer too large");
|
||||
skygw_log_write_flush(LOGFILE_TRACE,
|
||||
"schemarouter: failed to change database: Query buffer too large [%d bytes]",GWBUF_LENGTH(buf));
|
||||
message_len = 25 + MYSQL_DATABASE_MAXLEN;
|
||||
fail_str = calloc(1, message_len+1);
|
||||
snprintf(fail_str,
|
||||
message_len,
|
||||
"Unknown database '%s'",
|
||||
(char*)rses->rses_mysql_session->db);
|
||||
succp = false;
|
||||
goto reply_error;
|
||||
}
|
||||
reply_error:
|
||||
{
|
||||
GWBUF* errbuf;
|
||||
skygw_log_write_flush(
|
||||
LOGFILE_TRACE,
|
||||
"schemarouter: failed to change database: %s", fail_str);
|
||||
errbuf = modutil_create_mysql_err_msg(1, 0, 1049, "42000", fail_str);
|
||||
free(fail_str);
|
||||
|
||||
|
||||
if (errbuf == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Creating buffer for error message failed.")));
|
||||
goto retblock;
|
||||
}
|
||||
/** Set flags that help router to identify session commans reply */
|
||||
gwbuf_set_type(errbuf, GWBUF_TYPE_MYSQL);
|
||||
gwbuf_set_type(errbuf, GWBUF_TYPE_SESCMD_RESPONSE);
|
||||
gwbuf_set_type(errbuf, GWBUF_TYPE_RESPONSE_END);
|
||||
/**
|
||||
* Create an incoming event for randomly selected backend DCB which
|
||||
* will then be notified and replied 'back' to the client.
|
||||
*/
|
||||
DCB *dcb = NULL;
|
||||
int i;
|
||||
for(i = 0;i<rses->rses_nbackends;i++)
|
||||
{
|
||||
if(rses->rses_backend_ref[i].bref_dcb){
|
||||
dcb = rses->rses_backend_ref[i].bref_dcb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(dcb == NULL)
|
||||
{
|
||||
skygw_log_write_flush(LOGFILE_ERROR,"Error : All backend connections are down.");
|
||||
return false;
|
||||
}
|
||||
poll_add_epollin_event_to_dcb(rses->dcb_reply,
|
||||
gwbuf_clone(errbuf));
|
||||
gwbuf_free(errbuf);
|
||||
}
|
||||
retblock:
|
||||
return succp;
|
||||
}
|
||||
|
161
server/modules/routing/schemarouter/sharding_common.c
Normal file
161
server/modules/routing/schemarouter/sharding_common.c
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* This file is distributed as part of the MariaDB Corporation MaxScale. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2013-2015
|
||||
*/
|
||||
|
||||
#include <sharding_common.h>
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
extern size_t log_ses_count[];
|
||||
extern __thread log_info_t tls_log_info;
|
||||
|
||||
/**
|
||||
* Extract the database name from a COM_INIT_DB or literal USE ... query.
|
||||
* @param buf Buffer with the database change query
|
||||
* @param str Pointer where the database name is copied
|
||||
* @return True for success, false for failure
|
||||
*/
|
||||
bool extract_database(GWBUF* buf, char* str)
|
||||
{
|
||||
uint8_t* packet;
|
||||
char *saved,*tok,*query = NULL;
|
||||
bool succp = true;
|
||||
unsigned int plen;
|
||||
|
||||
packet = GWBUF_DATA(buf);
|
||||
plen = gw_mysql_get_byte3(packet) - 1;
|
||||
|
||||
/** Copy database name from MySQL packet to session */
|
||||
if(query_classifier_get_operation(buf) == QUERY_OP_CHANGE_DB)
|
||||
{
|
||||
query = modutil_get_SQL(buf);
|
||||
tok = strtok_r(query," ;",&saved);
|
||||
if(tok == NULL || strcasecmp(tok,"use") != 0)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet.");
|
||||
succp = false;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
tok = strtok_r(NULL," ;",&saved);
|
||||
if(tok == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet.");
|
||||
succp = false;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
strncpy(str,tok,MYSQL_DATABASE_MAXLEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(str,packet + 5,plen);
|
||||
memset(str + plen,0,1);
|
||||
}
|
||||
retblock:
|
||||
free(query);
|
||||
return succp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fake error message from a DCB.
|
||||
* @param fail_str Custom error message
|
||||
* @param dcb DCB to use as the origin of the error
|
||||
*/
|
||||
void create_error_reply(char* fail_str,DCB* dcb)
|
||||
{
|
||||
skygw_log_write_flush(
|
||||
LOGFILE_TRACE,
|
||||
"change_current_db: failed to change database: %s", fail_str);
|
||||
GWBUF* errbuf = modutil_create_mysql_err_msg(1, 0, 1049, "42000", fail_str);
|
||||
|
||||
if (errbuf == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Creating buffer for error message failed.")));
|
||||
return;
|
||||
}
|
||||
/** Set flags that help router to identify session commands reply */
|
||||
gwbuf_set_type(errbuf, GWBUF_TYPE_MYSQL);
|
||||
gwbuf_set_type(errbuf, GWBUF_TYPE_SESCMD_RESPONSE);
|
||||
gwbuf_set_type(errbuf, GWBUF_TYPE_RESPONSE_END);
|
||||
|
||||
poll_add_epollin_event_to_dcb(dcb,
|
||||
errbuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read new database name from MYSQL_COM_INIT_DB packet or a literal USE ... COM_QUERY packet, check that it exists
|
||||
* in the hashtable and copy its name to MYSQL_session.
|
||||
*
|
||||
* @param mysql_session The MySQL session structure
|
||||
* @param dbhash Hashtable containing valid databases
|
||||
* @param buf Buffer containing the database change query
|
||||
*
|
||||
* @return true if new database is set, false if non-existent database was tried
|
||||
* to be set
|
||||
*/
|
||||
bool change_current_db(MYSQL_session* mysql_session,
|
||||
HASHTABLE* dbhash,
|
||||
GWBUF* buf)
|
||||
{
|
||||
char* target;
|
||||
bool succp;
|
||||
char db[MYSQL_DATABASE_MAXLEN+1];
|
||||
if(GWBUF_LENGTH(buf) <= MYSQL_DATABASE_MAXLEN - 5)
|
||||
{
|
||||
/** Copy database name from MySQL packet to session */
|
||||
if(!extract_database(buf,db))
|
||||
{
|
||||
succp = false;
|
||||
goto retblock;
|
||||
}
|
||||
skygw_log_write(LOGFILE_TRACE,"change_current_db: INIT_DB with database '%s'",
|
||||
db);
|
||||
/**
|
||||
* Update the session's active database only if it's in the hashtable.
|
||||
* If it isn't found, send a custom error packet to the client.
|
||||
*/
|
||||
|
||||
if((target = (char*)hashtable_fetch(dbhash,(char*)db)) == NULL)
|
||||
{
|
||||
succp = false;
|
||||
goto retblock;
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(mysql_session->db,db,MYSQL_DATABASE_MAXLEN);
|
||||
skygw_log_write(LOGFILE_TRACE,"change_current_db: database is on server: '%s'.",target);
|
||||
succp = true;
|
||||
goto retblock;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Create error message */
|
||||
skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"change_current_db: failed to change database: Query buffer too large");
|
||||
skygw_log_write_flush(LOGFILE_TRACE,
|
||||
"change_current_db: failed to change database: Query buffer too large [%d bytes]",GWBUF_LENGTH(buf));
|
||||
succp = false;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
retblock:
|
||||
return succp;
|
||||
}
|
@ -13,8 +13,9 @@
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2013-2014
|
||||
* Copyright MariaDB Corporation Ab 2013-2015
|
||||
*/
|
||||
|
||||
#include <my_config.h>
|
||||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
@ -24,6 +25,7 @@
|
||||
|
||||
#include <router.h>
|
||||
#include <shardrouter.h>
|
||||
#include <sharding_common.h>
|
||||
#include <secrets.h>
|
||||
#include <mysql.h>
|
||||
#include <skygw_utils.h>
|
||||
@ -119,9 +121,9 @@ static route_target_t get_shard_route_target(
|
||||
|
||||
static uint8_t getCapabilities(ROUTER* inst, void* router_session);
|
||||
|
||||
static void subsvc_clear_state(SUBSERVICE* svc,subsvc_state_t state);
|
||||
static void subsvc_set_state(SUBSERVICE* svc,subsvc_state_t state);
|
||||
static bool get_shard_subsvc(SUBSERVICE** subsvc,ROUTER_CLIENT_SES* session,char* target);
|
||||
void subsvc_clear_state(SUBSERVICE* svc,subsvc_state_t state);
|
||||
void subsvc_set_state(SUBSERVICE* svc,subsvc_state_t state);
|
||||
bool get_shard_subsvc(SUBSERVICE** subsvc,ROUTER_CLIENT_SES* session,char* target);
|
||||
|
||||
static ROUTER_OBJECT MyObject = {
|
||||
createInstance,
|
||||
@ -205,19 +207,6 @@ static void refreshInstance(
|
||||
CONFIG_PARAMETER* param);
|
||||
|
||||
static int router_handle_state_switch(DCB* dcb, DCB_REASON reason, void* data);
|
||||
/*
|
||||
static bool handle_error_new_connection(
|
||||
ROUTER_INSTANCE* inst,
|
||||
ROUTER_CLIENT_SES* rses,
|
||||
DCB* backend_dcb,
|
||||
GWBUF* errmsg);
|
||||
static void handle_error_reply_client(
|
||||
SESSION* ses,
|
||||
ROUTER_CLIENT_SES* rses,
|
||||
DCB* backend_dcb,
|
||||
GWBUF* errmsg);
|
||||
*/
|
||||
|
||||
|
||||
static SPINLOCK instlock;
|
||||
static ROUTER_INSTANCE* instances;
|
||||
@ -225,11 +214,6 @@ static ROUTER_INSTANCE* instances;
|
||||
static int hashkeyfun(void* key);
|
||||
static int hashcmpfun(void *, void *);
|
||||
|
||||
static bool change_current_db(
|
||||
ROUTER_INSTANCE* inst,
|
||||
ROUTER_CLIENT_SES* rses,
|
||||
GWBUF* buf);
|
||||
|
||||
static int
|
||||
hashkeyfun(void* key)
|
||||
{
|
||||
@ -300,8 +284,8 @@ char* get_lenenc_str(void* data, int* len)
|
||||
break;
|
||||
case 0xfe:
|
||||
size = *ptr + ((*(ptr + 2) << 8)) + (*(ptr + 3) << 16) +
|
||||
(*(ptr + 4) << 24) + (*(ptr + 5) << 32) + (*(ptr + 6) << 40) +
|
||||
(*(ptr + 7) << 48) + (*(ptr + 8) << 56);
|
||||
(*(ptr + 4) << 24) + ((long)*(ptr + 5) << 32) + ((long)*(ptr + 6) << 40) +
|
||||
((long)*(ptr + 7) << 48) + ((long)*(ptr + 8) << 56);
|
||||
offset = 8;
|
||||
break;
|
||||
default:
|
||||
@ -972,6 +956,7 @@ createInstance(SERVICE *service, char **options)
|
||||
if((res_svc = calloc(sz, sizeof(SERVICE*))) == NULL)
|
||||
{
|
||||
free(router);
|
||||
free(services);
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: Memory allocation failed.");
|
||||
return NULL;
|
||||
}
|
||||
@ -1190,7 +1175,7 @@ newSession(
|
||||
atomic_add(&client_rses->rses_versno, 2);
|
||||
ss_dassert(client_rses->rses_versno == 2);
|
||||
|
||||
client_rses->dbhash = hashtable_alloc(100, hashkeyfun, hashcmpfun);
|
||||
client_rses->dbhash = hashtable_alloc(100, simple_str_hash,strcmp);
|
||||
hashtable_memory_fns(client_rses->dbhash, (HASHMEMORYFN) strdup,
|
||||
(HASHMEMORYFN) strdup,
|
||||
(HASHMEMORYFN) free,
|
||||
@ -1296,11 +1281,10 @@ freeSession(
|
||||
void* router_client_session)
|
||||
{
|
||||
ROUTER_CLIENT_SES* router_cli_ses;
|
||||
ROUTER_INSTANCE* router;
|
||||
int i;
|
||||
|
||||
router_cli_ses = (ROUTER_CLIENT_SES *) router_client_session;
|
||||
router = (ROUTER_INSTANCE *) router_instance;
|
||||
|
||||
|
||||
/**
|
||||
* For each property type, walk through the list, finalize properties
|
||||
@ -1564,7 +1548,7 @@ routeQuery(ROUTER* instance,
|
||||
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
|
||||
mysql_server_cmd_t packet_type;
|
||||
uint8_t* packet;
|
||||
int ret = 1;
|
||||
int i,ret = 1;
|
||||
SUBSERVICE* target_subsvc;
|
||||
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *) instance;
|
||||
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *) router_session;
|
||||
@ -1573,7 +1557,10 @@ routeQuery(ROUTER* instance,
|
||||
route_target_t route_target = TARGET_UNDEFINED;
|
||||
bool succp = false;
|
||||
char* tname = NULL;
|
||||
skygw_log_write_flush(LOGFILE_TRACE,"shardrouter: routeQuery");
|
||||
char db[MYSQL_DATABASE_MAXLEN + 1];
|
||||
char errbuf[26+MYSQL_DATABASE_MAXLEN];
|
||||
|
||||
skygw_log_write_flush(LOGFILE_DEBUG,"shardrouter: routeQuery");
|
||||
CHK_CLIENT_RSES(router_cli_ses);
|
||||
|
||||
/** Dirty read for quick check if router is closed. */
|
||||
@ -1714,8 +1701,13 @@ routeQuery(ROUTER* instance,
|
||||
|
||||
if(packet_type == MYSQL_COM_INIT_DB)
|
||||
{
|
||||
if(!(change_successful = change_current_db(inst, router_cli_ses, querybuf)))
|
||||
if(!(change_successful = change_current_db(router_cli_ses->rses_mysql_session,
|
||||
router_cli_ses->dbhash,
|
||||
querybuf)))
|
||||
{
|
||||
extract_database(querybuf,db);
|
||||
snprintf(errbuf,"Unknown database: %s",db);
|
||||
create_error_reply(errbuf,router_cli_ses->replydcb);
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Changing database failed.")));
|
||||
@ -2066,39 +2058,6 @@ clientReply(
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
subsvc_set_state(SUBSERVICE* svc,subsvc_state_t state)
|
||||
{
|
||||
if(state & SUBSVC_WAITING_RESULT)
|
||||
{
|
||||
int prev1;
|
||||
|
||||
/** Increase waiter count */
|
||||
prev1 = atomic_add(&svc->n_res_waiting, 1);
|
||||
ss_dassert(prev1 >= 0);
|
||||
}
|
||||
|
||||
svc->state |= state;
|
||||
}
|
||||
|
||||
static void
|
||||
subsvc_clear_state(SUBSERVICE* svc,subsvc_state_t state)
|
||||
{
|
||||
|
||||
|
||||
if(state & SUBSVC_WAITING_RESULT)
|
||||
{
|
||||
int prev1;
|
||||
|
||||
/** Decrease waiter count */
|
||||
prev1 = atomic_add(&svc->n_res_waiting, -1);
|
||||
ss_dassert(prev1 >= 0);
|
||||
}
|
||||
|
||||
svc->state &= ~state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a generic router session property strcture.
|
||||
*/
|
||||
@ -2639,8 +2598,7 @@ mysql_sescmd_get_property(
|
||||
* capabilities specified, rc > 0 when there are capabilities.
|
||||
*/
|
||||
static uint8_t
|
||||
getCapabilities(
|
||||
ROUTER* inst,
|
||||
getCapabilities(ROUTER* inst,
|
||||
void* router_session)
|
||||
{
|
||||
ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *) router_session;
|
||||
@ -2910,37 +2868,7 @@ handleError(
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
get_shard_subsvc(SUBSERVICE** subsvc,ROUTER_CLIENT_SES* session,char* target)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(subsvc == NULL || session == NULL || target == NULL)
|
||||
return false;
|
||||
|
||||
for(i = 0;i<session->n_subservice;i++)
|
||||
{
|
||||
if(strcmp(session->subservice[i]->service->name,target) == 0)
|
||||
{
|
||||
|
||||
if (SUBSVC_IS_OK(session->subservice[i]))
|
||||
{
|
||||
if(subsvc_is_valid(session->subservice[i])){
|
||||
*subsvc = session->subservice[i];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The service has failed
|
||||
*/
|
||||
|
||||
subsvc_set_state(session->subservice[i],SUBSVC_FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Finds the subservice who owns this session.
|
||||
* @param rses Router client session
|
||||
@ -3008,110 +2936,3 @@ return_rc:
|
||||
return rc;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Read new database nbame from MYSQL_COM_INIT_DB packet, check that it exists
|
||||
* in the hashtable and copy its name to MYSQL_session.
|
||||
*
|
||||
* @param inst Router instance
|
||||
* @param rses Router client session
|
||||
* @param buf Query buffer
|
||||
*
|
||||
* @return true if new database is set, false if non-existent database was tried
|
||||
* to be set
|
||||
*/
|
||||
static bool
|
||||
change_current_db(
|
||||
ROUTER_INSTANCE* inst,
|
||||
ROUTER_CLIENT_SES* rses,
|
||||
GWBUF* buf)
|
||||
{
|
||||
bool succp;
|
||||
uint8_t* packet;
|
||||
unsigned int plen;
|
||||
int message_len;
|
||||
char* fail_str;
|
||||
|
||||
if(GWBUF_LENGTH(buf) <= MYSQL_DATABASE_MAXLEN - 5)
|
||||
{
|
||||
packet = GWBUF_DATA(buf);
|
||||
plen = gw_mysql_get_byte3(packet) - 1;
|
||||
|
||||
/** Copy database name from MySQL packet to session */
|
||||
|
||||
memcpy(rses->rses_mysql_session->db,
|
||||
packet + 5,
|
||||
plen);
|
||||
memset(rses->rses_mysql_session->db + plen, 0, 1);
|
||||
|
||||
/**
|
||||
* Update the session's active database only if it's in the hashtable.
|
||||
* If it isn't found, send a custom error packet to the client.
|
||||
*/
|
||||
|
||||
if(hashtable_fetch(
|
||||
rses->dbhash,
|
||||
(char*) rses->rses_mysql_session->db) == NULL)
|
||||
{
|
||||
|
||||
/** Create error message */
|
||||
message_len = 25 + MYSQL_DATABASE_MAXLEN;
|
||||
fail_str = calloc(1, message_len + 1);
|
||||
snprintf(fail_str,
|
||||
message_len,
|
||||
"Unknown database '%s'",
|
||||
(char*) rses->rses_mysql_session->db);
|
||||
rses->rses_mysql_session->db[0] = '\0';
|
||||
succp = false;
|
||||
goto reply_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = true;
|
||||
goto retblock;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Create error message */
|
||||
message_len = 25 + MYSQL_DATABASE_MAXLEN;
|
||||
fail_str = calloc(1, message_len + 1);
|
||||
snprintf(fail_str,
|
||||
message_len,
|
||||
"Unknown database '%s'",
|
||||
(char*) rses->rses_mysql_session->db);
|
||||
succp = false;
|
||||
goto reply_error;
|
||||
}
|
||||
reply_error:
|
||||
{
|
||||
GWBUF* errbuf;
|
||||
skygw_log_write_flush(
|
||||
LOGFILE_TRACE,
|
||||
"shardrouter: failed to change database: %s", fail_str);
|
||||
errbuf = modutil_create_mysql_err_msg(1, 0, 1049, "42000", fail_str);
|
||||
errbuf = modutil_create_mysql_err_msg(1, 0, 1049, "42000", fail_str);
|
||||
free(fail_str);
|
||||
|
||||
if(errbuf == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Creating buffer for error message failed.")));
|
||||
goto retblock;
|
||||
}
|
||||
/** Set flags that help router to identify session commans reply */
|
||||
gwbuf_set_type(errbuf, GWBUF_TYPE_MYSQL);
|
||||
gwbuf_set_type(errbuf, GWBUF_TYPE_SESCMD_RESPONSE);
|
||||
gwbuf_set_type(errbuf, GWBUF_TYPE_RESPONSE_END);
|
||||
/**
|
||||
* Create an incoming event for randomly selected backend DCB which
|
||||
* will then be notified and replied 'back' to the client.
|
||||
*/
|
||||
poll_add_epollin_event_to_dcb(rses->replydcb,
|
||||
gwbuf_clone(errbuf));
|
||||
gwbuf_free(errbuf);
|
||||
}
|
||||
retblock:
|
||||
return succp;
|
||||
}
|
||||
|
78
server/modules/routing/schemarouter/svcconn.c
Normal file
78
server/modules/routing/schemarouter/svcconn.c
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is distributed as part of the MariaDB Corporation MaxScale. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2013-2014
|
||||
*/
|
||||
|
||||
#include <shardrouter.h>
|
||||
|
||||
void
|
||||
subsvc_set_state(SUBSERVICE* svc,subsvc_state_t state)
|
||||
{
|
||||
if(state & SUBSVC_WAITING_RESULT)
|
||||
{
|
||||
|
||||
/** Increase waiter count */
|
||||
atomic_add(&svc->n_res_waiting, 1);
|
||||
}
|
||||
|
||||
svc->state |= state;
|
||||
}
|
||||
|
||||
void
|
||||
subsvc_clear_state(SUBSERVICE* svc,subsvc_state_t state)
|
||||
{
|
||||
|
||||
|
||||
if(state & SUBSVC_WAITING_RESULT)
|
||||
{
|
||||
/** Decrease waiter count */
|
||||
atomic_add(&svc->n_res_waiting, -1);
|
||||
}
|
||||
|
||||
svc->state &= ~state;
|
||||
}
|
||||
|
||||
bool
|
||||
get_shard_subsvc(SUBSERVICE** subsvc,ROUTER_CLIENT_SES* session,char* target)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(subsvc == NULL || session == NULL || target == NULL)
|
||||
return false;
|
||||
|
||||
for(i = 0;i<session->n_subservice;i++)
|
||||
{
|
||||
if(strcmp(session->subservice[i]->service->name,target) == 0)
|
||||
{
|
||||
|
||||
if (SUBSVC_IS_OK(session->subservice[i]))
|
||||
{
|
||||
if(subsvc_is_valid(session->subservice[i])){
|
||||
*subsvc = session->subservice[i];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The service has failed
|
||||
*/
|
||||
|
||||
subsvc_set_state(session->subservice[i],SUBSVC_FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
Reference in New Issue
Block a user