Merge from Z3

Merge from Z3
This commit is contained in:
MassimilianoPinto
2014-09-11 12:51:16 +02:00
148 changed files with 9288 additions and 811 deletions

View File

@ -21,15 +21,16 @@
include ../../../build_gateway.inc
LOGPATH := $(ROOT_PATH)/log_manager
QCLASSPATH := $(ROOT_PATH)/query_classifier
UTILSPATH := $(ROOT_PATH)/utils
CC=cc
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -I$(LOGPATH) \
-I$(UTILSPATH) -Wall -g
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -I$(LOGPATH) -I$(QCLASSPATH) \
-I$(UTILSPATH) -I$(MYSQL_ROOT) -Wall -g
include ../../../makefile.inc
LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(DEST)/lib \
LDFLAGS=-shared -L$(LOGPATH) -L$(EMBEDDED_LIB) -L$(QCLASSPATH) -Wl,-rpath,$(DEST)/lib \
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH)
TESTSRCS=testfilter.c
@ -42,17 +43,31 @@ TOPNSRCS=topfilter.c
TOPNOBJ=$(TOPNSRCS:.c=.o)
TEESRCS=tee.c
TEEOBJ=$(TEESRCS:.c=.o)
MQSRCS=mqfilter.c
MQOBJ=$(MQSRCS:.c=.o)
SRCS=$(TESTSRCS) $(QLASRCS) $(REGEXSRCS) $(TOPNSRCS) $(TEESRCS)
OBJ=$(SRCS:.c=.o)
LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
MODULES= libtestfilter.so libqlafilter.so libregexfilter.so libtopfilter.so libtee.so
MODULES= libtestfilter.so libqlafilter.so libregexfilter.so libtopfilter.so libhintfilter.so libtee.so
ifndef BUILD_RABBITMQ
BUILD_RABBITMQ=Y
endif
ifeq ($(BUILD_RABBITMQ),Y)
SRCS += $(MQSRCS)
MODULES += libmqfilter.so
LIBS += -lrabbitmq -lquery_classifier
endif
all: $(MODULES)
libtestfilter.so: $(TESTOBJ)
$(CC) $(LDFLAGS) $(TESTOBJ) $(LIBS) -o $@
libmqfilter.so: $(MQOBJ)
$(CC) $(LDFLAGS) $(MQOBJ) $(LIBS) -o $@
libqlafilter.so: $(QLAOBJ)
$(CC) $(LDFLAGS) $(QLAOBJ) $(LIBS) -o $@
@ -65,32 +80,38 @@ libtopfilter.so: $(TOPNOBJ)
libtee.so: $(TEEOBJ)
$(CC) $(LDFLAGS) $(TEEOBJ) $(LIBS) -o $@
libhintfilter.so:
(cd hint; touch depend.mk ; make; cp $@ ..)
.c.o:
$(CC) $(CFLAGS) $< -o $@
clean:
$(DEL) $(OBJ) $(MODULES)
rm -f $(OBJ) $(MODULES)
(cd hint; touch depend.mk; make clean)
tags:
ctags $(SRCS) $(HDRS)
(cd hint; touch depend.mk; make tags)
depend:
@$(DEL) depend.mk
@rm -f depend.mk
cc -M $(CFLAGS) $(SRCS) > depend.mk
(cd hint; touch depend.mk; make depend)
install: $(MODULES)
install -D $(MODULES) $(DEST)/modules
cleantests:
$(MAKE) -C test cleantests
buildtests:
$(MAKE) -C test DEBUG=Y buildtests
runtests:
$(MAKE) -C test runtests
testall:
$(MAKE) -C test testall
include depend.mk

View File

@ -0,0 +1,70 @@
# This file is distributed as part of MaxScale form SkySQL. 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 SkySQL Ab 2014
#
# Revision History
# Date Who Description
# 21/07/14 Mark Riddoch Initial module development
include ../../../../build_gateway.inc
LOGPATH := $(ROOT_PATH)/log_manager
UTILSPATH := $(ROOT_PATH)/utils
CC=cc
CFLAGS=-c -fPIC -I/usr/include -I../../include -I../../../include -I$(LOGPATH) \
-I$(UTILSPATH) -Wall -g
include ../../../../makefile.inc
LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(DEST)/lib \
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH)
SRCS= hintfilter.c hintparser.c
OBJ=$(SRCS:.c=.o)
LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
libhintfilter.so: $(OBJ)
$(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@
.c.o:
$(CC) $(CFLAGS) $< -o $@
clean:
rm -f $(OBJ) libhintfilter.so
tags:
ctags $(SRCS) $(HDRS)
depend:
@rm -f depend.mk
cc -M $(CFLAGS) $(SRCS) > depend.mk
install: $(MODULES)
install -D $(MODULES) $(DEST)/modules
cleantests:
$(MAKE) -C test cleantests
buildtests:
$(MAKE) -C test DEBUG=Y buildtests
runtests:
$(MAKE) -C test runtests
testall:
$(MAKE) -C test testall
include depend.mk

View File

@ -0,0 +1,272 @@
/*
* This file is distributed as part of MaxScale by SkySQL. 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 SkySQL Ab 2014
*/
#include <stdio.h>
#include <filter.h>
#include <modinfo.h>
#include <modutil.h>
#include <mysqlhint.h>
/**
* hintfilter.c - a filter to parse the MaxScale hint syntax and attach those
* hints to the buffers that carry the requests.
*
*/
MODULE_INFO info = {
MODULE_API_FILTER,
MODULE_ALPHA_RELEASE,
FILTER_VERSION,
"A hint parsing filter"
};
static char *version_str = "V1.0.0";
static FILTER *createInstance(char **options, FILTER_PARAMETER **params);
static void *newSession(FILTER *instance, SESSION *session);
static void closeSession(FILTER *instance, void *session);
static void freeSession(FILTER *instance, void *session);
static void setDownstream(FILTER *instance, void *fsession, DOWNSTREAM *downstream);
static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue);
static void diagnostic(FILTER *instance, void *fsession, DCB *dcb);
static FILTER_OBJECT MyObject = {
createInstance,
newSession,
closeSession,
freeSession,
setDownstream,
NULL, // No upstream requirement
routeQuery,
NULL,
diagnostic,
};
/**
* Implementation of the mandatory version entry point
*
* @return version string of the module
*/
char *
version()
{
return version_str;
}
/**
* The module initialisation routine, called when the module
* is first loaded.
*/
void
ModuleInit()
{
}
/**
* The module entry point routine. It is this routine that
* must populate the structure that is referred to as the
* "module object", this is a structure with the set of
* external entry points for this module.
*
* @return The module object
*/
FILTER_OBJECT *
GetModuleObject()
{
return &MyObject;
}
/**
* Create an instance of the filter for a particular service
* within MaxScale.
*
* @param options The options for this filter
*
* @return The instance data for this new instance
*/
static FILTER *
createInstance(char **options, FILTER_PARAMETER **params)
{
HINT_INSTANCE *my_instance;
if ((my_instance = calloc(1, sizeof(HINT_INSTANCE))) != NULL)
my_instance->sessions = 0;
return (FILTER *)my_instance;
}
/**
* Associate a new session with this instance of the filter.
*
* @param instance The filter instance data
* @param session The session itself
* @return Session specific data for this session
*/
static void *
newSession(FILTER *instance, SESSION *session)
{
HINT_INSTANCE *my_instance = (HINT_INSTANCE *)instance;
HINT_SESSION *my_session;
if ((my_session = calloc(1, sizeof(HINT_SESSION))) != NULL)
{
my_session->query_len = 0;
my_session->request = NULL;
my_session->stack = NULL;
my_session->named_hints = NULL;
}
return my_session;
}
/**
* Close a session with the filter, this is the mechanism
* by which a filter may cleanup data structure etc.
*
* @param instance The filter instance data
* @param session The session being closed
*/
static void
closeSession(FILTER *instance, void *session)
{
HINT_SESSION *my_session = (HINT_SESSION *)session;
NAMEDHINTS* named_hints;
HINTSTACK* hint_stack;
if (my_session->request)
gwbuf_free(my_session->request);
/** Free named hints */
named_hints = my_session->named_hints;
while ((named_hints = free_named_hint(named_hints)) != NULL)
;
/** Free stacked hints */
hint_stack = my_session->stack;
while ((hint_stack = free_hint_stack(hint_stack)) != NULL)
;
}
/**
* Free the memory associated with this filter session.
*
* @param instance The filter instance data
* @param session The session being closed
*/
static void
freeSession(FILTER *instance, void *session)
{
free(session);
return;
}
/**
* Set the downstream component for this filter.
*
* @param instance The filter instance data
* @param session The session being closed
* @param downstream The downstream filter or router
*/
static void
setDownstream(FILTER *instance, void *session, DOWNSTREAM *downstream)
{
HINT_SESSION *my_session = (HINT_SESSION *)session;
my_session->down = *downstream;
}
/**
* The routeQuery entry point. This is passed the query buffer
* to which the filter should be applied. Once applied the
* query shoudl normally be passed to the downstream component
* (filter or router) in the filter chain.
*
* @param instance The filter instance data
* @param session The filter session
* @param queue The query data
*/
static int
routeQuery(FILTER *instance, void *session, GWBUF *queue)
{
HINT_SESSION *my_session = (HINT_SESSION *)session;
char *ptr;
int rval, len, residual;
HINT *hint;
if (my_session->request == NULL)
{
/*
* No stored buffer, so this must be the first
* buffer of a new request.
*/
if (modutil_MySQL_Query(queue, &ptr, &len, &residual) == 0)
{
return my_session->down.routeQuery(
my_session->down.instance,
my_session->down.session, queue);
}
my_session->request = queue;
my_session->query_len = len;
}
else
{
gwbuf_append(my_session->request, queue);
}
if (gwbuf_length(my_session->request) < my_session->query_len)
{
/*
* We have not got the entire SQL text, buffer and wait for
* the remainder.
*/
return 1;
}
/* We have the entire SQL text, parse for hints and attach to the
* buffer at the head of the queue.
*/
queue = my_session->request;
my_session->request = NULL;
my_session->query_len = 0;
hint = hint_parser(my_session, queue);
queue->hint = hint;
/* Now process the request */
rval = my_session->down.routeQuery(my_session->down.instance,
my_session->down.session, queue);
return rval;
}
/**
* Diagnostics routine
*
* If fsession is NULL then print diagnostics on the filter
* instance as a whole, otherwise print diagnostics for the
* particular session.
*
* @param instance The filter instance
* @param fsession Filter session, may be NULL
* @param dcb The DCB for diagnostic output
*/
static void
diagnostic(FILTER *instance, void *fsession, DCB *dcb)
{
HINT_INSTANCE *my_instance = (HINT_INSTANCE *)instance;
HINT_SESSION *my_session = (HINT_SESSION *)fsession;
}

View File

@ -0,0 +1,770 @@
/*
* This file is distributed as part of MaxScale by SkySQL. 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 SkySQL Ab 2014
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <skygw_utils.h>
#include <log_manager.h>
#include <filter.h>
#include <modinfo.h>
#include <modutil.h>
#include <mysqlhint.h>
extern int lm_enabled_logfiles_bitmask;
/**
* hintparser.c - Find any comment in the SQL packet and look for MAXSCALE
* hints in that comment.
*/
/**
* The keywords in the hint syntax
*/
struct {
char *keyword;
TOKEN_VALUE token;
} keywords[] = {
{ "maxscale", TOK_MAXSCALE },
{ "prepare", TOK_PREPARE },
{ "start", TOK_START },
{ "begin", TOK_START },
{ "stop", TOK_STOP },
{ "end", TOK_STOP },
{ "=", TOK_EQUAL },
{ "route", TOK_ROUTE },
{ "to", TOK_TO },
{ "master", TOK_MASTER },
{ "slave", TOK_SLAVE },
{ "server", TOK_SERVER },
{ NULL, 0 }
};
/**
HINT_TOKEN kwords[] = {
{ TOK_MAXSCALE, "maxscale" },
{ TOK_PREPARE, "prepare" },
{ TOK_START, "start" },
{ TOK_START, "begin" },
{ TOK_STOP, "stop" },
{ TOK_STOP, "end" },
{ TOK_EQUAL, "=" },
{ TOK_ROUTE, "route" },
{ TOK_TO, "to" },
{ TOK_MASTER, "master" },
{ TOK_SLAVE, "slave" },
{ TOK_SERVER, "server" },
{ 0, NULL}
};
*/
static HINT_TOKEN *hint_next_token(GWBUF **buf, char **ptr);
static void hint_pop(HINT_SESSION *);
static HINT *lookup_named_hint(HINT_SESSION *, char *);
static void create_named_hint(HINT_SESSION *, char *, HINT *);
static void hint_push(HINT_SESSION *, HINT *);
static const char* token_get_keyword (HINT_TOKEN* token);
static void token_free(HINT_TOKEN* token);
typedef enum { HM_EXECUTE, HM_START, HM_PREPARE } HINT_MODE;
void token_free(HINT_TOKEN* token)
{
if (token->value != NULL)
{
free(token->value);
}
free(token);
}
static const char* token_get_keyword (
HINT_TOKEN* token)
{
switch (token->token) {
case TOK_EOL:
return "End of line";
break;
case TOK_STRING:
return token->value;
break;
default:
{
int i = 0;
while (i < TOK_EOL && keywords[i].token != token->token)
i++;
ss_dassert(i != TOK_EOL);
if (i == TOK_EOL)
{
return "Unknown token";
}
else
{
return keywords[i].keyword;
}
}
break;
}
}
/**
* Parse the hint comments in the MySQL statement passed in request.
* Add any hints to the buffer for later processing.
*
* @param session The filter session
* @param request The MySQL request buffer
* @return The hints parsed in this statement or active on the
* stack
*/
HINT *
hint_parser(HINT_SESSION *session, GWBUF *request)
{
char *ptr, lastch = ' ';
int len, residual, state;
int found, escape, quoted, squoted;
HINT *rval = NULL;
char *pname, *lvalue, *hintname = NULL;
GWBUF *buf;
HINT_TOKEN *tok;
HINT_MODE mode = HM_EXECUTE;
/* First look for any comment in the SQL */
modutil_MySQL_Query(request, &ptr, &len, &residual);
buf = request;
found = 0;
escape = 0;
quoted = 0;
squoted = 0;
do {
while (len--)
{
if (*ptr == '\\')
escape = 1;
else if (*ptr == '\"' && quoted)
quoted = 0;
else if (*ptr == '\"' && quoted == 0)
quoted = 0;
else if (*ptr == '\'' && squoted)
squoted = 0;
else if (*ptr == '\"' && squoted == 0)
squoted = 0;
else if (quoted || squoted)
;
else if (escape)
escape = 0;
else if (*ptr == '#')
{
found = 1;
break;
}
else if (*ptr == '/')
lastch = '/';
else if (*ptr == '*' && lastch == '/')
{
found = 1;
break;
}
else if (*ptr == '-' && lastch == '-')
{
found = 1;
break;
}
else if (*ptr == '-')
lastch = '-';
else
lastch = *ptr;
ptr++;
}
if (found)
break;
buf = buf->next;
if (buf)
{
len = GWBUF_LENGTH(buf);
ptr = GWBUF_DATA(buf);
}
} while (buf);
if (!found) /* No comment so we need do no more */
{
goto retblock;
}
/*
* If we have got here then we have a comment, ptr point to
* the comment character if it is a '#' comment or the second
* character of the comment if it is a -- or /* comment
*
* Move to the next character in the SQL.
*/
ptr++;
if (ptr > (char *)(buf->end))
{
buf = buf->next;
if (buf)
ptr = GWBUF_DATA(buf);
else
goto retblock;
}
tok = hint_next_token(&buf, &ptr);
if (tok == NULL)
{
goto retblock;
}
/** This is not MaxScale hint because it doesn't start with 'maxscale' */
if (tok->token != TOK_MAXSCALE)
{
token_free(tok);
goto retblock;
}
token_free(tok);
state = HS_INIT;
while ((tok = hint_next_token(&buf, &ptr)) != NULL
&& tok->token != TOK_EOL)
{
switch (state)
{
case HS_INIT:
switch (tok->token)
{
case TOK_ROUTE:
state = HS_ROUTE;
break;
case TOK_STRING:
state = HS_NAME;
lvalue = strdup(tok->value);
break;
case TOK_STOP:
/* Action: pop active hint */
hint_pop(session);
state = HS_INIT;
break;
case TOK_START:
hintname = NULL;
mode = HM_START;
state = HS_INIT;
break;
default:
/* Error: expected hint, name or STOP */
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Error : Syntax error in hint. Expected "
"'route', 'stop' or hint name instead of "
"'%s'. Hint ignored.",
token_get_keyword(tok))));
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Syntax error in hint. Expected "
"'route', 'stop' or hint name instead of "
"'%s'. Hint ignored.",
token_get_keyword(tok))));
token_free(tok);
goto retblock;
}
break;
case HS_ROUTE:
if (tok->token != TOK_TO)
{
/* Error, expect TO */;
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Error : Syntax error in hint. Expected "
"'to' instead of '%s'. Hint ignored.",
token_get_keyword(tok))));
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Syntax error in hint. Expected "
"'to' instead of '%s'. Hint ignored.",
token_get_keyword(tok))));
token_free(tok);
goto retblock;
}
state = HS_ROUTE1;
break;
case HS_ROUTE1:
switch (tok->token)
{
case TOK_MASTER:
rval = hint_create_route(rval,
HINT_ROUTE_TO_MASTER, NULL);
break;
case TOK_SLAVE:
rval = hint_create_route(rval,
HINT_ROUTE_TO_SLAVE, NULL);
break;
case TOK_SERVER:
state = HS_ROUTE_SERVER;
break;
default:
/* Error expected MASTER, SLAVE or SERVER */
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Error : Syntax error in hint. Expected "
"'master', 'slave', or 'server' instead "
"of '%s'. Hint ignored.",
token_get_keyword(tok))));
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Syntax error in hint. Expected "
"'master', 'slave', or 'server' instead "
"of '%s'. Hint ignored.",
token_get_keyword(tok))));
token_free(tok);
goto retblock;
}
break;
case HS_ROUTE_SERVER:
if (tok->token == TOK_STRING)
{
rval = hint_create_route(rval,
HINT_ROUTE_TO_NAMED_SERVER, tok->value);
}
else
{
/* Error: Expected server name */
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Error : Syntax error in hint. Expected "
"server name instead of '%s'. Hint "
"ignored.",
token_get_keyword(tok))));
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Syntax error in hint. Expected "
"server name instead of '%s'. Hint "
"ignored.",
token_get_keyword(tok))));
token_free(tok);
goto retblock;
}
break;
case HS_NAME:
switch (tok->token)
{
case TOK_EQUAL:
pname = lvalue;
state = HS_PVALUE;
break;
case TOK_PREPARE:
pname = lvalue;
state = HS_PREPARE;
break;
case TOK_START:
/* Action start(lvalue) */
hintname = lvalue;
mode = HM_START;
state = HS_INIT;
break;
default:
/* Error, token tok->value not expected */
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Error : Syntax error in hint. Expected "
"'=', 'prepare', or 'start' instead of "
"'%s'. Hint ignored.",
token_get_keyword(tok))));
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Syntax error in hint. Expected "
"'=', 'prepare', or 'start' instead of "
"'%s'. Hint ignored.",
token_get_keyword(tok))));
token_free(tok);
goto retblock;
}
break;
case HS_PVALUE:
/* Action: pname = tok->value */
rval = hint_create_parameter(rval, pname, tok->value);
state = HS_INIT;
break;
case HS_PREPARE:
mode = HM_PREPARE;
hintname = lvalue;
switch (tok->token)
{
case TOK_ROUTE:
state = HS_ROUTE;
break;
case TOK_STRING:
state = HS_NAME;
lvalue = tok->value;
break;
default:
/* Error, token tok->value not expected */
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Error : Syntax error in hint. Expected "
"'route' or hint name instead of "
"'%s'. Hint ignored.",
token_get_keyword(tok))));
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Syntax error in hint. Expected "
"'route' or hint name instead of "
"'%s'. Hint ignored.",
token_get_keyword(tok))));
token_free(tok);
goto retblock;
}
break;
}
token_free(tok);
} /*< while */
if (tok->token == TOK_EOL)
{
token_free(tok);
}
switch (mode)
{
case HM_START:
/*
* We are starting either a predefined set of hints,
* creating a new set of hints and starting in a single
* operation or starting an anonymous block of hints.
*/
if (hintname == NULL && rval != NULL)
{
/* We are starting an anonymous block of hints */
hint_push(session, rval);
rval = NULL;
} else if (hintname && rval)
{
/* We are creating and starting a block of hints */
if (lookup_named_hint(session, hintname) != NULL)
{
/* Error hint with this name already exists */
}
else
{
create_named_hint(session, hintname, rval);
hint_push(session, hint_dup(rval));
}
} else if (hintname && rval == NULL)
{
/* We starting an already define set of named hints */
rval = lookup_named_hint(session, hintname);
hint_push(session, hint_dup(rval));
free(hintname);
rval = NULL;
} else if (hintname == NULL && rval == NULL)
{
/* Error case */
}
break;
case HM_PREPARE:
/*
* We are preparing a named set of hints. Note this does
* not trigger the usage of these hints currently.
*/
if (hintname == NULL || rval == NULL)
{
/* Error case, name and hints must be defined */
}
else
{
create_named_hint(session, hintname, rval);
}
/* We are not starting the hints now, so return an empty
* hint set.
*/
rval = NULL;
break;
case HM_EXECUTE:
/*
* We have a one-off hint for the statement we are
* currently forwarding.
*/
break;
}
retblock:
if (rval == NULL)
{
/* No new hint parsed in this statement, apply the current
* top of stack if there is one.
*/
if (session->stack)
rval = hint_dup(session->stack->hint);
}
return rval;
}
/**
* Return the next token in the inout stream
*
* @param buf A pointer to the buffer point, will be updated if a
* new buffer is used.
* @param ptr The pointer within the buffer we are processing
* @return A HINT token
*/
static HINT_TOKEN *
hint_next_token(GWBUF **buf, char **ptr)
{
char word[100], *dest;
int inword = 0;
int endtag = 0;
char inquote = '\0';
int i, found;
HINT_TOKEN *tok;
if ((tok = (HINT_TOKEN *)malloc(sizeof(HINT_TOKEN))) == NULL)
return NULL;
tok->value = NULL;
dest = word;
while (*ptr < (char *)((*buf)->end) || (*buf)->next)
{
/** word ends, don't move ptr but return with read word */
if (inword && inquote == '\0' &&
(isspace(**ptr) || **ptr == '='))
{
inword = 0;
break;
}
/** found '=', move ptr and return with '=' */
else if (!inword && inquote == '\0' && **ptr == '=')
{
*dest = **ptr;
*dest++;
(*ptr)++;
break;
}
else if (**ptr == '\'' && inquote == '\'')
inquote = '\0';
else if (**ptr == '\'')
inquote = **ptr;
/** Any other character which belongs to the word, move ahead */
else if(**ptr == '/' && endtag)
{
/** Comment end tag found, rewind the pointer back and return the token */
inword = 0;
(*ptr)--;
break;
}
else if(**ptr == '*' && !endtag)
{
endtag = 1;
}
else if (inword || (isspace(**ptr) == 0))
{
*dest++ = **ptr;
inword = 1;
}
(*ptr)++;
if (*ptr > (char *)((*buf)->end) && (*buf)->next)
{
*buf = (*buf)->next;
*ptr = (*buf)->start;
}
if (dest - word > 98)
break;
} /*< while */
*dest = 0;
/* We now have a word in the local word, check to see if it is a
* token we recognise.
*/
if (word[0] == '\0' || (word[0] == '*' && word[1] == '/'))
{
tok->token = TOK_EOL;
return tok;
}
found = 0;
for (i = 0; keywords[i].keyword; i++)
{
if (strcasecmp(word, keywords[i].keyword) == 0)
{
tok->token = keywords[i].token;
found = 1;
break;
}
}
if (found == 0)
{
tok->token = TOK_STRING;
tok->value = strdup(word);
}
return tok;
}
/**
* hint_pop - pop the hint off the top of the stack if it is not empty
*
* @param session The filter session.
*/
void
hint_pop(HINT_SESSION *session)
{
HINTSTACK *ptr;
HINT *hint;
if ((ptr = session->stack) != NULL)
{
session->stack = ptr->next;
while (ptr->hint)
{
hint = ptr->hint;
ptr->hint = hint->next;
hint_free(hint);
}
free(ptr);
}
}
/**
* Push a hint onto the stack of actie hints
*
* @param session The filter session
* @param hint The hint to push, the hint ownership is retained
* by the stack and should not be freed by the caller
*/
static void
hint_push(HINT_SESSION *session, HINT *hint)
{
HINTSTACK *item;
if ((item = (HINTSTACK *)malloc(sizeof(HINTSTACK))) == NULL)
return;
item->hint = hint;
item->next = session->stack;
session->stack = item;
}
/**
* Search for a hint block that already exists with this name
*
* @param session The filter session
* @param name The name to lookup
* @return the HINT or NULL if the name was not found.
*/
static HINT *
lookup_named_hint(HINT_SESSION *session, char *name)
{
NAMEDHINTS *ptr = session->named_hints;
while (ptr)
{
if (strcmp(ptr->name, name) == 0)
return ptr->hints;
ptr = ptr->next;
}
return NULL;
}
/**
* Create a named hint block
*
* @param session The filter session
* @param name The name of the block to ceate
* @param hint The hints themselves
*/
static void
create_named_hint(HINT_SESSION *session, char *name, HINT *hint)
{
NAMEDHINTS *block;
if ((block = (NAMEDHINTS *)malloc(sizeof(NAMEDHINTS))) == NULL)
return;
block->name = name;
block->hints = hint_dup(hint);
block->next = session->named_hints;
session->named_hints = block;
}
/**
* Release the given NAMEDHINTS struct and all included hints.
*
* @param named_hint NAMEDHINTS struct to be released
*
* @return pointer to next NAMEDHINTS struct.
*/
NAMEDHINTS* free_named_hint(
NAMEDHINTS* named_hint)
{
NAMEDHINTS* next;
if (named_hint != NULL)
{
HINT* hint;
next = named_hint->next;
while (named_hint->hints != NULL)
{
hint = named_hint->hints->next;
hint_free(named_hint->hints);
named_hint->hints = hint;
}
free(named_hint->name);
free(named_hint);
return next;
}
else
{
return NULL;
}
}
/**
* Release the given HINTSTACK struct and all included hints.
*
* @param hint_stack HINTSTACK struct to be released
*
* @return pointer to next HINTSTACK struct.
*/
HINTSTACK* free_hint_stack(
HINTSTACK* hint_stack)
{
HINTSTACK* next;
if (hint_stack != NULL)
{
HINT* hint;
next = hint_stack->next;
while (hint_stack->hint != NULL)
{
hint = hint_stack->hint->next;
hint_free(hint_stack->hint);
hint_stack->hint = hint;
}
free(hint_stack);
return next;
}
else
{
return NULL;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -219,7 +219,7 @@ typedef enum mysql_server_cmd {
MYSQL_COM_QUERY,
MYSQL_COM_FIELD_LIST,
MYSQL_COM_CREATE_DB,
MYSQL_COM_DROP_DB,
MYSQL_COM_DROP_DB,
MYSQL_COM_REFRESH,
MYSQL_COM_SHUTDOWN,
MYSQL_COM_STATISTICS,

View File

@ -0,0 +1,114 @@
#ifndef _MYSQLHINT_H
#define _MYSQLHINT_H
/*
* This file is distributed as part of the SkySQL Gateway. 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 SkySQL Ab 2013
*/
/*
* Revision History
*
* Date Who Description
* 17-07-2014 Mark Riddoch Initial implementation
*/
#include <hint.h>
/* Parser tokens for the hint parser */
typedef enum {
TOK_MAXSCALE = 1,
TOK_PREPARE,
TOK_START,
TOK_STOP,
TOK_EQUAL,
TOK_STRING,
TOK_ROUTE,
TOK_TO,
TOK_MASTER,
TOK_SLAVE,
TOK_SERVER,
TOK_EOL
} TOKEN_VALUE;
/* The tokenising return type */
typedef struct {
TOKEN_VALUE token; // The token itself
char *value; // The string version of the token
} HINT_TOKEN;
/**
* A named hint set.
*
* The hint "MaxScale name PREPARE ..." can be used to defined a named set
* of hints that can be later applied.
*/
typedef struct namedhints {
char *name; /*< Hintsets name */
HINT *hints;
struct namedhints
*next; /*< Next named hint */
} NAMEDHINTS;
/**
* A session meaintains a stack of hints, the hints BEGIN and STOP are used
* push hints on and off the stack. The current top of the stack is added to
* any statement that does not explicitly define a hint for that signle
* statement.
*/
typedef struct hintstack {
HINT *hint;
struct hintstack
*next;
} HINTSTACK;
/**
* The hint instance structure
*/
typedef struct {
int sessions;
} HINT_INSTANCE;
/**
* A hint parser session structure
*/
typedef struct {
DOWNSTREAM down;
GWBUF *request;
int query_len;
HINTSTACK *stack;
NAMEDHINTS *named_hints; /* The named hints defined in this session */
} HINT_SESSION;
/* Some useful macros */
#define CURRENT_HINT(session) ((session)->stack ? \
(session)->stack->hints : NULL)
/* Hint Parser State Machine */
#define HS_INIT 0
#define HS_ROUTE 1
#define HS_ROUTE1 2
#define HS_ROUTE_SERVER 3
#define HS_NAME 4
#define HS_PVALUE 5
#define HS_PREPARE 6
extern HINT *hint_parser(HINT_SESSION *session, GWBUF *request);
NAMEDHINTS* free_named_hint(NAMEDHINTS* named_hint);
HINTSTACK* free_hint_stack(HINTSTACK* hint_stack);
#endif

View File

@ -67,11 +67,26 @@ typedef enum backend_type_t {
BE_UNDEFINED=-1,
BE_MASTER,
BE_JOINED = BE_MASTER,
BE_SLAVE,
BE_SLAVE,
BE_COUNT
} backend_type_t;
struct router_instance;
typedef enum {
TARGET_MASTER = 0x01,
TARGET_SLAVE = 0x02,
TARGET_NAMED_SERVER = 0x04,
TARGET_ALL = 0x08,
TARGET_RLAG_MAX = 0x10
} route_target_t;
#define TARGET_IS_MASTER(t) (t & TARGET_MASTER)
#define TARGET_IS_SLAVE(t) (t & TARGET_SLAVE)
#define TARGET_IS_NAMED_SERVER(t) (t & TARGET_NAMED_SERVER)
#define TARGET_IS_ALL(t) (t & TARGET_ALL)
#define TARGET_IS_RLAG_MAX(t) (t & TARGET_RLAG_MAX)
typedef struct rses_property_st rses_property_t;
typedef struct router_client_session ROUTER_CLIENT_SES;
@ -79,7 +94,8 @@ typedef enum rses_property_type_t {
RSES_PROP_TYPE_UNDEFINED=-1,
RSES_PROP_TYPE_SESCMD=0,
RSES_PROP_TYPE_FIRST = RSES_PROP_TYPE_SESCMD,
RSES_PROP_TYPE_LAST=RSES_PROP_TYPE_SESCMD,
RSES_PROP_TYPE_TMPTABLES,
RSES_PROP_TYPE_LAST=RSES_PROP_TYPE_TMPTABLES,
RSES_PROP_TYPE_COUNT=RSES_PROP_TYPE_LAST+1
} rses_property_type_t;
@ -104,6 +120,7 @@ typedef enum select_criteria {
/** default values for rwsplit configuration parameters */
#define CONFIG_MAX_SLAVE_CONN 1
#define CONFIG_MAX_SLAVE_RLAG -1 /*< not used */
#define CONFIG_SQL_VARIABLES_IN TYPE_ALL
#define GET_SELECT_CRITERIA(s) \
(strncmp(s,"LEAST_GLOBAL_CONNECTIONS", strlen("LEAST_GLOBAL_CONNECTIONS")) == 0 ? \
@ -144,7 +161,7 @@ struct rses_property_st {
rses_property_type_t rses_prop_type;
union rses_prop_data {
mysql_sescmd_t sescmd;
void* placeholder; /*< to be removed due new type */
HASHTABLE* temp_tables;
} rses_prop_data;
rses_property_t* rses_prop_next; /*< next property of same type */
#if defined(SS_DEBUG)
@ -218,6 +235,7 @@ typedef struct rwsplit_config_st {
int rw_max_slave_conn_count;
select_criteria_t rw_slave_select_criteria;
int rw_max_slave_replication_lag;
target_t rw_use_sql_variables_in;
} rwsplit_config_t;

View File

@ -17,6 +17,7 @@
# Revision History
# Date Who Description
# 08/07/13 Mark Riddoch Initial implementation
# 28/07/14 Massimiliano Pinto new monitor ndbcluster added
include ../../../build_gateway.inc
include ../../../makefile.inc
@ -38,11 +39,13 @@ MYSQLSRCS=mysql_mon.c
MYSQLOBJ=$(MYSQLSRCS:.c=.o)
GALERASRCS=galera_mon.c
GALERAOBJ=$(GALERASRCS:.c=.o)
SRCS=$(MYSQLSRCS)
NDBCLUSTERSRCS=ndbcluster_mon.c
NDBCLUSTEROBJ=$(NDBCLUSTERSRCS:.c=.o)
SRCS=$(MYSQLSRCS) $(GALERASRCS) $(NDBCLUSTERSRCS)
OBJ=$(SRCS:.c=.o)
LIBS=$(UTILSPATH)/skygw_utils.o -llog_manager \
-L$(EMBEDDED_LIB) -lmysqld
MODULES=libmysqlmon.so libgaleramon.so
MODULES=libmysqlmon.so libgaleramon.so libndbclustermon.so
all: $(MODULES)
@ -53,6 +56,9 @@ libmysqlmon.so: $(MYSQLOBJ)
libgaleramon.so: $(GALERAOBJ)
$(CC) $(LDFLAGS) $(GALERAOBJ) $(LIBS) -o $@
libndbclustermon.so: $(NDBCLUSTEROBJ)
$(CC) $(LDFLAGS) $(NDBCLUSTEROBJ) $(LIBS) -o $@
.c.o:
$(CC) $(CFLAGS) $< -o $@

View File

@ -69,7 +69,7 @@ static void defaultUsers(void *, char *, char *);
static void diagnostics(DCB *, void *);
static void setInterval(void *, unsigned long);
static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, registerServer, unregisterServer, defaultUsers, diagnostics, setInterval, NULL, NULL };
static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, registerServer, unregisterServer, defaultUsers, diagnostics, setInterval, NULL, NULL, NULL };
/**
* Implementation of the mandatory version entry point

View File

@ -40,6 +40,9 @@
* the status to update in server status field before
* starting the replication consistency check.
* This will also give routers a consistent "status" of all servers
* 28/08/14 Massimiliano Pinto Added detectStaleMaster feature: previous detected master will be used again, even if the replication is stopped.
* This means both IO and SQL threads are not working on slaves.
* This option is not enabled by default.
*
* @endverbatim
*/
@ -62,7 +65,7 @@ extern int lm_enabled_logfiles_bitmask;
static void monitorMain(void *);
static char *version_str = "V1.2.0";
static char *version_str = "V1.3.0";
MODULE_INFO info = {
MODULE_API_MONITOR,
@ -80,6 +83,7 @@ static void diagnostics(DCB *, void *);
static void setInterval(void *, unsigned long);
static void defaultId(void *, unsigned long);
static void replicationHeartbeat(void *, int);
static void detectStaleMaster(void *, int);
static bool mon_status_changed(MONITOR_SERVERS* mon_srv);
static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv);
static MONITOR_SERVERS *getServerByNodeId(MONITOR_SERVERS *, long);
@ -91,7 +95,7 @@ static int add_slave_to_master(long *, int, long);
static void monitor_set_pending_status(MONITOR_SERVERS *, int);
static void monitor_clear_pending_status(MONITOR_SERVERS *, int);
static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, registerServer, unregisterServer, defaultUser, diagnostics, setInterval, defaultId, replicationHeartbeat };
static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, registerServer, unregisterServer, defaultUser, diagnostics, setInterval, defaultId, replicationHeartbeat, detectStaleMaster };
/**
* Implementation of the mandatory version entry point
@ -160,6 +164,7 @@ MYSQL_MONITOR *handle;
handle->id = MONITOR_DEFAULT_ID;
handle->interval = MONITOR_INTERVAL;
handle->replicationHeartbeat = 0;
handle->detectStaleMaster = 0;
handle->master = NULL;
spinlock_init(&handle->lock);
}
@ -306,6 +311,7 @@ char *sep;
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval);
dcb_printf(dcb,"\tMaxScale MonitorId:\t%lu\n", handle->id);
dcb_printf(dcb,"\tReplication lag:\t%s\n", (handle->replicationHeartbeat == 1) ? "enabled" : "disabled");
dcb_printf(dcb,"\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled");
dcb_printf(dcb, "\tMonitored servers: ");
db = handle->databases;
@ -403,6 +409,12 @@ char *server_string;
monitor_clear_pending_status(database, SERVER_SLAVE);
monitor_clear_pending_status(database, SERVER_MASTER);
/* Clean addition status too */
server_clear_status(database->server, SERVER_SLAVE_OF_EXTERNAL_MASTER);
server_clear_status(database->server, SERVER_STALE_STATUS);
monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER);
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
return;
}
free(dpwd);
@ -458,12 +470,20 @@ char *server_string;
if (strncmp(row[12], "Yes", 3) == 0
&& strncmp(row[13], "Yes", 3) == 0) {
isslave += 1;
}
/* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building
* the replication tree, slaves ids will be added to master(s) and we will have at least the
* root master server.
* Please note, there could be no slaves at all if Slave_SQL_Running == 'No'
*/
if (strncmp(row[12], "Yes", 3) == 0) {
/* get Master_Server_Id values */
master_id = atol(row[41]);
if (master_id == 0)
master_id = -1;
}
i++;
}
/* store master_id of current node */
@ -489,7 +509,14 @@ char *server_string;
if (strncmp(row[10], "Yes", 3) == 0
&& strncmp(row[11], "Yes", 3) == 0) {
isslave = 1;
}
/* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building
* the replication tree, slaves ids will be added to master(s) and we will have at least the
* root master server.
* Please note, there could be no slaves at all if Slave_SQL_Running == 'No'
*/
if (strncmp(row[10], "Yes", 3) == 0) {
/* get Master_Server_Id values */
master_id = atol(row[39]);
if (master_id == 0)
@ -505,6 +532,7 @@ char *server_string;
/* Remove addition info */
monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER);
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
/* Please note, the MASTER status and SERVER_SLAVE_OF_EXTERNAL_MASTER
* will be assigned in the monitorMain() via get_replication_tree() routine
@ -534,6 +562,7 @@ monitorMain(void *arg)
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
MONITOR_SERVERS *ptr;
int replication_heartbeat = handle->replicationHeartbeat;
int detect_stale_master = handle->detectStaleMaster;
int num_servers=0;
MONITOR_SERVERS *root_master;
@ -545,6 +574,7 @@ MONITOR_SERVERS *root_master;
"module. Exiting.\n")));
return;
}
handle->status = MONITOR_RUNNING;
while (1)
{
@ -616,10 +646,19 @@ MONITOR_SERVERS *root_master;
while (ptr)
{
if (! SERVER_IN_MAINT(ptr->server)) {
ptr->server->status = ptr->pending_status;
/* If "detect_stale_master" option is On, let's use the previus master */
if (detect_stale_master && root_master && (!strcmp(ptr->server->name, root_master->server->name) && ptr->server->port == root_master->server->port) && (ptr->server->status & SERVER_MASTER) && !(ptr->pending_status & SERVER_MASTER)) {
/* in this case server->status will not be updated from pending_status */
LOGIF(LM, (skygw_log_write_flush(
LOGFILE_MESSAGE, "[mysql_mon]: root server [%s:%i] is no longer Master, let's use it again even if it could be a stale master, you have been warned!", ptr->server->name, ptr->server->port)));
/* Set the STALE bit for this server in server struct */
server_set_status(ptr->server, SERVER_STALE_STATUS);
} else {
ptr->server->status = ptr->pending_status;
}
}
ptr = ptr->next;
}
ptr = ptr->next;
}
/* Do now the heartbeat replication set/get for MySQL Replication Consistency */
if (replication_heartbeat && root_master && (SERVER_IS_MASTER(root_master->server) || SERVER_IS_RELAY_SERVER(root_master->server))) {
@ -665,19 +704,34 @@ setInterval(void *arg, unsigned long interval)
{
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
memcpy(&handle->interval, &interval, sizeof(unsigned long));
}
}
/**
* Enable/Disable the MySQL Replication hearbeat, detecting slave lag behind master.
*
* @param arg The handle allocated by startMonitor
* @param replicationHeartbeat To enable it 1, disable it with 0
* @param arg The handle allocated by startMonitor
* @param enable To enable it 1, disable it with 0
*/
static void
replicationHeartbeat(void *arg, int replicationHeartbeat)
replicationHeartbeat(void *arg, int enable)
{
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
memcpy(&handle->replicationHeartbeat, &replicationHeartbeat, sizeof(int));
memcpy(&handle->replicationHeartbeat, &enable, sizeof(int));
}
/**
* Enable/Disable the MySQL Replication Stale Master dectection, allowing a previouvsly detected master to still act as a Master.
* This option must be enabled in order to keep the Master when the replication is stopped or removed from slaves.
* If the replication is still stopped when MaxSclale is restarted no Master will be available.
*
* @param arg The handle allocated by startMonitor
* @param enable To enable it 1, disable it with 0
*/
static void
detectStaleMaster(void *arg, int enable)
{
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
memcpy(&handle->detectStaleMaster, &enable, sizeof(int));
}
static bool mon_status_changed(
@ -1038,6 +1092,10 @@ static MONITOR_SERVERS *get_replication_tree(MYSQL_MONITOR *handle, int num_serv
monitor_set_pending_status(master, SERVER_MASTER);
} else {
if (current->master_id > 0) {
/* this server is slave of another server not in MaxScale configuration
* we cannot use it as a real slave.
*/
monitor_clear_pending_status(ptr, SERVER_SLAVE);
monitor_set_pending_status(ptr, SERVER_SLAVE_OF_EXTERNAL_MASTER);
}
}

View File

@ -32,6 +32,7 @@
* 26/05/14 Massimiliano Pinto Default values for MONITOR_INTERVAL
* 28/05/14 Massimiliano Pinto Addition of new fields in MYSQL_MONITOR struct
* 24/06/14 Massimiliano Pinto Addition of master field in MYSQL_MONITOR struct and MONITOR_MAX_NUM_SLAVES
* 28/08/14 Massimiliano Pinto Addition of detectStaleMaster
*
* @endverbatim
*/
@ -43,9 +44,9 @@
typedef struct monitor_servers {
SERVER *server; /**< The server being monitored */
MYSQL *con; /**< The MySQL connection */
int mon_err_count;
unsigned int mon_prev_status;
unsigned int pending_status; /**< Pending Status flag bitmap */
int mon_err_count;
unsigned int mon_prev_status;
unsigned int pending_status; /**< Pending Status flag bitmap */
struct monitor_servers
*next; /**< The next server in the list */
} MONITOR_SERVERS;
@ -54,17 +55,18 @@ typedef struct monitor_servers {
* The handle for an instance of a MySQL Monitor module
*/
typedef struct {
SPINLOCK lock; /**< The monitor spinlock */
pthread_t tid; /**< id of monitor thread */
int shutdown; /**< Flag to shutdown the monitor thread */
int status; /**< Monitor status */
char *defaultUser; /**< Default username for monitoring */
char *defaultPasswd; /**< Default password for monitoring */
unsigned long interval; /**< Monitor sampling interval */
unsigned long id; /**< Monitor ID */
SPINLOCK lock; /**< The monitor spinlock */
pthread_t tid; /**< id of monitor thread */
int shutdown; /**< Flag to shutdown the monitor thread */
int status; /**< Monitor status */
char *defaultUser; /**< Default username for monitoring */
char *defaultPasswd; /**< Default password for monitoring */
unsigned long interval; /**< Monitor sampling interval */
unsigned long id; /**< Monitor ID */
int replicationHeartbeat; /**< Monitor flag for MySQL replication heartbeat */
MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */
MONITOR_SERVERS *databases; /**< Linked list of servers to monitor */
int detectStaleMaster; /**< Monitor flag for MySQL replication Stale Master detection */
MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */
MONITOR_SERVERS *databases; /**< Linked list of servers to monitor */
} MYSQL_MONITOR;
#define MONITOR_RUNNING 1

View File

@ -0,0 +1,461 @@
/*
* This file is distributed as part of the SkySQL Gateway. 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 SkySQL Ab 2013
*/
/**
* @file ndbcluster_mon.c - A MySQL cluster SQL node monitor
*
* @verbatim
* Revision History
*
* Date Who Description
* 25/07/14 Massimiliano Pinto Initial implementation
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <monitor.h>
#include <mysqlmon.h>
#include <thread.h>
#include <mysql.h>
#include <mysqld_error.h>
#include <skygw_utils.h>
#include <log_manager.h>
#include <secrets.h>
#include <dcb.h>
#include <modinfo.h>
extern int lm_enabled_logfiles_bitmask;
static void monitorMain(void *);
static char *version_str = "V1.0.0";
MODULE_INFO info = {
MODULE_API_MONITOR,
MODULE_BETA_RELEASE,
MONITOR_VERSION,
"A MySQL cluster SQL node monitor"
};
static void *startMonitor(void *);
static void stopMonitor(void *);
static void registerServer(void *, SERVER *);
static void unregisterServer(void *, SERVER *);
static void defaultUsers(void *, char *, char *);
static void diagnostics(DCB *, void *);
static void setInterval(void *, unsigned long);
static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, registerServer, unregisterServer, defaultUsers, diagnostics, setInterval, NULL, NULL, NULL };
/**
* Implementation of the mandatory version entry point
*
* @return version string of the module
*/
char *
version()
{
return version_str;
}
/**
* The module initialisation routine, called when the module
* is first loaded.
*/
void
ModuleInit()
{
LOGIF(LM, (skygw_log_write(
LOGFILE_MESSAGE,
"Initialise the MySQL Cluster Monitor module %s.\n",
version_str)));
}
/**
* The module entry point routine. It is this routine that
* must populate the structure that is referred to as the
* "module object", this is a structure with the set of
* external entry points for this module.
*
* @return The module object
*/
MONITOR_OBJECT *
GetModuleObject()
{
return &MyObject;
}
/**
* Start the instance of the monitor, returning a handle on the monitor.
*
* This function creates a thread to execute the actual monitoring.
*
* @return A handle to use when interacting with the monitor
*/
static void *
startMonitor(void *arg)
{
MYSQL_MONITOR *handle;
if (arg != NULL)
{
handle = (MYSQL_MONITOR *)arg;
handle->shutdown = 0;
}
else
{
if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL)
return NULL;
handle->databases = NULL;
handle->shutdown = 0;
handle->defaultUser = NULL;
handle->defaultPasswd = NULL;
handle->id = MONITOR_DEFAULT_ID;
handle->interval = MONITOR_INTERVAL;
spinlock_init(&handle->lock);
}
handle->tid = (THREAD)thread_start(monitorMain, handle);
return handle;
}
/**
* Stop a running monitor
*
* @param arg Handle on thr running monior
*/
static void
stopMonitor(void *arg)
{
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
handle->shutdown = 1;
thread_wait((void *)handle->tid);
}
/**
* Register a server that must be added to the monitored servers for
* a monitoring module.
*
* @param arg A handle on the running monitor module
* @param server The server to add
*/
static void
registerServer(void *arg, SERVER *server)
{
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
MONITOR_SERVERS *ptr, *db;
if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL)
return;
db->server = server;
db->con = NULL;
db->next = NULL;
spinlock_acquire(&handle->lock);
if (handle->databases == NULL)
handle->databases = db;
else
{
ptr = handle->databases;
while (ptr->next != NULL)
ptr = ptr->next;
ptr->next = db;
}
spinlock_release(&handle->lock);
}
/**
* Remove a server from those being monitored by a monitoring module
*
* @param arg A handle on the running monitor module
* @param server The server to remove
*/
static void
unregisterServer(void *arg, SERVER *server)
{
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
MONITOR_SERVERS *ptr, *lptr;
spinlock_acquire(&handle->lock);
if (handle->databases == NULL)
{
spinlock_release(&handle->lock);
return;
}
if (handle->databases->server == server)
{
ptr = handle->databases;
handle->databases = handle->databases->next;
free(ptr);
}
else
{
ptr = handle->databases;
while (ptr->next != NULL && ptr->next->server != server)
ptr = ptr->next;
if (ptr->next)
{
lptr = ptr->next;
ptr->next = ptr->next->next;
free(lptr);
}
}
spinlock_release(&handle->lock);
}
/**
* Diagnostic interface
*
* @param dcb DCB to send output
* @param arg The monitor handle
*/
static void
diagnostics(DCB *dcb, void *arg)
{
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
MONITOR_SERVERS *db;
char *sep;
switch (handle->status)
{
case MONITOR_RUNNING:
dcb_printf(dcb, "\tMonitor running\n");
break;
case MONITOR_STOPPING:
dcb_printf(dcb, "\tMonitor stopping\n");
break;
case MONITOR_STOPPED:
dcb_printf(dcb, "\tMonitor stopped\n");
break;
}
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval);
dcb_printf(dcb, "\tMonitored servers: ");
db = handle->databases;
sep = "";
while (db)
{
dcb_printf(dcb, "%s%s:%d", sep, db->server->name, db->server->port);
sep = ", ";
db = db->next;
}
dcb_printf(dcb, "\n");
}
/**
* Set the default username and password to use to monitor if the server does not
* override this.
*
* @param arg The handle allocated by startMonitor
* @param uname The default user name
* @param passwd The default password
*/
static void
defaultUsers(void *arg, char *uname, char *passwd)
{
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
if (handle->defaultUser)
free(handle->defaultUser);
if (handle->defaultPasswd)
free(handle->defaultPasswd);
handle->defaultUser = strdup(uname);
handle->defaultPasswd = strdup(passwd);
}
/**
* Monitor an individual server
*
* @param database The database to probe
*/
static void
monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPasswd)
{
MYSQL_ROW row;
MYSQL_RES *result;
int num_fields;
int isjoined = 0;
char *uname = defaultUser, *passwd = defaultPasswd;
unsigned long int server_version = 0;
char *server_string;
if (database->server->monuser != NULL)
{
uname = database->server->monuser;
passwd = database->server->monpw;
}
if (uname == NULL)
return;
/* Don't even probe server flagged as in maintenance */
if (SERVER_IN_MAINT(database->server))
return;
if (database->con == NULL || mysql_ping(database->con) != 0)
{
char *dpwd = decryptPassword(passwd);
int rc;
int read_timeout = 1;
database->con = mysql_init(NULL);
rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
if (mysql_real_connect(database->con, database->server->name,
uname, dpwd, NULL, database->server->port, NULL, 0) == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Monitor was unable to connect to "
"server %s:%d : \"%s\"",
database->server->name,
database->server->port,
mysql_error(database->con))));
server_clear_status(database->server, SERVER_RUNNING);
database->server->node_id = -1;
free(dpwd);
return;
}
free(dpwd);
}
/* If we get this far then we have a working connection */
server_set_status(database->server, SERVER_RUNNING);
/* get server version from current server */
server_version = mysql_get_server_version(database->con);
/* get server version string */
server_string = (char *)mysql_get_server_info(database->con);
if (server_string) {
database->server->server_string = strdup(server_string);
}
/* Check if the the SQL node is able to contact one or more data nodes */
if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result)))
{
if (atoi(row[1]) > 0)
isjoined = 1;
}
mysql_free_result(result);
}
/* Check the the SQL node id in the MySQL cluster */
if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_cluster_node_id'") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
long cluster_node_id = -1;
num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result)))
{
cluster_node_id = strtol(row[1], NULL, 10);
if ((errno == ERANGE && (cluster_node_id == LONG_MAX
|| cluster_node_id == LONG_MIN)) || (errno != 0 && cluster_node_id == 0))
{
cluster_node_id = -1;
}
database->server->node_id = cluster_node_id;
}
mysql_free_result(result);
}
if (isjoined) {
server_set_status(database->server, SERVER_NDB);
database->server->depth = 0;
} else {
server_clear_status(database->server, SERVER_NDB);
database->server->depth = -1;
}
}
/**
* The entry point for the monitoring module thread
*
* @param arg The handle of the monitor
*/
static void
monitorMain(void *arg)
{
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
MONITOR_SERVERS *ptr;
long master_id;
if (mysql_thread_init())
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Fatal : mysql_thread_init failed in monitor "
"module. Exiting.\n")));
return;
}
handle->status = MONITOR_RUNNING;
while (1)
{
master_id = -1;
if (handle->shutdown)
{
handle->status = MONITOR_STOPPING;
mysql_thread_end();
handle->status = MONITOR_STOPPED;
return;
}
ptr = handle->databases;
while (ptr)
{
unsigned int prev_status = ptr->server->status;
monitorDatabase(ptr, handle->defaultUser, handle->defaultPasswd);
if (ptr->server->status != prev_status ||
SERVER_IS_DOWN(ptr->server))
{
LOGIF(LM, (skygw_log_write_flush(
LOGFILE_MESSAGE,
"Backend server %s:%d state : %s",
ptr->server->name,
ptr->server->port,
STRSRVSTATUS(ptr->server))));
}
ptr = ptr->next;
}
thread_millisleep(handle->interval);
}
}
/**
* Set the monitor sampling interval.
*
* @param arg The handle allocated by startMonitor
* @param interval The interval to set in monitor struct, in milliseconds
*/
static void
setInterval(void *arg, unsigned long interval)
{
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
memcpy(&handle->interval, &interval, sizeof(unsigned long));
}

View File

@ -422,7 +422,6 @@ static int gw_read_backend_event(DCB *dcb) {
GWBUF *read_buffer = NULL;
ROUTER_OBJECT *router = NULL;
ROUTER *router_instance = NULL;
void *rsession = NULL;
SESSION *session = dcb->session;
int nbytes_read = 0;

View File

@ -34,6 +34,7 @@
* 28/02/2014 Massimiliano Pinto Added: client IPv4 in dcb->ipv4 and inet_ntop for string representation
* 11/03/2014 Massimiliano Pinto Added: Unix socket support
* 07/05/2014 Massimiliano Pinto Added: specific version string in server handshake
* 09/09/2014 Massimiliano Pinto Added: 777 permission for socket path
*
*/
#include <skygw_utils.h>
@ -985,6 +986,16 @@ int gw_MySQLListener(
return 0;
}
/* set permission for all users */
if (chmod(config_bind, 0777) < 0) {
fprintf(stderr,
"\n* chmod failed for %s due error %i, %s.\n\n",
config_bind,
errno,
strerror(errno));
}
break;
case AF_INET:
@ -1420,7 +1431,6 @@ static int route_by_statement(
int rc = -1;
GWBUF* packetbuf;
#if defined(SS_DEBUG)
gwbuf_type_t prevtype;
GWBUF* tmpbuf;
tmpbuf = *p_readbuf;

View File

@ -94,19 +94,15 @@ install: $(MODULES)
(cd binlog; make DEST=$(DEST) install)
cleantests:
$(MAKE) -C readwritesplit/test cleantests
$(MAKE) -C test cleantests
buildtests:
$(MAKE) -C readwritesplit/test DEBUG=Y buildtests
$(MAKE) -C test DEBUG=Y buildtests
runtests:
$(MAKE) -C test runtests
$(MAKE) -C readwritesplit runtests
testall:
$(MAKE) -C test testall
$(MAKE) -C readwritesplit testall
include depend.mk

View File

@ -839,6 +839,7 @@ static struct {
{ "master", SERVER_MASTER },
{ "slave", SERVER_SLAVE },
{ "synced", SERVER_JOINED },
{ "ndb", SERVER_NDB },
{ "maintenance", SERVER_MAINT },
{ "maint", SERVER_MAINT },
{ NULL, 0 }

View File

@ -311,6 +311,11 @@ char *weightby;
inst->bitmask |= (SERVER_JOINED);
inst->bitvalue |= SERVER_JOINED;
}
else if (!strcasecmp(options[i], "ndb"))
{
inst->bitmask |= (SERVER_NDB);
inst->bitvalue |= SERVER_NDB;
}
else
{
LOGIF(LM, (skygw_log_write(
@ -318,7 +323,7 @@ char *weightby;
"* Warning : Unsupported router "
"option \'%s\' for readconnroute. "
"Expected router options are "
"[slave|master|synced]",
"[slave|master|synced|ndb]",
options[i])));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,7 @@ testall:
-$(MAKE) cleantests
-$(MAKE) DEBUG=Y buildtests
-$(MAKE) runtests
-$(MAKE) -C test_hints testall
buildtests:
@ -32,4 +33,4 @@ runtests:
@echo "-------------------------------" >> $(TESTLOG)
./rwsplit.sh $(TESTLOG) $(THOST) $(TPORT_RW) $(TMASTER_ID) $(TUSER) $(TPWD)
@echo "" >> $(TESTLOG)
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)

View File

@ -230,6 +230,16 @@ if [ "$a" != "$TRETVAL" ]; then
else
echo "$TINPUT PASSED">>$TLOG ;
fi
TINPUT=test_temporary_table.sql
a=`$RUNCMD < ./$TINPUT`
TRETVAL=1
if [ "$a" != "$TRETVAL" ]; then
echo "$TINPUT FAILED, return value $a when $TRETVAL was expected">>$TLOG;
else
echo "$TINPUT PASSED">>$TLOG ;
fi
echo "-----------------------------------" >> $TLOG
echo "Session variables: Stress Test 1" >> $TLOG
echo "-----------------------------------" >> $TLOG
@ -238,6 +248,10 @@ RUNCMD=mysql\ --host=$THOST\ -P$TPORT\ -u$TUSER\ -p$TPWD\ --unbuffered=true\ --d
TINPUT=test_sescmd2.sql
for ((i = 0;i<1000;i++))
do
if [[ $(( i % 50 )) -eq 0 ]]
then
printf "."
fi
a=`$RUNCMD < $TINPUT 2>&1`
if [[ "`echo "$a"|grep -i 'error'`" != "" ]]
then
@ -255,15 +269,19 @@ fi
echo "-----------------------------------" >> $TLOG
echo "Session variables: Stress Test 2" >> $TLOG
echo "-----------------------------------" >> $TLOG
echo ""
err=""
TINPUT=test_sescmd3.sql
for ((j = 0;j<1000;j++))
do
b=`$RUNCMD < $TINPUT 2>&1`
if [[ "`echo "$b"|grep -i 'null'`" != "" ]]
if [[ $(( j % 50 )) -eq 0 ]]
then
err=`echo "$b" | grep -i null`
printf "."
fi
b=`$RUNCMD < $TINPUT 2>&1`
if [[ "`echo "$b"|grep -i 'null\|error'`" != "" ]]
then
err=`echo "$b" | grep -i null\|error`
break
fi
done

View File

@ -0,0 +1,53 @@
# cleantests - clean local and subdirectories' tests
# buildtests - build all local and subdirectories' tests
# runtests - run all local tests
# testall - clean, build and run local and subdirectories' tests
include ../../../../../../build_gateway.inc
include $(ROOT_PATH)/makefile.inc
include $(ROOT_PATH)/test.inc
ARGS=6
CC=cc
TESTLOG := $(shell pwd)/testrwsplit_hints.log
RET := -1
cleantests:
- $(DEL) *.o
- $(DEL) *~
- $(DEL) *.sql
- $(DEL) *.output
- $(DEL) *.log
testall:
-$(MAKE) cleantests
-$(MAKE) DEBUG=Y buildtests
-$(MAKE) runtests
buildtests:
runtests:
@echo "" >> $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG)
@echo $(shell date) >> $(TESTLOG)
@echo "Test Read/Write split router - hint routing" >> $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG)
@echo "Running simple tests" >> $(TESTLOG)
@echo "" >> $(TESTLOG)
./rwsplit_hints.sh $(TESTLOG) $(THOST) $(TPORT_RW_HINT) $(TMASTER_ID) $(TUSER) $(TPWD) simple_tests
@echo "" >> $(TESTLOG)
@echo "Running syntax error tests" >> $(TESTLOG)
@echo "" >> $(TESTLOG)
./syntax_check.sh $(TESTLOG) $(THOST) $(TPORT_RW_HINT) $(TMASTER_ID) $(TUSER) $(TPWD) error_tests
@echo "" >> $(TESTLOG)
@echo "Running complex tests" >> $(TESTLOG)
@echo "" >> $(TESTLOG)
./rwsplit_hints.sh $(TESTLOG) $(THOST) $(TPORT_RW_HINT) $(TMASTER_ID) $(TUSER) $(TPWD) complex_tests
@echo "" >> $(TESTLOG)
@echo "Running stack tests" >> $(TESTLOG)
@echo "" >> $(TESTLOG)
./rwsplit_hints.sh $(TESTLOG) $(THOST) $(TPORT_RW_HINT) $(TMASTER_ID) $(TUSER) $(TPWD) stack_tests
@echo "" >> $(TESTLOG)
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)

View File

@ -0,0 +1,48 @@
select @@server_id; -- maxscale begin route to master:3000
select @@server_id;:3000
select @@server_id; -- maxscale route to server server3:3002
select @@server_id;:3000
select @@server_id; -- maxscale end:
select @@server_id; -- maxscale named1 prepare route to master:
select @@server_id; -- maxscale named1 begin:3000
select @@server_id;:3000
select @@server_id; -- maxscale route to server server3:3002
select @@server_id;:3000
select @@server_id; -- maxscale end:
select @@server_id; -- maxscale shorthand1 begin route to server server2:3001
select @@server_id;:3001
select @@server_id; -- maxscale route to server server3:3002
select @@server_id;:3001
select @@server_id; -- maxscale end:
select @@server_id; # maxscale begin route to master:3000
select @@server_id;:3000
select @@server_id; # maxscale route to server server3:3002
select @@server_id;:3000
select @@server_id; # maxscale end:
select @@server_id; # maxscale named2 prepare route to master:
select @@server_id; # maxscale named2 begin:3000
select @@server_id;:3000
select @@server_id; # maxscale route to server server3:3002
select @@server_id;:3000
select @@server_id; # maxscale end:
select @@server_id; # maxscale shorthand2 begin route to server server2:3001
select @@server_id;:3001
select @@server_id; # maxscale route to server server3:3002
select @@server_id;:3001
select @@server_id; # maxscale end:
select @@server_id/* maxscale begin route to master */;:3000
select @@server_id;:3000
select @@server_id/* maxscale route to server server3 */;:3002
select @@server_id;:3000
select @@server_id/* maxscale end */;:
select @@server_id/* maxscale named3 prepare route to master */;:
select @@server_id/* maxscale named3 begin */;:3000
select @@server_id;:3000
select @@server_id/* maxscale route to server server3 */;:3002
select @@server_id;:3000
select @@server_id/* maxscale end */;:
select @@server_id/* maxscale shorthand3 begin route to server server2 */; :3001
select @@server_id;:3001
select @@server_id/* maxscale route to server server3 */;:3002
select @@server_id;:3001
select @@server_id/* maxscale end */;:

View File

@ -0,0 +1,39 @@
select @@server_id; -- maxscalemaxscale route to master:
select @@server_id; -- master to route maxscale:
select @@server_id; -- route to master:
select @@server_id; -- maxscale to master:
select @@server_id; -- maxscale route master:
select @@server_id; -- maxscale route to:
select @@server_id; -- maxscale begin master:
select @@server_id; -- maxscale master route to master:
select @@server_id; -- maxscale route to maxscale route to master:
select @@server_id; -- maxscale maxscale route to master:
select @@server_id; -- maxscale route to to server =):
select @@server_id; -- maxscale route to maxscale server server1:
select @@server_id; -- maxscale route to server1:
select @@server_id; # maxscalemaxscale route to master:
select @@server_id; # master to route maxscale:
select @@server_id; # route to master:
select @@server_id; # maxscale to master:
select @@server_id; # maxscale route master:
select @@server_id; # maxscale route to:
select @@server_id; # maxscale begin master:
select @@server_id; # maxscale master route to master:
select @@server_id; # maxscale route to maxscale route to master:
select @@server_id; # maxscale maxscale route to master:
select @@server_id; # maxscale route to to server =):
select @@server_id; # maxscale route to maxscale server server1:
select @@server_id; # maxscale route to server1:
select @@server_id; /* maxscalemaxscale route to master */;:
select @@server_id; /* master to route maxscale */;:
select @@server_id; /* route to master */;:
select @@server_id; /* maxscale to master */;:
select @@server_id; /* maxscale route master */;:
select @@server_id; /* maxscale route to */;:
select @@server_id; /* maxscale begin master */;:
select @@server_id; /* maxscale master route to master */;:
select @@server_id; /* maxscale route to maxscale route to master */;:
select @@server_id; /* maxscale maxscale route to master */;:
select @@server_id; /* maxscale route to to server =) */;:
select @@server_id; /* maxscale route to maxscale server server1 */;:
select @@server_id; /* maxscale route to server1 */;:

View File

@ -0,0 +1,9 @@
-- maxscale route to master:3000
-- maxscale route to server server1:3000
-- maxscale route to server server2:3001
-- maxscale route to server server3:3002
-- maxscale route to server server4:3003
-- maxscale max_slave_replication_lag = 100:
-- maxscale max_slave_replication_lag= 100:
-- maxscale max_slave_replication_lag =100:
-- maxscale max_slave_replication_lag=100:

View File

@ -0,0 +1,65 @@
#!/bin/bash
NARGS=7
TLOG=$1
THOST=$2
TPORT=$3
TMASTER_ID=$4
TUSER=$5
TPWD=$6
TESTINPUT=$7
if [ $# != $NARGS ] ;
then
echo""
echo "Wrong number of arguments, gave "$#" but "$NARGS" is required"
echo ""
echo "Usage :"
echo " rwsplit_hints.sh <log filename> <host> <port> <master id> <user> <password> <test file>"
echo ""
exit 1
fi
RUNCMD=mysql\ --host=$THOST\ -P$TPORT\ -u$TUSER\ -p$TPWD\ --unbuffered=true\ --disable-reconnect\ --silent\ --comment
i=0
while read -r LINE
do
TINPUT[$i]=`echo "$LINE"|awk '{split($0,a,":");print a[1]}'`
TRETVAL[$i]=`echo "$LINE"|awk '{split($0,a,":");print a[2]}'`
echo "${TINPUT[i]}" >> $TESTINPUT.sql
i=$((i+1))
done < $TESTINPUT
`$RUNCMD < $TESTINPUT.sql > $TESTINPUT.output`
x=0
crash=1
all_passed=1
while read -r TOUTPUT
do
crash=0
if [ "$TOUTPUT" != "${TRETVAL[x]}" -a "${TRETVAL[x]}" != "" ]
then
all_passed=0
echo "$TESTINPUT:$((x + 1)): ${TINPUT[x]} FAILED, return value $TOUTPUT when ${TRETVAL[x]} was expected">>$TLOG;
fi
x=$((x+1))
done < $TESTINPUT.output
if [ $crash -eq 1 ]
then
all_passed=0
for ((v=0;v<$i;v++))
do
echo "${TINPUT[v]} FAILED, nothing was returned">>$TLOG;
done
fi
if [ $all_passed -eq 1 ]
then
echo "Test set: PASSED">>$TLOG;
else
echo "Test set: FAILED">>$TLOG;
fi

View File

@ -0,0 +1,18 @@
select @@server_id; -- maxscale route to master:3000
select @@server_id; -- maxscale route to slave:
select @@server_id; -- maxscale route to server server1:3000
select @@server_id; -- maxscale route to server server2:3001
select @@server_id; -- maxscale route to server server3:3002
select @@server_id; -- maxscale route to server server4:3003
select @@server_id; # maxscale route to master:3000
select @@server_id; # maxscale route to slave:
select @@server_id; # maxscale route to server server1:3000
select @@server_id; # maxscale route to server server2:3001
select @@server_id; # maxscale route to server server3:3002
select @@server_id; # maxscale route to server server4:3003
select @@server_id/* maxscale route to master */;:3000
select @@server_id/* maxscale route to slave */;:
select @@server_id/* maxscale route to server server1 */;:3000
select @@server_id/* maxscale route to server server2 */;:3001
select @@server_id/* maxscale route to server server3 */;:3002
select @@server_id/* maxscale route to server server4 */;:3003

View File

@ -0,0 +1,50 @@
select @@server_id; -- maxscale stack_named1 prepare route to server server1:
select @@server_id; -- maxscale stack_named2 prepare route to server server2:
select @@server_id; -- maxscale stack_named3 prepare route to server server3:
select @@server_id; -- maxscale stack_named4 prepare route to server server4:
select @@server_id; -- maxscale stack_named1 begin:3000
select @@server_id;:3000
select @@server_id; -- maxscale stack_named2 begin:3001
select @@server_id;:3001
select @@server_id; -- maxscale stack_named3 begin:3002
select @@server_id;:3002
select @@server_id; -- maxscale stack_named4 begin:3003
select @@server_id;:3003
select @@server_id; -- maxscale stack_shorthand1 begin route to server server1:3000
select @@server_id;:3000
select @@server_id; -- maxscale stack_shorthand2 begin route to server server2:3001
select @@server_id;:3001
select @@server_id; -- maxscale stack_shorthand3 begin route to server server3:3002
select @@server_id;:3002
select @@server_id; -- maxscale stack_shorthand4 begin route to server server4:3003
select @@server_id;:3003
select @@server_id; -- maxscale end:3002
select @@server_id;:3002
select @@server_id; -- maxscale end:3001
select @@server_id;:3001
select @@server_id; -- maxscale end:3000
select @@server_id;:3000
select @@server_id; -- maxscale end:3003
select @@server_id;:3003
select @@server_id; -- maxscale end:3002
select @@server_id;:3002
select @@server_id; -- maxscale end:3001
select @@server_id;:3001
select @@server_id; -- maxscale end:3000
select @@server_id; -- maxscale end:
select @@server_id; -- maxscale stack_shorthand1 begin:3000
select @@server_id; -- maxscale stack_shorthand2 begin:3001
select @@server_id; -- maxscale stack_shorthand3 begin:3002
select @@server_id; -- maxscale stack_shorthand4 begin:3003
select @@server_id; -- maxscale stack_named1 begin:3000
select @@server_id; -- maxscale stack_named2 begin:3001
select @@server_id; -- maxscale stack_named3 begin:3002
select @@server_id; -- maxscale stack_named4 begin:3003
select @@server_id; -- maxscale end:3002
select @@server_id; -- maxscale end:3001
select @@server_id; -- maxscale end:3000
select @@server_id; -- maxscale end:3003
select @@server_id; -- maxscale end:3002
select @@server_id; -- maxscale end:3001
select @@server_id; -- maxscale end:3000
select @@server_id; -- maxscale end:

View File

@ -0,0 +1,33 @@
#! /bin/bash
NARGS=7
TLOG=$1
THOST=$2
TPORT=$3
TMASTER_ID=$4
TUSER=$5
TPWD=$6
TESTINPUT=$7
if [ $# != $NARGS ] ;
then
echo""
echo "Wrong number of arguments, gave "$#" but "$NARGS" is required"
echo ""
echo "Usage :"
echo " syntax_check.sh <log filename> <host> <port> <master id> <user> <password> <test file>"
echo ""
exit 1
fi
./rwsplit_hints.sh dummy.log $THOST $TPORT $TMASTER_ID $TUSER $TPWD $TESTINPUT
exp_count=`cat error_tests|wc -l`
err_count=`tac ../../../../../test/log/skygw_err* | gawk '/enabled/{if(!bg){ bg = 1} else exit 0}{if(bg) print}'|grep -c 'Hint ignored'`
if [[ $err_count -ge $exp_count ]]
then
echo "Test set: PASSED">>$TLOG;
else
echo "Expected $exp_count ignored hints in the error log but found $err_count instead">>$TLOG
echo "Test set: FAILED">>$TLOG;
fi

View File

@ -0,0 +1,5 @@
use test;
drop table if exists t1;
create temporary table t1 (id integer);
insert into t1 values(1);
select id from t1;

View File

@ -0,0 +1,616 @@
/*
* This file is distributed as part of 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 SkySQL Ab 2014
*/
#include <stdio.h>
#include <router.h>
#include <modinfo.h>
#include <server.h>
#include <service.h>
#include <session.h>
#include <monitor.h>
#include <string.h>
/**
* The instance structure for this router.
*/
typedef struct {
SERVICE *service;
} WEB_INSTANCE;
/**
* The session structure for this router.
*/
typedef struct {
SESSION *session;
} WEB_SESSION;
static char *version_str = "V1.0.0";
MODULE_INFO info = {
MODULE_API_ROUTER,
MODULE_IN_DEVELOPMENT,
ROUTER_VERSION,
"A test router - not for use in real systems"
};
static ROUTER *createInstance(SERVICE *service, char **options);
static void *newSession(ROUTER *instance, SESSION *session);
static void closeSession(ROUTER *instance, void *session);
static void freeSession(ROUTER *instance, void *session);
static int routeQuery(ROUTER *instance, void *session, GWBUF *queue);
static void diagnostic(ROUTER *instance, DCB *dcb);
static uint8_t getCapabilities (ROUTER* inst, void* router_session);
static ROUTER_OBJECT MyObject = {
createInstance,
newSession,
closeSession,
freeSession,
routeQuery,
diagnostic,
NULL,
NULL,
getCapabilities
};
static void send_index(WEB_SESSION *);
static void send_css(WEB_SESSION *);
static void send_menu(WEB_SESSION *);
static void send_blank(WEB_SESSION *);
static void send_title(WEB_SESSION *);
static void send_frame1(WEB_SESSION *);
static void send_services(WEB_SESSION *);
static void send_sessions(WEB_SESSION *);
static void send_servers(WEB_SESSION *);
static void send_monitors(WEB_SESSION *);
static void respond_error(WEB_SESSION *, int, char *);
/**
* A map of URL to function that implements the URL
*/
static struct {
char *page; /* URL */
void (*fcn)(WEB_SESSION *); /* Function to call */
} pages[] = {
{ "index.html", send_index },
{ "services.html", send_services },
{ "menu.html", send_menu },
{ "sessions.html", send_sessions },
{ "blank.html", send_blank },
{ "title.html", send_title },
{ "frame1.html", send_frame1 },
{ "servers.html", send_servers },
{ "monitors.html", send_monitors },
{ "styles.css", send_css },
{ NULL, NULL }
};
/**
* Implementation of the mandatory version entry point
*
* @return version string of the module
*/
char *
version()
{
return version_str;
}
/**
* The module initialisation routine, called when the module
* is first loaded.
*/
void
ModuleInit()
{
}
/**
* The module entry point routine. It is this routine that
* must populate the structure that is referred to as the
* "module object", this is a structure with the set of
* external entry points for this module.
*
* @return The module object
*/
ROUTER_OBJECT *
GetModuleObject()
{
return &MyObject;
}
/**
* Create an instance of the router for a particular service
* within the gateway.
*
* @param service The service this router is being create for
* @param options The options for this query router
*
* @return The instance data for this new instance
*/
static ROUTER *
createInstance(SERVICE *service, char **options)
{
WEB_INSTANCE *inst;
if ((inst = (WEB_INSTANCE *)malloc(sizeof(WEB_INSTANCE))) == NULL)
return NULL;
inst->service = service;
return (ROUTER *)inst;
}
/**
* Associate a new session with this instance of the router.
*
* @param instance The router instance data
* @param session The session itself
* @return Session specific data for this session
*/
static void *
newSession(ROUTER *instance, SESSION *session)
{
WEB_SESSION *wsession;
if ((wsession = (WEB_SESSION *)malloc(sizeof(WEB_SESSION))) == NULL)
return NULL;
wsession->session = session;
return wsession;
}
/**
* Close a session with the router, this is the mechanism
* by which a router may cleanup data structure etc.
*
* @param instance The router instance data
* @param session The session being closed
*/
static void
closeSession(ROUTER *instance, void *session)
{
free(session);
}
static void freeSession(
ROUTER* router_instance,
void* router_client_session)
{
return;
}
static int
routeQuery(ROUTER *instance, void *session, GWBUF *queue)
{
WEB_SESSION *wsession = (WEB_SESSION *)session;
char *ptr;
int i, found = 0;
char *url;
if ((url = gwbuf_get_property(queue, "URL")) == NULL)
{
respond_error(wsession, 404, "No URL available");
}
ptr = strrchr(url, '/');
if (ptr)
ptr++;
else
ptr = url;
for (i = 0; pages[i].page; i++)
{
if (!strcmp(ptr, pages[i].page))
{
(pages[i].fcn)(wsession);
found = 1;
}
}
if (!found)
respond_error(wsession, 404, "Unrecognised URL received");
gwbuf_free(queue);
return 0;
}
/**
* Diagnostics routine
*
* @param instance The router instance
* @param dcb The DCB for diagnostic output
*/
static void
diagnostic(ROUTER *instance, DCB *dcb)
{
}
/**
* Return the router capabilities bitmask
*
* @param inst The router instance
* @param router_session The router session
* @return Router capabilities bitmask
*/
static uint8_t
getCapabilities(ROUTER *inst, void *router_session)
{
return 0;
}
/**
* The HTML of the index page.
*/
static char *index_page =
"<HTML><HEAD>"
"<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">"
"<TITLE>MaxScale</TITLE>"
"</HEAD>"
"<FRAMESET ROWS=\"60,*\">"
"<FRAME SRC=\"title.html\">"
"<FRAME SRC=\"frame1.html\">"
"</FRAMESET>"
"</HTML>";
/**
* The HTML of the title page
*/
static char *title_page =
"<HTML><HEAD>"
"<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">"
"<TITLE>MaxScale</TITLE>"
"</HEAD><BODY>"
"<H1>MaxScale - Status View</H1>"
"</BODY></HTML>";
/**
* HTML of the main frames, those below the title frame
*/
static char *frame1_page =
"<HTML>"
"<FRAMESET COLS=\"20%,80%\">"
"<FRAME SRC=\"menu.html\">"
"<FRAME SRC=\"blank.html\" NAME=\"darea\">"
"</FRAMESET>"
"</HTML>";
/**
* The menu page HTML
*/
static char *menu_page =
"<HTML><HEAD>"
"<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">"
"</HEAD><BODY>"
"<H2>Options</H2><P>"
"<UL><LI><A HREF=\"monitors.html\" target=\"darea\">Monitors</A>"
"<LI><A HREF=\"services.html\" target=\"darea\">Services</A>"
"<LI><A HREF=\"servers.html\" target=\"darea\">Servers</A>"
"<LI><A HREF=\"sessions.html\" target=\"darea\">Sessions</A>"
"</UL></BODY></HTML>";
/**
* A blank page, contents of the display area when we first connect
*/
static char *blank_page = "<HTML><BODY>&nbsp;</BODY></HTML>";
/**
* The CSS used for every "page"
*/
static char *css =
"table, td, th { border: 1px solid blue; }\n"
"th { background-color: blue; color: white; padding: 5px }\n"
"td { padding: 5px; }\n"
"table { border-collapse: collapse; }\n"
"a:link { color: #0000FF; }\n"
"a:visted { color: #0000FF; }\n"
"a:hover { color: #FF0000; }\n"
"a:active { color: #0000FF; }\n"
"h1 { color: blue; font-family: serif }\n"
"h2 { color: blue; font-family: serif }\n"
"p { font-family: serif }\n"
"li { font-family: serif }\n";
/**
* Send the standard HTTP headers for an HTML file
*/
static void
send_html_header(DCB *dcb)
{
char date[64] = "";
const char *fmt = "%a, %d %b %Y %H:%M:%S GMT";
time_t httpd_current_time = time(NULL);
strftime(date, sizeof(date), fmt, localtime(&httpd_current_time));
dcb_printf(dcb, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: %s\r\nConnection: close\r\nContent-Type: text/html\r\n", date, "MaxScale");
dcb_printf(dcb, "\r\n");
}
/**
* Send a static HTML page
*
* @param dcb The DCB of the connection to the browser
* @param html The HTML to send
*/
static void
send_static_html(DCB *dcb, char *html)
{
dcb_printf(dcb, html);
}
/**
* Send the index page
*
* @param session The router session
*/
static void
send_index(WEB_SESSION *session)
{
DCB *dcb = session->session->client;
send_html_header(dcb);
send_static_html(dcb, index_page);
dcb_close(dcb);
}
/**
* Send the CSS
*
* @param session The router session
*/
static void
send_css(WEB_SESSION *session)
{
DCB *dcb = session->session->client;
send_html_header(dcb);
send_static_html(dcb, css);
dcb_close(dcb);
}
/**
* Send the title page
*
* @param session The router session
*/
static void
send_title(WEB_SESSION *session)
{
DCB *dcb = session->session->client;
send_html_header(dcb);
send_static_html(dcb, title_page);
dcb_close(dcb);
}
/**
* Send the frame1 page
*
* @param session The router session
*/
static void
send_frame1(WEB_SESSION *session)
{
DCB *dcb = session->session->client;
send_html_header(dcb);
send_static_html(dcb, frame1_page);
dcb_close(dcb);
}
/**
* Send the menu page
*
* @param session The router session
*/
static void
send_menu(WEB_SESSION *session)
{
DCB *dcb = session->session->client;
send_html_header(dcb);
send_static_html(dcb, menu_page);
dcb_close(dcb);
}
/**
* Send a blank page
*
* @param session The router session
*/
static void
send_blank(WEB_SESSION *session)
{
DCB *dcb = session->session->client;
send_html_header(dcb);
send_static_html(dcb, blank_page);
dcb_close(dcb);
}
/**
* Write a table row for a service. This is called using the service
* iterator function
*
* @param service The service to display
* @param dcb The DCB to print the HTML to
*/
static void
service_row(SERVICE *service, DCB *dcb)
{
dcb_printf(dcb, "<TR><TD>%s</TD><TD>%s</TD><TD>%d</TD><TD>%d</TD></TR>\n",
service->name, service->routerModule,
service->stats.n_current, service->stats.n_sessions);
}
/**
* Send the services page. This produces a table by means of the
* serviceIterate call.
*
* @param session The router session
*/
static void
send_services(WEB_SESSION *session)
{
DCB *dcb = session->session->client;
send_html_header(dcb);
dcb_printf(dcb, "<HTML><HEAD>");
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
dcb_printf(dcb, "<BODY><H2>Services</H2><P>");
dcb_printf(dcb, "<TABLE><TR><TH>Name</TH><TH>Router</TH><TH>");
dcb_printf(dcb, "Current Sessions</TH><TH>Total Sessions</TH></TR>\n");
serviceIterate(service_row, dcb);
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
dcb_close(dcb);
}
/**
* Write a session row for a session. this is called using the session
* iterator function
*
* @param session The session to display
* @param dcb The DCB to send the HTML to
*/
static void
session_row(SESSION *session, DCB *dcb)
{
dcb_printf(dcb, "<TR><TD>%-16p</TD><TD>%s</TD><TD>%s</TD><TD>%s</TD></TR>\n",
session, ((session->client && session->client->remote)
? session->client->remote : ""),
(session->service && session->service->name
? session->service->name : ""),
session_state(session->state));
}
/**
* Send the sessions page. The produces a table of all the current sessions
* display. It makes use of the sessionIterate call to call the function
* session_row() with each session.
*
* @param session The router session
*/
static void
send_sessions(WEB_SESSION *session)
{
DCB *dcb = session->session->client;
send_html_header(dcb);
dcb_printf(dcb, "<HTML><HEAD>");
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
dcb_printf(dcb, "<BODY><H2>Sessions</H2><P>");
dcb_printf(dcb, "<TABLE><TR><TH>Session</TH><TH>Client</TH><TH>");
dcb_printf(dcb, "Service</TH><TH>State</TH></TR>\n");
sessionIterate(session_row, dcb);
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
dcb_close(dcb);
}
/**
* Display a table row for a particular server. This is called via the
* serverIterate call in send_servers.
*
* @param server The server to print
* @param dcb The DCB to send the HTML to
*/
static void
server_row(SERVER *server, DCB *dcb)
{
dcb_printf(dcb, "<TR><TD>%s</TD><TD>%s</TD><TD>%d</TD><TD>%s</TD><TD>%d</TD></TR>\n",
server->unique_name, server->name, server->port,
server_status(server), server->stats.n_current);
}
/**
* Send the servers page
*
* @param session The router session
*/
static void
send_servers(WEB_SESSION *session)
{
DCB *dcb = session->session->client;
send_html_header(dcb);
dcb_printf(dcb, "<HTML><HEAD>");
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
dcb_printf(dcb, "<BODY><H2>Servers</H2><P>");
dcb_printf(dcb, "<TABLE><TR><TH>Server</TH><TH>Address</TH><TH>");
dcb_printf(dcb, "Port</TH><TH>State</TH><TH>Connections</TH></TR>\n");
serverIterate(server_row, dcb);
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
dcb_close(dcb);
}
/**
* Print a table row for the monitors table
*
* @param monitor The monitor to print
* @param dcb The DCB to print to
*/
static void
monitor_row(MONITOR *monitor, DCB *dcb)
{
dcb_printf(dcb, "<TR><TD>%s</TD><TD>%s</TD></TR>\n",
monitor->name, monitor->state & MONITOR_STATE_RUNNING
? "Running" : "Stopped");
}
/**
* Send the monitors page. This iterates on all the monitors and send
* the rows via the monitor_monitor.
*
* @param session The router session
*/
static void
send_monitors(WEB_SESSION *session)
{
DCB *dcb = session->session->client;
send_html_header(dcb);
dcb_printf(dcb, "<HTML><HEAD>");
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
dcb_printf(dcb, "<BODY><H2>Monitors</H2><P>");
dcb_printf(dcb, "<TABLE><TR><TH>Monitor</TH><TH>State</TH></TR>\n");
monitorIterate(monitor_row, dcb);
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
dcb_close(dcb);
}
/**
* Respond with an HTTP error
*
* @param session The router session
* @param err The HTTP error code to send
* @param msg The message to print
*/
static void
respond_error(WEB_SESSION *session, int err, char *msg)
{
DCB *dcb = session->session->client;
dcb_printf(dcb, "HTTP/1.1 %d %s\n", err, msg);
dcb_printf(dcb, "Content-Type: text/html\n");
dcb_printf(dcb, "\n");
dcb_printf(dcb, "<HTML><BODY>\n");
dcb_printf(dcb, "MaxScale webserver plugin unable to satisfy request.\n");
dcb_printf(dcb, "<P>Code: %d, %s\n", err, msg);
dcb_printf(dcb, "</BODY></HTML>");
dcb_close(dcb);
}