Branch update
Branch update
This commit is contained in:
@ -116,10 +116,12 @@ char fname[1024], *home;
|
||||
char uname[80], passwd[80];
|
||||
|
||||
initialise();
|
||||
if ((home = getenv("MAXSCALE_HOME")) != NULL)
|
||||
if ((home = getenv("MAXSCALE_HOME")) != NULL && strlen(home) < 1024){
|
||||
sprintf(fname, "%s/etc/passwd", home);
|
||||
else
|
||||
}
|
||||
else{
|
||||
sprintf(fname, "/usr/local/skysql/MaxScale/etc/passwd");
|
||||
}
|
||||
if ((fp = fopen(fname, "r")) == NULL)
|
||||
return NULL;
|
||||
if ((rval = users_alloc()) == NULL)
|
||||
@ -150,10 +152,12 @@ FILE *fp;
|
||||
char fname[1024], *home, *cpasswd;
|
||||
|
||||
initialise();
|
||||
if ((home = getenv("MAXSCALE_HOME")) != NULL)
|
||||
if ((home = getenv("MAXSCALE_HOME")) != NULL && strlen(home) < 1024){
|
||||
sprintf(fname, "%s/etc/passwd", home);
|
||||
else
|
||||
}
|
||||
else{
|
||||
sprintf(fname, "/usr/local/skysql/MaxScale/etc/passwd");
|
||||
}
|
||||
|
||||
if (users == NULL)
|
||||
{
|
||||
@ -246,7 +250,7 @@ char* admin_remove_user(
|
||||
/**
|
||||
* Open passwd file and remove user from the file.
|
||||
*/
|
||||
if ((home = getenv("MAXSCALE_HOME")) != NULL) {
|
||||
if ((home = getenv("MAXSCALE_HOME")) != NULL && strlen(home) < 1024) {
|
||||
sprintf(fname, "%s/etc/passwd", home);
|
||||
sprintf(fname_tmp, "%s/etc/passwd_tmp", home);
|
||||
} else {
|
||||
@ -310,7 +314,12 @@ char* admin_remove_user(
|
||||
* Unmatching lines are copied to tmp file.
|
||||
*/
|
||||
if (strncmp(uname, fusr, strlen(uname)+1) != 0) {
|
||||
fsetpos(fp, &rpos); /** one step back */
|
||||
if(fsetpos(fp, &rpos) != 0){ /** one step back */
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unable to set stream position. ")));
|
||||
|
||||
}
|
||||
fgets(line, LINELEN, fp);
|
||||
fputs(line, fp_tmp);
|
||||
}
|
||||
|
||||
@ -81,6 +81,7 @@ SHARED_BUF *sbuf;
|
||||
if ((sbuf = (SHARED_BUF *)malloc(sizeof(SHARED_BUF))) == NULL)
|
||||
{
|
||||
free(rval);
|
||||
rval = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
@ -90,6 +91,7 @@ SHARED_BUF *sbuf;
|
||||
ss_dassert(sbuf->data != NULL);
|
||||
free(rval);
|
||||
free(sbuf);
|
||||
rval = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
spinlock_init(&rval->gwbuf_lock);
|
||||
@ -106,9 +108,8 @@ SHARED_BUF *sbuf;
|
||||
rval->gwbuf_bufobj = NULL;
|
||||
CHK_GWBUF(rval);
|
||||
retblock:
|
||||
if (rval == NULL || sbuf == NULL || sbuf->data == NULL)
|
||||
if (rval == NULL)
|
||||
{
|
||||
ss_dassert(rval != NULL && sbuf != NULL && sbuf->data != NULL);
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Memory allocation failed due to %s.",
|
||||
@ -516,8 +517,10 @@ void* gwbuf_get_buffer_object_data(
|
||||
}
|
||||
/** Unlock */
|
||||
spinlock_release(&buf->gwbuf_lock);
|
||||
|
||||
return bo->bo_data;
|
||||
if(bo){
|
||||
return bo->bo_data;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
* 12/09/14 Mark Riddoch Addition of checks on servers list and
|
||||
* internal router suppression of messages
|
||||
* 30/10/14 Massimiliano Pinto Added disable_master_failback parameter
|
||||
* 07/11/14 Massimiliano Pinto Addition of monitor timeouts for connect/read/write
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -59,6 +60,7 @@
|
||||
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
|
||||
extern int setipaddress(struct in_addr *, char *);
|
||||
static int process_config_context(CONFIG_CONTEXT *);
|
||||
static int process_config_update(CONFIG_CONTEXT *);
|
||||
static void free_config_context(CONFIG_CONTEXT *);
|
||||
@ -462,6 +464,7 @@ int error_count = 0;
|
||||
|
||||
if (!succp)
|
||||
{
|
||||
if(param){
|
||||
LOGIF(LM, (skygw_log_write(
|
||||
LOGFILE_MESSAGE,
|
||||
"* Warning : invalid value type "
|
||||
@ -471,6 +474,12 @@ int error_count = 0;
|
||||
((SERVICE*)obj->element)->name,
|
||||
param->name,
|
||||
param->value)));
|
||||
}else{
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Error : parameter was NULL")));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} /*< if (rw_split) */
|
||||
@ -767,6 +776,9 @@ int error_count = 0;
|
||||
int replication_heartbeat = 0;
|
||||
int detect_stale_master = 0;
|
||||
int disable_master_failback = 0;
|
||||
int connect_timeout = 0;
|
||||
int read_timeout = 0;
|
||||
int write_timeout = 0;
|
||||
|
||||
module = config_get_value(obj->parameters, "module");
|
||||
servers = config_get_value(obj->parameters, "servers");
|
||||
@ -788,6 +800,16 @@ int error_count = 0;
|
||||
disable_master_failback = atoi(config_get_value(obj->parameters, "disable_master_failback"));
|
||||
}
|
||||
|
||||
if (config_get_value(obj->parameters, "backend_connect_timeout")) {
|
||||
connect_timeout = atoi(config_get_value(obj->parameters, "backend_connect_timeout"));
|
||||
}
|
||||
if (config_get_value(obj->parameters, "backend_read_timeout")) {
|
||||
read_timeout = atoi(config_get_value(obj->parameters, "backend_read_timeout"));
|
||||
}
|
||||
if (config_get_value(obj->parameters, "backend_write_timeout")) {
|
||||
write_timeout = atoi(config_get_value(obj->parameters, "backend_write_timeout"));
|
||||
}
|
||||
|
||||
if (module)
|
||||
{
|
||||
obj->element = monitor_alloc(obj->object, module);
|
||||
@ -819,6 +841,14 @@ int error_count = 0;
|
||||
if(disable_master_failback == 1)
|
||||
monitorDisableMasterFailback(obj->element, disable_master_failback);
|
||||
|
||||
/* set timeouts */
|
||||
if (connect_timeout > 0)
|
||||
monitorSetNetworkTimeout(obj->element, MONITOR_CONNECT_TIMEOUT, connect_timeout);
|
||||
if (read_timeout > 0)
|
||||
monitorSetNetworkTimeout(obj->element, MONITOR_READ_TIMEOUT, read_timeout);
|
||||
if (write_timeout > 0)
|
||||
monitorSetNetworkTimeout(obj->element, MONITOR_WRITE_TIMEOUT, write_timeout);
|
||||
|
||||
/* get the servers to monitor */
|
||||
s = strtok(servers, ",");
|
||||
while (s)
|
||||
@ -1271,7 +1301,7 @@ SERVER *server;
|
||||
(PERCENT_TYPE|COUNT_TYPE));
|
||||
}
|
||||
|
||||
if (!succp)
|
||||
if (!succp && param != NULL)
|
||||
{
|
||||
LOGIF(LM, (skygw_log_write(
|
||||
LOGFILE_MESSAGE,
|
||||
@ -1316,6 +1346,7 @@ SERVER *server;
|
||||
|
||||
if (!succp)
|
||||
{
|
||||
if(param){
|
||||
LOGIF(LM, (skygw_log_write(
|
||||
LOGFILE_MESSAGE,
|
||||
"* Warning : invalid value type "
|
||||
@ -1325,6 +1356,11 @@ SERVER *server;
|
||||
((SERVICE*)obj->element)->name,
|
||||
param->name,
|
||||
param->value)));
|
||||
}else{
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Error : parameter was NULL")));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1359,7 +1395,7 @@ SERVER *server;
|
||||
if (enable_root_user)
|
||||
serviceEnableRootUser(service, atoi(enable_root_user));
|
||||
|
||||
if (allow_localhost_match_wildcard_host)
|
||||
if (allow_localhost_match_wildcard_host && service)
|
||||
serviceEnableLocalhostMatchWildcardHost(
|
||||
service,
|
||||
atoi(allow_localhost_match_wildcard_host));
|
||||
@ -1623,6 +1659,9 @@ static char *monitor_params[] =
|
||||
"detect_replication_lag",
|
||||
"detect_stale_master",
|
||||
"disable_master_failback",
|
||||
"backend_connect_timeout",
|
||||
"backend_read_timeout",
|
||||
"backend_write_timeout",
|
||||
NULL
|
||||
};
|
||||
/**
|
||||
|
||||
@ -646,7 +646,13 @@ getUsers(SERVICE *service, USERS *users)
|
||||
|
||||
users_data = (char *)calloc(nusers, (users_data_row_len * sizeof(char)) + 1);
|
||||
|
||||
if(users_data == NULL) {
|
||||
if (users_data == NULL) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Memory allocation for user data failed due to "
|
||||
"%d, %s.",
|
||||
errno,
|
||||
strerror(errno))));
|
||||
mysql_free_result(result);
|
||||
mysql_close(con);
|
||||
|
||||
@ -855,9 +861,6 @@ static int uh_cmpfun( void* v1, void* v2) {
|
||||
if (v1 == NULL || v2 == NULL)
|
||||
return 0;
|
||||
|
||||
if (hu1 == NULL || hu2 == NULL)
|
||||
return 0;
|
||||
|
||||
if (hu1->user == NULL || hu2->user == NULL)
|
||||
return 0;
|
||||
|
||||
@ -961,9 +964,6 @@ char *mysql_format_user_entry(void *data)
|
||||
|
||||
entry = (MYSQL_USER_HOST *) data;
|
||||
|
||||
if (entry == NULL)
|
||||
return NULL;
|
||||
|
||||
mysql_user = (char *) calloc(mysql_user_len, sizeof(char));
|
||||
|
||||
if (mysql_user == NULL)
|
||||
|
||||
@ -1140,41 +1140,48 @@ dcb_close(DCB *dcb)
|
||||
/*<
|
||||
* Stop dcb's listening and modify state accordingly.
|
||||
*/
|
||||
rc = poll_remove_dcb(dcb);
|
||||
|
||||
ss_dassert(dcb->state == DCB_STATE_NOPOLLING ||
|
||||
dcb->state == DCB_STATE_ZOMBIE);
|
||||
/**
|
||||
* close protocol and router session
|
||||
*/
|
||||
if (dcb->func.close != NULL)
|
||||
{
|
||||
dcb->func.close(dcb);
|
||||
}
|
||||
dcb_call_callback(dcb, DCB_REASON_CLOSE);
|
||||
if (dcb->state == DCB_STATE_POLLING)
|
||||
{
|
||||
rc = poll_remove_dcb(dcb);
|
||||
|
||||
if (rc == 0) {
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [dcb_close] Removed dcb %p in state %s from "
|
||||
"poll set.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
} else {
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"%lu [dcb_close] Error : Removing dcb %p in state %s from "
|
||||
"poll set failed.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
}
|
||||
|
||||
if (dcb->state == DCB_STATE_NOPOLLING)
|
||||
{
|
||||
dcb_add_to_zombieslist(dcb);
|
||||
}
|
||||
if (rc == 0) {
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [dcb_close] Removed dcb %p in state %s from "
|
||||
"poll set.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
} else {
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Removing DCB fd == %d in state %s from "
|
||||
"poll set failed.",
|
||||
dcb->fd,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
}
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
/**
|
||||
* close protocol and router session
|
||||
*/
|
||||
if (dcb->func.close != NULL)
|
||||
{
|
||||
dcb->func.close(dcb);
|
||||
}
|
||||
dcb_call_callback(dcb, DCB_REASON_CLOSE);
|
||||
|
||||
|
||||
if (dcb->state == DCB_STATE_NOPOLLING)
|
||||
{
|
||||
dcb_add_to_zombieslist(dcb);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
ss_dassert(dcb->state == DCB_STATE_NOPOLLING ||
|
||||
dcb->state == DCB_STATE_ZOMBIE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1662,7 +1669,7 @@ static bool dcb_set_state_nomutex(
|
||||
"Old state %s > new state %s.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
STRDCBSTATE(*old_state),
|
||||
(old_state == NULL ? "NULL" : STRDCBSTATE(*old_state)),
|
||||
STRDCBSTATE(new_state))));
|
||||
}
|
||||
return succp;
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <filter.h>
|
||||
#include <session.h>
|
||||
#include <modules.h>
|
||||
@ -328,27 +329,43 @@ DOWNSTREAM *me;
|
||||
if ((filter->obj = load_module(filter->module,
|
||||
MODULE_FILTER)) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
me = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
}
|
||||
|
||||
if (filter->filter == NULL)
|
||||
{
|
||||
if ((filter->filter = (filter->obj->createInstance)(filter->options,
|
||||
filter->parameters)) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
me = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
}
|
||||
if ((me = (DOWNSTREAM *)calloc(1, sizeof(DOWNSTREAM))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Memory allocation for filter session failed "
|
||||
"due to %d,%s.",
|
||||
errno,
|
||||
strerror(errno))));
|
||||
|
||||
goto retblock;
|
||||
}
|
||||
me->instance = filter->filter;
|
||||
me->routeQuery = (void *)(filter->obj->routeQuery);
|
||||
me->session = filter->obj->newSession(me->instance, session);
|
||||
|
||||
|
||||
if ((me->session=filter->obj->newSession(me->instance, session)) == NULL)
|
||||
{
|
||||
free(me);
|
||||
me = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
filter->obj->setDownstream(me->instance, me->session, downstream);
|
||||
|
||||
|
||||
retblock:
|
||||
return me;
|
||||
}
|
||||
|
||||
|
||||
@ -141,7 +141,7 @@ const char *progname = NULL;
|
||||
static struct option long_options[] = {
|
||||
{"homedir", required_argument, 0, 'c'},
|
||||
{"config", required_argument, 0, 'f'},
|
||||
{"nodeamon", required_argument, 0, 'd'},
|
||||
{"nodaemon", no_argument, 0, 'd'},
|
||||
{"log", required_argument, 0, 'l'},
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{"help", no_argument, 0, '?'},
|
||||
@ -479,6 +479,11 @@ static bool resolve_maxscale_conf_fname(
|
||||
goto return_succp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Allocate memory for use of realpath */
|
||||
*cnf_full_path = (char *)malloc(PATH_MAX+1);
|
||||
}
|
||||
/*<
|
||||
* 3. argument is valid relative pathname
|
||||
* '-f ../myconf.cnf'
|
||||
@ -549,6 +554,7 @@ static bool resolve_maxscale_homedir(
|
||||
if (*p_home_dir != NULL)
|
||||
{
|
||||
log_context = strdup("Command-line argument");
|
||||
tmp = NULL;
|
||||
goto check_home_dir;
|
||||
}
|
||||
/*<
|
||||
@ -640,7 +646,7 @@ check_home_dir:
|
||||
fprintf(stderr,
|
||||
"Using %s as MAXSCALE_HOME = %s\n",
|
||||
log_context,
|
||||
tmp);
|
||||
(tmp == NULL ? *p_home_dir : tmp));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -648,8 +654,11 @@ check_home_dir:
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
free(tmp);
|
||||
|
||||
if (tmp != NULL)
|
||||
{
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
if (log_context != NULL)
|
||||
{
|
||||
free(log_context);
|
||||
@ -993,7 +1002,7 @@ int main(int argc, char **argv)
|
||||
int n_services;
|
||||
int eno = 0; /*< local variable for errno */
|
||||
int opt;
|
||||
void** threads; /*< thread list */
|
||||
void** threads = NULL; /*< thread list */
|
||||
char mysql_home[PATH_MAX+1];
|
||||
char datadir_arg[10+PATH_MAX+1]; /*< '--datadir=' + PATH_MAX */
|
||||
char language_arg[11+PATH_MAX+1]; /*< '--language=' + PATH_MAX */
|
||||
@ -1470,7 +1479,14 @@ int main(int argc, char **argv)
|
||||
bool succp;
|
||||
|
||||
sprintf(buf, "%s/log", home_dir);
|
||||
mkdir(buf, 0777);
|
||||
if(mkdir(buf, 0777) != 0){
|
||||
|
||||
if(errno != EEXIST){
|
||||
fprintf(stderr,
|
||||
"Error: Cannot create log directory: %s\n",buf);
|
||||
goto return_main;
|
||||
}
|
||||
}
|
||||
argv[0] = "MaxScale";
|
||||
argv[1] = "-j";
|
||||
argv[2] = buf;
|
||||
@ -1517,7 +1533,12 @@ int main(int argc, char **argv)
|
||||
* machine.
|
||||
*/
|
||||
sprintf(datadir, "%s/data%d", home_dir, getpid());
|
||||
mkdir(datadir, 0777);
|
||||
if(mkdir(datadir, 0777) != 0){
|
||||
LOGIF(LE,(skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Directory creation failed due to %s.",
|
||||
strerror(errno))));
|
||||
}
|
||||
|
||||
if (!daemon_mode)
|
||||
{
|
||||
@ -1695,11 +1716,7 @@ int main(int argc, char **argv)
|
||||
for (n = 0; n < n_threads - 1; n++)
|
||||
{
|
||||
thread_wait(threads[n]);
|
||||
}
|
||||
free(threads);
|
||||
free(home_dir);
|
||||
free(cnf_file_path);
|
||||
|
||||
}
|
||||
/*<
|
||||
* Wait the flush thread.
|
||||
*/
|
||||
@ -1723,6 +1740,13 @@ int main(int argc, char **argv)
|
||||
unlink_pidfile();
|
||||
|
||||
return_main:
|
||||
if (threads)
|
||||
free(threads);
|
||||
if (home_dir)
|
||||
free(home_dir);
|
||||
if (cnf_file_path)
|
||||
free(cnf_file_path);
|
||||
|
||||
return rc;
|
||||
} /*< End of main */
|
||||
|
||||
|
||||
@ -93,7 +93,7 @@ unsigned char mask;
|
||||
{
|
||||
bitmask->bits = realloc(bitmask->bits,
|
||||
(bitmask->length + BIT_LENGTH_INC) / 8);
|
||||
memset(bitmask + (bitmask->length / 8), 0,
|
||||
memset(bitmask->bits + (bitmask->length / 8), 0,
|
||||
BIT_LENGTH_INC / 8);
|
||||
bitmask->length += (BIT_LENGTH_INC / 8);
|
||||
}
|
||||
@ -121,7 +121,7 @@ unsigned char mask;
|
||||
{
|
||||
bitmask->bits = realloc(bitmask->bits,
|
||||
(bitmask->length + BIT_LENGTH_INC) / 8);
|
||||
memset(bitmask + (bitmask->length / 8), 0,
|
||||
memset(bitmask->bits + (bitmask->length / 8), 0,
|
||||
BIT_LENGTH_INC / 8);
|
||||
bitmask->length += (BIT_LENGTH_INC / 8);
|
||||
}
|
||||
@ -150,7 +150,7 @@ unsigned char mask;
|
||||
{
|
||||
bitmask->bits = realloc(bitmask->bits,
|
||||
(bitmask->length + BIT_LENGTH_INC) / 8);
|
||||
memset(bitmask + (bitmask->length / 8), 0,
|
||||
memset(bitmask->bits + (bitmask->length / 8), 0,
|
||||
BIT_LENGTH_INC / 8);
|
||||
bitmask->length += (BIT_LENGTH_INC / 8);
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ void *
|
||||
load_module(const char *module, const char *type)
|
||||
{
|
||||
char *home, *version;
|
||||
char fname[MAXPATHLEN];
|
||||
char fname[MAXPATHLEN+1];
|
||||
void *dlhandle, *sym;
|
||||
char *(*ver)();
|
||||
void *(*ep)(), *modobj;
|
||||
@ -94,11 +94,12 @@ MODULE_INFO *mod_info = NULL;
|
||||
*
|
||||
* Search of the shared object.
|
||||
*/
|
||||
sprintf(fname, "./lib%s.so", module);
|
||||
snprintf(fname,MAXPATHLEN+1, "./lib%s.so", module);
|
||||
|
||||
if (access(fname, F_OK) == -1)
|
||||
{
|
||||
home = get_maxscale_home ();
|
||||
sprintf(fname, "%s/modules/lib%s.so", home, module);
|
||||
snprintf(fname, MAXPATHLEN+1,"%s/modules/lib%s.so", home, module);
|
||||
|
||||
if (access(fname, F_OK) == -1)
|
||||
{
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *enc;
|
||||
char *enc, *pw;
|
||||
|
||||
if (argc != 2)
|
||||
{
|
||||
@ -47,9 +47,21 @@ char *enc;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((enc = encryptPassword(argv[1])) != NULL)
|
||||
pw = calloc(81,sizeof(char));
|
||||
|
||||
if(pw == NULL){
|
||||
fprintf(stderr, "Error: cannot allocate enough memory.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
strncpy(pw,argv[1],80);
|
||||
|
||||
if ((enc = encryptPassword(pw)) != NULL){
|
||||
printf("%s\n", enc);
|
||||
else
|
||||
}else{
|
||||
fprintf(stderr, "Failed to encode the password\n");
|
||||
}
|
||||
|
||||
free(pw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -175,6 +175,51 @@ GWBUF *addition;
|
||||
return orig;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract the SQL from a COM_QUERY packet and return in a NULL terminated buffer.
|
||||
* The buffer shoudl be freed by the caller when it is no longer required.
|
||||
*
|
||||
* If the packet is not a COM_QUERY packet then the function will return NULL
|
||||
*
|
||||
* @param buf The buffer chain
|
||||
* @return Null terminated string containing query text or NULL on error
|
||||
*/
|
||||
char *
|
||||
modutil_get_SQL(GWBUF *buf)
|
||||
{
|
||||
unsigned int len, length;
|
||||
unsigned char *ptr, *dptr, *rval = NULL;
|
||||
|
||||
if (!modutil_is_SQL(buf))
|
||||
return rval;
|
||||
ptr = GWBUF_DATA(buf);
|
||||
length = *ptr++;
|
||||
length += (*ptr++ << 8);
|
||||
length += (*ptr++ << 16);
|
||||
|
||||
if ((rval = (char *)malloc(length + 1)) == NULL)
|
||||
return NULL;
|
||||
dptr = rval;
|
||||
ptr += 2; // Skip sequence id and COM_QUERY byte
|
||||
len = GWBUF_LENGTH(buf) - 5;
|
||||
while (buf && length > 0)
|
||||
{
|
||||
int clen = length > len ? len : length;
|
||||
memcpy(dptr, ptr, clen);
|
||||
dptr += clen;
|
||||
length -= clen;
|
||||
buf = buf->next;
|
||||
if (buf)
|
||||
{
|
||||
ptr = GWBUF_DATA(buf);
|
||||
len = GWBUF_LENGTH(buf);
|
||||
}
|
||||
}
|
||||
*dptr = 0;
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy query string from GWBUF buffer to separate memory area.
|
||||
*
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
* 23/05/14 Massimiliano Pinto Addition of monitor_interval parameter
|
||||
* and monitor id
|
||||
* 30/10/14 Massimiliano Pinto Addition of disable_master_failback parameter
|
||||
* 07/11/14 Massimiliano Pinto Addition of monitor network timeouts
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -342,5 +343,18 @@ monitorDisableMasterFailback(MONITOR *mon, int disable)
|
||||
{
|
||||
if (mon->module->disableMasterFailback != NULL) {
|
||||
mon->module->disableMasterFailback(mon->handle, disable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Monitor timeouts for connect/read/write
|
||||
*
|
||||
* @param mon The monitor instance
|
||||
* @param type The timeout handling type
|
||||
* @param value The timeout to set
|
||||
*/
|
||||
void
|
||||
monitorSetNetworkTimeout(MONITOR *mon, int type, int value) {
|
||||
if (mon->module->setNetworkTimeout != NULL) {
|
||||
mon->module->setNetworkTimeout(mon->handle, type, value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,7 +292,8 @@ poll_remove_dcb(DCB *dcb)
|
||||
CHK_DCB(dcb);
|
||||
|
||||
/*< It is possible that dcb has already been removed from the set */
|
||||
if (dcb->state != DCB_STATE_POLLING) {
|
||||
if (dcb->state != DCB_STATE_POLLING)
|
||||
{
|
||||
if (dcb->state == DCB_STATE_NOPOLLING ||
|
||||
dcb->state == DCB_STATE_ZOMBIE)
|
||||
{
|
||||
@ -300,7 +301,6 @@ poll_remove_dcb(DCB *dcb)
|
||||
}
|
||||
goto return_rc;
|
||||
}
|
||||
|
||||
/*<
|
||||
* Set state to NOPOLLING and remove dcb from poll set.
|
||||
*/
|
||||
|
||||
@ -254,6 +254,7 @@ MAXKEYS key;
|
||||
secret_file,
|
||||
errno,
|
||||
strerror(errno))));
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -122,6 +122,13 @@ SERVICE *service;
|
||||
}
|
||||
service->name = strdup(servname);
|
||||
service->routerModule = strdup(router);
|
||||
if (service->name == NULL || service->routerModule == NULL)
|
||||
{
|
||||
if (service->name)
|
||||
free(service->name);
|
||||
free(service);
|
||||
return NULL;
|
||||
}
|
||||
service->version_string = NULL;
|
||||
memset(&service->stats, 0, sizeof(SERVICE_STATS));
|
||||
service->ports = NULL;
|
||||
@ -196,8 +203,13 @@ GWPROTOCOL *funcs;
|
||||
|
||||
if (port->listener == NULL)
|
||||
{
|
||||
return 0;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Failed to create listener for service %s.",
|
||||
service->name)));
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if (strcmp(port->protocol, "MySQLClient") == 0) {
|
||||
int loaded;
|
||||
|
||||
@ -206,12 +218,25 @@ GWPROTOCOL *funcs;
|
||||
* including hosts and db names
|
||||
*/
|
||||
service->users = mysql_users_alloc();
|
||||
loaded = load_mysql_users(service);
|
||||
|
||||
|
||||
if ((loaded = load_mysql_users(service)) < 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unable to load users from %s:%d for "
|
||||
"service %s.",
|
||||
port->address,
|
||||
port->port,
|
||||
service->name)));
|
||||
hashtable_free(service->users->data);
|
||||
free(service->users);
|
||||
dcb_free(port->listener);
|
||||
port->listener = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
/* At service start last update is set to USERS_REFRESH_TIME seconds earlier.
|
||||
* This way MaxScale could try reloading users' just after startup
|
||||
*/
|
||||
|
||||
service->rate_limit.last=time(NULL) - USERS_REFRESH_TIME;
|
||||
service->rate_limit.nloads=1;
|
||||
|
||||
@ -219,14 +244,21 @@ GWPROTOCOL *funcs;
|
||||
LOGFILE_MESSAGE,
|
||||
"Loaded %d MySQL Users for service [%s].",
|
||||
loaded, service->name)));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Generic users table */
|
||||
service->users = users_alloc();
|
||||
}
|
||||
|
||||
if ((funcs =
|
||||
(GWPROTOCOL *)load_module(port->protocol, MODULE_PROTOCOL)) == NULL)
|
||||
if ((funcs=(GWPROTOCOL *)load_module(port->protocol, MODULE_PROTOCOL))
|
||||
== NULL)
|
||||
{
|
||||
if (service->users->data)
|
||||
{
|
||||
hashtable_free(service->users->data);
|
||||
}
|
||||
free(service->users);
|
||||
dcb_free(port->listener);
|
||||
port->listener = NULL;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -235,34 +267,60 @@ GWPROTOCOL *funcs;
|
||||
"for service %s not started.",
|
||||
port->protocol,
|
||||
service->name)));
|
||||
return 0;
|
||||
goto retblock;
|
||||
}
|
||||
memcpy(&(port->listener->func), funcs, sizeof(GWPROTOCOL));
|
||||
port->listener->session = NULL;
|
||||
|
||||
if (port->address)
|
||||
sprintf(config_bind, "%s:%d", port->address, port->port);
|
||||
else
|
||||
sprintf(config_bind, "0.0.0.0:%d", port->port);
|
||||
|
||||
if (port->listener->func.listen(port->listener, config_bind)) {
|
||||
if (port->listener->func.listen(port->listener, config_bind))
|
||||
{
|
||||
port->listener->session = session_alloc(service, port->listener);
|
||||
|
||||
if (port->listener->session != NULL) {
|
||||
if (port->listener->session != NULL)
|
||||
{
|
||||
port->listener->session->state = SESSION_STATE_LISTENER;
|
||||
listeners += 1;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Failed to create session to service %s.",
|
||||
service->name)));
|
||||
|
||||
if (service->users->data)
|
||||
{
|
||||
hashtable_free(service->users->data);
|
||||
}
|
||||
free(service->users);
|
||||
dcb_close(port->listener);
|
||||
port->listener = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
} else {
|
||||
dcb_close(port->listener);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unable to start to listen port %d for %s %s.",
|
||||
port->port,
|
||||
port->protocol,
|
||||
service->name)));
|
||||
if (service->users->data)
|
||||
{
|
||||
hashtable_free(service->users->data);
|
||||
}
|
||||
free(service->users);
|
||||
dcb_close(port->listener);
|
||||
port->listener = NULL;
|
||||
}
|
||||
|
||||
retblock:
|
||||
return listeners;
|
||||
}
|
||||
|
||||
@ -1003,8 +1061,8 @@ int service_refresh_users(SERVICE *service) {
|
||||
if ( (time(NULL) < (service->rate_limit.last + USERS_REFRESH_TIME)) || (service->rate_limit.nloads > USERS_REFRESH_MAX_PER_TIME)) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"%lu [service_refresh_users] refresh rate limit exceeded loading new users' table",
|
||||
pthread_self())));
|
||||
"Refresh rate limit exceeded for load of users' table for service '%s'.",
|
||||
service->name)));
|
||||
|
||||
spinlock_release(&service->users_table_spin);
|
||||
return 1;
|
||||
|
||||
@ -668,9 +668,10 @@ int i;
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Failed to create filter '%s' for service '%s'.\n",
|
||||
service->filters[i]->name,
|
||||
service->name)));
|
||||
"Error : Failed to create filter '%s' for "
|
||||
"service '%s'.\n",
|
||||
service->filters[i]->name,
|
||||
service->name)));
|
||||
return 0;
|
||||
}
|
||||
session->filters[i].filter = service->filters[i];
|
||||
|
||||
@ -269,7 +269,7 @@ int result = 0;
|
||||
char *home, buf[1024];
|
||||
|
||||
/* Unlink any existing password file before running this test */
|
||||
if ((home = getenv("MAXSCALE_HOME")) == NULL)
|
||||
if ((home = getenv("MAXSCALE_HOME")) == NULL || strlen(home) >= 1024)
|
||||
home = "/usr/local/skysql";
|
||||
sprintf(buf, "%s/etc/passwd", home);
|
||||
if (strcmp(buf, "/etc/passwd") != 0)
|
||||
|
||||
@ -61,7 +61,9 @@ int buflen;
|
||||
ss_info_dassert(0 == GWBUF_EMPTY(buffer), "Buffer should not be empty");
|
||||
ss_info_dassert(GWBUF_IS_TYPE_UNDEFINED(buffer), "Buffer type should be undefined");
|
||||
ss_dfprintf(stderr, "\t..done\nSet a hint for the buffer");
|
||||
hint = hint_create_parameter(NULL, strdup("name"), "value");
|
||||
char* name = strdup("name");
|
||||
hint = hint_create_parameter(NULL, name, "value");
|
||||
free(name);
|
||||
gwbuf_add_hint(buffer, hint);
|
||||
ss_info_dassert(hint == buffer->hint, "Buffer should point to first and only hint");
|
||||
ss_dfprintf(stderr, "\t..done\nSet a property for the buffer");
|
||||
|
||||
@ -158,6 +158,7 @@ static bool do_hashtest(
|
||||
hashtable_free(h);
|
||||
|
||||
return_succp:
|
||||
free(val_arr);
|
||||
return succp;
|
||||
}
|
||||
|
||||
|
||||
@ -46,7 +46,9 @@ HINT *hint;
|
||||
/* Hint tests */
|
||||
ss_dfprintf(stderr,
|
||||
"testhint : Add a parameter hint to a null list");
|
||||
hint = hint_create_parameter(NULL, strdup("name"), "value");
|
||||
char* name = strdup("name");
|
||||
hint = hint_create_parameter(NULL, name, "value");
|
||||
free(name);
|
||||
ss_info_dassert(NULL != hint, "New hint list should not be null");
|
||||
ss_info_dassert(0 == strcmp("value", hint->value), "Hint value should be correct");
|
||||
ss_info_dassert(0 != hint_exists(&hint, HINT_PARAMETER), "Hint of parameter type should exist");
|
||||
|
||||
@ -68,7 +68,8 @@ char *status;
|
||||
server_set_status(server, SERVER_MASTER);
|
||||
status = server_status(server);
|
||||
ss_info_dassert(0 == strcmp("Master, Running", status), "Should find correct status.");
|
||||
server_clear_status(server, SERVER_MASTER);
|
||||
server_clear_status(server, SERVER_MASTER);
|
||||
free(status);
|
||||
status = server_status(server);
|
||||
ss_info_dassert(0 == strcmp("Running", status), "Status of Server should be Running after master status cleared.");
|
||||
if (NULL != status) free(status);
|
||||
@ -78,7 +79,6 @@ char *status;
|
||||
ss_dfprintf(stderr, "\t..done\nFreeing Server.");
|
||||
ss_info_dassert(0 != server_free(server), "Free should succeed");
|
||||
ss_dfprintf(stderr, "\t..done\n");
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
extern int modutil_is_SQL(GWBUF *);
|
||||
extern int modutil_extract_SQL(GWBUF *, char **, int *);
|
||||
extern int modutil_MySQL_Query(GWBUF *, char **, int *, int *);
|
||||
extern char *modutil_get_SQL(GWBUF *);
|
||||
extern GWBUF *modutil_replace_SQL(GWBUF *, char *);
|
||||
extern char *modutil_get_query(GWBUF* buf);
|
||||
extern int modutil_send_mysql_err_packet(DCB *, int, int, int, const char *, const char *);
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
* 23/06/14 Massimiliano Pinto Addition of replicationHeartbeat
|
||||
* 28/08/14 Massimiliano Pinto Addition of detectStaleMaster
|
||||
* 30/10/14 Massimiliano Pinto Addition of disableMasterFailback
|
||||
* 07/11/14 Massimiliano Pinto Addition of setNetworkTimeout
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -71,6 +72,7 @@ typedef struct {
|
||||
void (*defaultUser)(void *, char *, char *);
|
||||
void (*diagnostics)(DCB *, void *);
|
||||
void (*setInterval)(void *, size_t);
|
||||
void (*setNetworkTimeout)(void *, int, int);
|
||||
void (*defaultId)(void *, unsigned long);
|
||||
void (*replicationHeartbeat)(void *, int);
|
||||
void (*detectStaleMaster)(void *, int);
|
||||
@ -98,6 +100,16 @@ typedef enum
|
||||
MONITOR_STATE_FREED = 0x08
|
||||
} monitor_state_t;
|
||||
|
||||
/**
|
||||
* Monitor network timeout types
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
MONITOR_CONNECT_TIMEOUT = 0,
|
||||
MONITOR_READ_TIMEOUT = 1,
|
||||
MONITOR_WRITE_TIMEOUT = 2
|
||||
} monitor_timeouts_t;
|
||||
|
||||
/**
|
||||
* Representation of the running monitor.
|
||||
*/
|
||||
@ -126,4 +138,5 @@ extern void monitorSetInterval (MONITOR *, unsigned long);
|
||||
extern void monitorSetReplicationHeartbeat(MONITOR *, int);
|
||||
extern void monitorDetectStaleMaster(MONITOR *, int);
|
||||
extern void monitorDisableMasterFailback(MONITOR *, int);
|
||||
extern void monitorSetNetworkTimeout(MONITOR *, int, int);
|
||||
#endif
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <filter.h>
|
||||
#include <modinfo.h>
|
||||
#include <modutil.h>
|
||||
@ -170,10 +171,11 @@ int i;
|
||||
|
||||
if ((my_instance = calloc(1, sizeof(QLA_INSTANCE))) != NULL)
|
||||
{
|
||||
if (options)
|
||||
if (options){
|
||||
my_instance->filebase = strdup(options[0]);
|
||||
else
|
||||
}else{
|
||||
my_instance->filebase = strdup("qla");
|
||||
}
|
||||
my_instance->source = NULL;
|
||||
my_instance->userName = NULL;
|
||||
my_instance->match = NULL;
|
||||
@ -196,9 +198,11 @@ int i;
|
||||
my_instance->userName = strdup(params[i]->value);
|
||||
else if (!strcmp(params[i]->name, "filebase"))
|
||||
{
|
||||
if (my_instance->filebase)
|
||||
if (my_instance->filebase){
|
||||
free(my_instance->filebase);
|
||||
my_instance->source = strdup(params[i]->value);
|
||||
my_instance->filebase = NULL;
|
||||
}
|
||||
my_instance->filebase = strdup(params[i]->value);
|
||||
}
|
||||
else if (!filter_standard_parameter(params[i]->name))
|
||||
{
|
||||
@ -219,7 +223,9 @@ int i;
|
||||
my_instance->match)));
|
||||
free(my_instance->match);
|
||||
free(my_instance->source);
|
||||
free(my_instance->filebase);
|
||||
if(my_instance->filebase){
|
||||
free(my_instance->filebase);
|
||||
}
|
||||
free(my_instance);
|
||||
return NULL;
|
||||
}
|
||||
@ -235,7 +241,9 @@ int i;
|
||||
regfree(&my_instance->re);
|
||||
free(my_instance->match);
|
||||
free(my_instance->source);
|
||||
free(my_instance->filebase);
|
||||
if(my_instance->filebase){
|
||||
free(my_instance->filebase);
|
||||
}
|
||||
free(my_instance);
|
||||
return NULL;
|
||||
}
|
||||
@ -265,10 +273,17 @@ char *remote, *userName;
|
||||
(char *)malloc(strlen(my_instance->filebase) + 20))
|
||||
== NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Memory allocation for qla filter "
|
||||
"file name failed due to %d, %s.",
|
||||
errno,
|
||||
strerror(errno))));
|
||||
free(my_session);
|
||||
return NULL;
|
||||
}
|
||||
my_session->active = 1;
|
||||
|
||||
if (my_instance->source
|
||||
&& (remote = session_get_remote(session)) != NULL)
|
||||
{
|
||||
@ -276,16 +291,45 @@ char *remote, *userName;
|
||||
my_session->active = 0;
|
||||
}
|
||||
userName = session_getUser(session);
|
||||
if (my_instance->userName && userName && strcmp(userName,
|
||||
my_instance->userName))
|
||||
|
||||
if (my_instance->userName &&
|
||||
userName &&
|
||||
strcmp(userName,my_instance->userName))
|
||||
{
|
||||
my_session->active = 0;
|
||||
sprintf(my_session->filename, "%s.%d", my_instance->filebase,
|
||||
my_instance->sessions);
|
||||
}
|
||||
sprintf(my_session->filename, "%s.%d",
|
||||
my_instance->filebase,
|
||||
my_instance->sessions);
|
||||
my_instance->sessions++;
|
||||
|
||||
if (my_session->active)
|
||||
{
|
||||
my_session->fp = fopen(my_session->filename, "w");
|
||||
|
||||
if (my_session->fp == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Opening output file for qla "
|
||||
"fileter failed due to %d, %s",
|
||||
errno,
|
||||
strerror(errno))));
|
||||
free(my_session->filename);
|
||||
free(my_session);
|
||||
my_session = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Memory allocation for qla filter failed due to "
|
||||
"%d, %s.",
|
||||
errno,
|
||||
strerror(errno))));
|
||||
}
|
||||
|
||||
return my_session;
|
||||
}
|
||||
|
||||
@ -364,7 +408,7 @@ struct timeval tv;
|
||||
{
|
||||
queue = gwbuf_make_contiguous(queue);
|
||||
}
|
||||
if (modutil_extract_SQL(queue, &ptr, &length) != 0)
|
||||
if ((ptr = modutil_get_SQL(queue)) != NULL)
|
||||
{
|
||||
if ((my_instance->match == NULL ||
|
||||
regexec(&my_instance->re, ptr, 0, NULL, 0) == 0) &&
|
||||
@ -380,6 +424,7 @@ struct timeval tv;
|
||||
fwrite(ptr, sizeof(char), length, my_session->fp);
|
||||
fwrite("\n", sizeof(char), 1, my_session->fp);
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
/* Pass the query downstream */
|
||||
|
||||
@ -60,7 +60,7 @@ static void setDownstream(FILTER *instance, void *fsession, DOWNSTREAM *downstre
|
||||
static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue);
|
||||
static void diagnostic(FILTER *instance, void *fsession, DCB *dcb);
|
||||
|
||||
static char *regex_replace(char *sql, int length, regex_t *re, char *replace);
|
||||
static char *regex_replace(char *sql, regex_t *re, char *replace);
|
||||
|
||||
static FILTER_OBJECT MyObject = {
|
||||
createInstance,
|
||||
@ -192,6 +192,7 @@ int i, cflags = REG_ICASE;
|
||||
|
||||
if (my_instance->match == NULL || my_instance->replace == NULL)
|
||||
{
|
||||
free(my_instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -301,7 +302,6 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
||||
REGEX_INSTANCE *my_instance = (REGEX_INSTANCE *)instance;
|
||||
REGEX_SESSION *my_session = (REGEX_SESSION *)session;
|
||||
char *sql, *newsql;
|
||||
int length;
|
||||
|
||||
if (modutil_is_SQL(queue))
|
||||
{
|
||||
@ -309,18 +309,21 @@ int length;
|
||||
{
|
||||
queue = gwbuf_make_contiguous(queue);
|
||||
}
|
||||
modutil_extract_SQL(queue, &sql, &length);
|
||||
newsql = regex_replace(sql, length, &my_instance->re,
|
||||
my_instance->replace);
|
||||
if (newsql)
|
||||
if ((sql = modutil_get_SQL(queue)) != NULL)
|
||||
{
|
||||
queue = modutil_replace_SQL(queue, newsql);
|
||||
queue = gwbuf_make_contiguous(queue);
|
||||
free(newsql);
|
||||
my_session->replacements++;
|
||||
newsql = regex_replace(sql, &my_instance->re,
|
||||
my_instance->replace);
|
||||
if (newsql)
|
||||
{
|
||||
queue = modutil_replace_SQL(queue, newsql);
|
||||
queue = gwbuf_make_contiguous(queue);
|
||||
free(newsql);
|
||||
my_session->replacements++;
|
||||
}
|
||||
else
|
||||
my_session->no_change++;
|
||||
free(sql);
|
||||
}
|
||||
else
|
||||
my_session->no_change++;
|
||||
|
||||
}
|
||||
return my_session->down.routeQuery(my_session->down.instance,
|
||||
@ -367,25 +370,24 @@ REGEX_SESSION *my_session = (REGEX_SESSION *)fsession;
|
||||
* Perform a regular expression match and subsititution on the SQL
|
||||
*
|
||||
* @param sql The original SQL text
|
||||
* @param length The length of the SQL text
|
||||
* @param re The compiled regular expression
|
||||
* @param replace The replacement text
|
||||
* @return The replaced text or NULL if no replacement was done.
|
||||
*/
|
||||
static char *
|
||||
regex_replace(char *sql, int length, regex_t *re, char *replace)
|
||||
regex_replace(char *sql, regex_t *re, char *replace)
|
||||
{
|
||||
char *orig, *result, *ptr;
|
||||
int i, res_size, res_length, rep_length;
|
||||
int last_match;
|
||||
int last_match, length;
|
||||
regmatch_t match[10];
|
||||
|
||||
orig = strndup(sql, length);
|
||||
if (regexec(re, orig, 10, match, 0))
|
||||
if (regexec(re, sql, 10, match, 0))
|
||||
{
|
||||
free(orig);
|
||||
return NULL;
|
||||
}
|
||||
length = strlen(sql);
|
||||
|
||||
res_size = 2 * length;
|
||||
result = (char *)malloc(res_size);
|
||||
res_length = 0;
|
||||
|
||||
@ -391,7 +391,7 @@ 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;
|
||||
int length, rval, residual = 0;
|
||||
GWBUF *clone = NULL;
|
||||
|
||||
if (my_session->residual)
|
||||
@ -403,16 +403,19 @@ GWBUF *clone = NULL;
|
||||
if (my_session->residual < 0)
|
||||
my_session->residual = 0;
|
||||
}
|
||||
else if (my_session->active &&
|
||||
modutil_MySQL_Query(queue, &ptr, &length, &residual))
|
||||
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))
|
||||
{
|
||||
char *dummy;
|
||||
|
||||
modutil_MySQL_Query(queue, &dummy, &length, &residual);
|
||||
clone = gwbuf_clone(queue);
|
||||
my_session->residual = residual;
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -137,8 +137,8 @@ FILTER_PARAMETER** read_params(int* paramc)
|
||||
do_read = 0;
|
||||
}
|
||||
}
|
||||
FILTER_PARAMETER** params;
|
||||
if((params = malloc(sizeof(FILTER_PARAMETER*)*(pc+1)))!=NULL){
|
||||
FILTER_PARAMETER** params = NULL;
|
||||
if((params = malloc(sizeof(FILTER_PARAMETER*)*(pc+1))) != NULL){
|
||||
for(i = 0;i<pc;i++){
|
||||
params[i] = malloc(sizeof(FILTER_PARAMETER));
|
||||
if(params[i]){
|
||||
@ -147,10 +147,10 @@ FILTER_PARAMETER** read_params(int* paramc)
|
||||
}
|
||||
free(names[i]);
|
||||
free(values[i]);
|
||||
}
|
||||
}
|
||||
params[pc] = NULL;
|
||||
*paramc = pc;
|
||||
}
|
||||
params[pc] = NULL;
|
||||
*paramc = pc;
|
||||
return params;
|
||||
}
|
||||
|
||||
@ -267,83 +267,105 @@ int clientReply(void* ins, void* session, GWBUF* queue)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a string from a file descriptor to a block of memory
|
||||
* @param fd File descriptor to read from, assumed to be open
|
||||
* @param buff Buffer to write to
|
||||
* @param size Size of the buffer
|
||||
* @return Number of bytes read
|
||||
*/
|
||||
int fdgets(int fd, char* buff, int size)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while(i < size - 1 && read(fd,&buff[i],1))
|
||||
{
|
||||
if(buff[i] == '\n' || buff[i] == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
buff[i] = '\0';
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loads a query from a file
|
||||
*@return 0 if successful, 1 if an error occurred
|
||||
*/
|
||||
int load_query()
|
||||
{
|
||||
char** query_list;
|
||||
char* buff;
|
||||
char rc;
|
||||
int i, qcount = 0, qbuff_sz = 10, buff_sz = 2048;
|
||||
char** query_list = NULL;
|
||||
char* buffer = NULL;
|
||||
int i, qcount = 0, qbuff_sz = 10, rval = 0;
|
||||
int offset = 0;
|
||||
unsigned int qlen = 0;
|
||||
|
||||
if((buff = calloc(buff_sz,sizeof(char))) == NULL ||
|
||||
(query_list = calloc(qbuff_sz,sizeof(char*))) == NULL){
|
||||
buffer = (char*)calloc(4092,sizeof(char));
|
||||
if(buffer == NULL){
|
||||
printf("Error: cannot allocate enough memory.\n");
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
while(read(instance.infile,&rc,1)){
|
||||
|
||||
if(rc != '\n' && rc != '\0'){
|
||||
|
||||
if(offset >= buff_sz){
|
||||
char* tmp = malloc(sizeof(char)*2*buff_sz);
|
||||
|
||||
if(tmp){
|
||||
memcpy(tmp,buff,buff_sz);
|
||||
free(buff);
|
||||
buff = tmp;
|
||||
buff_sz *= 2;
|
||||
}else{
|
||||
printf("Error: cannot allocate enough memory.\n");
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n");
|
||||
free(buff);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
buff[offset++] = rc;
|
||||
|
||||
}else{
|
||||
|
||||
|
||||
if(qcount >= qbuff_sz){
|
||||
char** tmpcl = malloc(sizeof(char*) * (qcount * 2 + 1));
|
||||
if(!tmpcl){
|
||||
printf("Error: cannot allocate enough memory.\n");
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n");
|
||||
return 1;
|
||||
}
|
||||
for(i = 0;i < qbuff_sz;i++){
|
||||
tmpcl[i] = query_list[i];
|
||||
}
|
||||
free(query_list);
|
||||
query_list = tmpcl;
|
||||
qbuff_sz = qcount * 2 + 1;
|
||||
}
|
||||
|
||||
query_list[qcount] = malloc(sizeof(char)*(offset + 1));
|
||||
memcpy(query_list[qcount],buff,offset);
|
||||
query_list[qcount][offset] = '\0';
|
||||
offset = 0;
|
||||
qcount++;
|
||||
|
||||
}
|
||||
|
||||
query_list = calloc(qbuff_sz,sizeof(char*));
|
||||
if(query_list == NULL){
|
||||
printf("Error: cannot allocate enough memory.\n");
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n");
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
while((offset = fdgets(instance.infile,buffer,4092))){
|
||||
|
||||
if(qbuff_sz <= qcount){
|
||||
char** tmpbuff = realloc(query_list,sizeof(char*)*qbuff_sz*2);
|
||||
if(tmpbuff == NULL){
|
||||
printf("Error: cannot allocate enough memory.\n");
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n");
|
||||
rval = 1;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
query_list = tmpbuff;
|
||||
qbuff_sz *= 2;
|
||||
|
||||
}
|
||||
|
||||
query_list[qcount] = calloc((offset + 1),sizeof(char));
|
||||
strcpy(query_list[qcount],buffer);
|
||||
offset = 0;
|
||||
qcount++;
|
||||
|
||||
}
|
||||
|
||||
/**TODO check what messes up the first querystring*/
|
||||
GWBUF** tmpbff = malloc(sizeof(GWBUF*)*(qcount + 1));
|
||||
if(tmpbff){
|
||||
for(i = 0;i<qcount;i++){
|
||||
|
||||
tmpbff[i] = gwbuf_alloc(strnlen(query_list[i],buff_sz) + 6);
|
||||
tmpbff[i] = gwbuf_alloc(strlen(query_list[i]) + 6);
|
||||
|
||||
if(tmpbff[i] == NULL)
|
||||
{
|
||||
printf("Error: cannot allocate a new buffer.\n");
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate a new buffer.\n");
|
||||
int x;
|
||||
for(x = 0;x<i;x++)
|
||||
{
|
||||
gwbuf_free(tmpbff[x]);
|
||||
}
|
||||
free(tmpbff);
|
||||
rval = 1;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
gwbuf_set_type(tmpbff[i],GWBUF_TYPE_MYSQL);
|
||||
memcpy(tmpbff[i]->sbuf->data + 5,query_list[i],strnlen(query_list[i],buff_sz));
|
||||
|
||||
qlen = strnlen(query_list[i],buff_sz);
|
||||
strcpy((char*)(tmpbff[i]->start + 5),query_list[i]);
|
||||
qlen = strlen(query_list[i]) + 1;
|
||||
tmpbff[i]->sbuf->data[0] = qlen;
|
||||
tmpbff[i]->sbuf->data[1] = (qlen << 8);
|
||||
tmpbff[i]->sbuf->data[2] = (qlen << 16);
|
||||
@ -357,14 +379,26 @@ int load_query()
|
||||
printf("Error: cannot allocate enough memory for buffers.\n");
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory for buffers.\n");
|
||||
free_buffers();
|
||||
return 1;
|
||||
rval = 1;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if(qcount < 1){
|
||||
return 1;
|
||||
rval = 1;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
instance.buffer_count = qcount;
|
||||
return 0;
|
||||
|
||||
retblock:
|
||||
|
||||
for(i = 0;i<qcount;i++)
|
||||
{
|
||||
free(query_list[i]);
|
||||
}
|
||||
free(query_list);
|
||||
free(buffer);
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
@ -572,7 +606,7 @@ int load_config( char* fname)
|
||||
|
||||
int load_filter(FILTERCHAIN* fc, CONFIG* cnf)
|
||||
{
|
||||
FILTER_PARAMETER** fparams;
|
||||
FILTER_PARAMETER** fparams = NULL;
|
||||
int i, paramc = -1;
|
||||
if(cnf == NULL){
|
||||
|
||||
@ -640,7 +674,7 @@ int load_filter(FILTERCHAIN* fc, CONFIG* cnf)
|
||||
|
||||
int sess_err = 0;
|
||||
|
||||
if(fc && fc->instance){
|
||||
if(cnf && fc && fc->instance){
|
||||
|
||||
|
||||
fc->filter = (FILTER*)fc->instance->createInstance(NULL,fparams);
|
||||
@ -713,15 +747,14 @@ int load_filter(FILTERCHAIN* fc, CONFIG* cnf)
|
||||
|
||||
}
|
||||
|
||||
if(cnf){
|
||||
int x;
|
||||
for(x = 0;x<paramc;x++){
|
||||
free(fparams[x]->name);
|
||||
free(fparams[x]->value);
|
||||
}
|
||||
free(fparams);
|
||||
int x;
|
||||
for(x = 0;x<paramc;x++){
|
||||
free(fparams[x]->name);
|
||||
free(fparams[x]->value);
|
||||
}
|
||||
|
||||
free(fparams);
|
||||
|
||||
return sess_err ? 0 : 1;
|
||||
}
|
||||
|
||||
@ -892,8 +925,9 @@ GWBUF* gen_packet(PACKET pkt)
|
||||
|
||||
int process_opts(int argc, char** argv)
|
||||
{
|
||||
int fd = open_file("harness.cnf",1), buffsize = 1024;
|
||||
int rd,fsize;
|
||||
unsigned int fd = open_file("harness.cnf",1), buffsize = 1024;
|
||||
int rd,rdsz;
|
||||
unsigned int fsize;
|
||||
char *buff = calloc(buffsize,sizeof(char)), *tok = NULL;
|
||||
|
||||
/**Parse 'harness.cnf' file*/
|
||||
@ -901,7 +935,8 @@ int process_opts(int argc, char** argv)
|
||||
lseek(fd,0,SEEK_SET);
|
||||
instance.thrcount = 1;
|
||||
instance.session_count = 1;
|
||||
read(fd,buff,fsize);
|
||||
rdsz = read(fd,buff,fsize);
|
||||
buff[rdsz] = '\0';
|
||||
tok = strtok(buff,"=");
|
||||
while(tok){
|
||||
if(!strcmp(tok,"threads")){
|
||||
@ -920,6 +955,7 @@ int process_opts(int argc, char** argv)
|
||||
instance.verbose = 1;
|
||||
|
||||
if(argc < 2){
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
char* conf_name = NULL;
|
||||
@ -937,6 +973,9 @@ int process_opts(int argc, char** argv)
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
if(conf_name){
|
||||
free(conf_name);
|
||||
}
|
||||
conf_name = strdup(optarg);
|
||||
break;
|
||||
|
||||
@ -992,6 +1031,8 @@ int process_opts(int argc, char** argv)
|
||||
}else{
|
||||
instance.running = 0;
|
||||
}
|
||||
free(conf_name);
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -453,7 +453,6 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
||||
TOPN_INSTANCE *my_instance = (TOPN_INSTANCE *)instance;
|
||||
TOPN_SESSION *my_session = (TOPN_SESSION *)session;
|
||||
char *ptr;
|
||||
int length;
|
||||
|
||||
if (my_session->active)
|
||||
{
|
||||
@ -461,7 +460,7 @@ int length;
|
||||
{
|
||||
queue = gwbuf_make_contiguous(queue);
|
||||
}
|
||||
if (modutil_extract_SQL(queue, &ptr, &length) != 0)
|
||||
if ((ptr = modutil_get_SQL(queue)) != NULL)
|
||||
{
|
||||
if ((my_instance->match == NULL ||
|
||||
regexec(&my_instance->re, ptr, 0, NULL, 0) == 0) &&
|
||||
@ -472,7 +471,11 @@ int length;
|
||||
if (my_session->current)
|
||||
free(my_session->current);
|
||||
gettimeofday(&my_session->start, NULL);
|
||||
my_session->current = strndup(ptr, length);
|
||||
my_session->current = ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +80,8 @@ static MONITOR_OBJECT MyObject = {
|
||||
unregisterServer,
|
||||
defaultUsers,
|
||||
diagnostics,
|
||||
setInterval,
|
||||
setInterval,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
824
server/modules/monitor/mm_mon.c
Normal file
824
server/modules/monitor/mm_mon.c
Normal file
@ -0,0 +1,824 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mysql_mon.c - A MySQL Multi Muster cluster monitor
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 08/09/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.1";
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_MONITOR,
|
||||
MODULE_BETA_RELEASE,
|
||||
MONITOR_VERSION,
|
||||
"A MySQL Multi Master monitor"
|
||||
};
|
||||
|
||||
static void *startMonitor(void *);
|
||||
static void stopMonitor(void *);
|
||||
static void registerServer(void *, SERVER *);
|
||||
static void unregisterServer(void *, SERVER *);
|
||||
static void defaultUser(void *, char *, char *);
|
||||
static void diagnostics(DCB *, void *);
|
||||
static void setInterval(void *, size_t);
|
||||
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 *get_current_master(MYSQL_MONITOR *);
|
||||
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,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
detectStaleMaster,
|
||||
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 Monitor module %s.",
|
||||
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.
|
||||
*
|
||||
* @param arg The current handle - NULL if first start
|
||||
* @return A handle to use when interacting with the monitor
|
||||
*/
|
||||
static void *
|
||||
startMonitor(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle;
|
||||
|
||||
if (arg)
|
||||
{
|
||||
handle = arg; /* Must be a restart */
|
||||
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;
|
||||
handle->replicationHeartbeat = 0;
|
||||
handle->detectStaleMaster = 0;
|
||||
handle->master = NULL;
|
||||
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;
|
||||
db->mon_err_count = 0;
|
||||
db->mon_prev_status = 0;
|
||||
/* pending status is updated by monitorMain */
|
||||
db->pending_status = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
defaultUser(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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Daignostic interface
|
||||
*
|
||||
* @param dcb DCB to print diagnostics
|
||||
* @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,"\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled");
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor an individual server
|
||||
*
|
||||
* @param handle The MySQL Monitor object
|
||||
* @param database The database to probe
|
||||
*/
|
||||
static void
|
||||
monitorDatabase(MYSQL_MONITOR *handle, MONITOR_SERVERS *database)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result;
|
||||
int num_fields;
|
||||
int isslave = 0;
|
||||
int ismaster = 0;
|
||||
char *uname = handle->defaultUser;
|
||||
char *passwd = handle->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 probe servers in maintenance mode */
|
||||
if (SERVER_IN_MAINT(database->server))
|
||||
return;
|
||||
|
||||
/** Store prevous status */
|
||||
database->mon_prev_status = database->server->status;
|
||||
|
||||
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)
|
||||
{
|
||||
free(dpwd);
|
||||
|
||||
if (mon_print_fail_status(database))
|
||||
{
|
||||
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))));
|
||||
}
|
||||
|
||||
/* The current server is not running
|
||||
*
|
||||
* Store server NOT running in server and monitor server pending struct
|
||||
*
|
||||
*/
|
||||
if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR)
|
||||
{
|
||||
server_set_status(database->server, SERVER_AUTH_ERROR);
|
||||
monitor_set_pending_status(database, SERVER_AUTH_ERROR);
|
||||
}
|
||||
server_clear_status(database->server, SERVER_RUNNING);
|
||||
monitor_clear_pending_status(database, SERVER_RUNNING);
|
||||
|
||||
/* Also clear M/S state in both server and monitor server pending struct */
|
||||
server_clear_status(database->server, SERVER_SLAVE);
|
||||
server_clear_status(database->server, SERVER_MASTER);
|
||||
monitor_clear_pending_status(database, SERVER_SLAVE);
|
||||
monitor_clear_pending_status(database, SERVER_MASTER);
|
||||
|
||||
/* Clean addition status too */
|
||||
server_clear_status(database->server, SERVER_STALE_STATUS);
|
||||
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
|
||||
|
||||
return;
|
||||
} else {
|
||||
server_clear_status(database->server, SERVER_AUTH_ERROR);
|
||||
monitor_clear_pending_status(database, SERVER_AUTH_ERROR);
|
||||
}
|
||||
free(dpwd);
|
||||
}
|
||||
/* Store current status in both server and monitor server pending struct */
|
||||
server_set_status(database->server, SERVER_RUNNING);
|
||||
monitor_set_pending_status(database, 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 = realloc(database->server->server_string, strlen(server_string)+1);
|
||||
if (database->server->server_string)
|
||||
strcpy(database->server->server_string, server_string);
|
||||
}
|
||||
|
||||
/* get server_id form current node */
|
||||
if (mysql_query(database->con, "SELECT @@server_id") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
long server_id = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
server_id = strtol(row[0], NULL, 10);
|
||||
if ((errno == ERANGE && (server_id == LONG_MAX
|
||||
|| server_id == LONG_MIN)) || (errno != 0 && server_id == 0))
|
||||
{
|
||||
server_id = -1;
|
||||
}
|
||||
database->server->node_id = server_id;
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
|
||||
/* Check if the Slave_SQL_Running and Slave_IO_Running status is
|
||||
* set to Yes
|
||||
*/
|
||||
|
||||
/* Check first for MariaDB 10.x.x and get status for multimaster replication */
|
||||
if (server_version >= 100000) {
|
||||
|
||||
if (mysql_query(database->con, "SHOW ALL SLAVES STATUS") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
int i = 0;
|
||||
long master_id = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
/* get Slave_IO_Running and Slave_SQL_Running values*/
|
||||
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 */
|
||||
memcpy(&database->server->master_id, &master_id, sizeof(long));
|
||||
|
||||
mysql_free_result(result);
|
||||
|
||||
/* If all configured slaves are running set this node as slave */
|
||||
if (isslave > 0 && isslave == i)
|
||||
isslave = 1;
|
||||
else
|
||||
isslave = 0;
|
||||
}
|
||||
} else {
|
||||
if (mysql_query(database->con, "SHOW SLAVE STATUS") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
long master_id = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
/* get Slave_IO_Running and Slave_SQL_Running values*/
|
||||
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)
|
||||
master_id = -1;
|
||||
}
|
||||
}
|
||||
/* store master_id of current node */
|
||||
memcpy(&database->server->master_id, &master_id, sizeof(long));
|
||||
|
||||
mysql_free_result(result);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* Remove addition info */
|
||||
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
|
||||
|
||||
/* Set the Slave Role */
|
||||
if (isslave)
|
||||
{
|
||||
monitor_set_pending_status(database, SERVER_SLAVE);
|
||||
/* Avoid any possible stale Master state */
|
||||
monitor_clear_pending_status(database, SERVER_MASTER);
|
||||
|
||||
/* Set replication depth to 1 */
|
||||
database->server->depth = 1;
|
||||
} else {
|
||||
/* Avoid any possible Master/Slave stale state */
|
||||
monitor_clear_pending_status(database, SERVER_SLAVE);
|
||||
monitor_clear_pending_status(database, SERVER_MASTER);
|
||||
}
|
||||
|
||||
/* Set the Master role */
|
||||
if (isslave && ismaster)
|
||||
{
|
||||
monitor_clear_pending_status(database, SERVER_SLAVE);
|
||||
monitor_set_pending_status(database, SERVER_MASTER);
|
||||
|
||||
/* Set replication depth to 0 */
|
||||
database->server->depth = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
int detect_stale_master = handle->detectStaleMaster;
|
||||
MONITOR_SERVERS *root_master;
|
||||
size_t nrounds = 0;
|
||||
|
||||
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)
|
||||
{
|
||||
if (handle->shutdown)
|
||||
{
|
||||
handle->status = MONITOR_STOPPING;
|
||||
mysql_thread_end();
|
||||
handle->status = MONITOR_STOPPED;
|
||||
return;
|
||||
}
|
||||
|
||||
/** Wait base interval */
|
||||
thread_millisleep(MON_BASE_INTERVAL_MS);
|
||||
/**
|
||||
* Calculate how far away the monitor interval is from its full
|
||||
* cycle and if monitor interval time further than the base
|
||||
* interval, then skip monitoring checks. Excluding the first
|
||||
* round.
|
||||
*/
|
||||
if (nrounds != 0 &&
|
||||
((nrounds*MON_BASE_INTERVAL_MS)%handle->interval) >=
|
||||
MON_BASE_INTERVAL_MS)
|
||||
{
|
||||
nrounds += 1;
|
||||
continue;
|
||||
}
|
||||
nrounds += 1;
|
||||
|
||||
/* start from the first server in the list */
|
||||
ptr = handle->databases;
|
||||
|
||||
while (ptr)
|
||||
{
|
||||
/* copy server status into monitor pending_status */
|
||||
ptr->pending_status = ptr->server->status;
|
||||
|
||||
/* monitor current node */
|
||||
monitorDatabase(handle, ptr);
|
||||
|
||||
if (mon_status_changed(ptr))
|
||||
{
|
||||
dcb_call_foreach(DCB_REASON_NOT_RESPONDING);
|
||||
}
|
||||
|
||||
if (mon_status_changed(ptr) ||
|
||||
mon_print_fail_status(ptr))
|
||||
{
|
||||
LOGIF(LM, (skygw_log_write_flush(
|
||||
LOGFILE_MESSAGE,
|
||||
"Backend server %s:%d state : %s",
|
||||
ptr->server->name,
|
||||
ptr->server->port,
|
||||
STRSRVSTATUS(ptr->server))));
|
||||
}
|
||||
if (SERVER_IS_DOWN(ptr->server))
|
||||
{
|
||||
/** Increase this server'e error count */
|
||||
ptr->mon_err_count += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Reset this server's error count */
|
||||
ptr->mon_err_count = 0;
|
||||
}
|
||||
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
/* Get Master server pointer */
|
||||
root_master = get_current_master(handle);
|
||||
|
||||
/* Update server status from monitor pending status on that server*/
|
||||
|
||||
ptr = handle->databases;
|
||||
while (ptr)
|
||||
{
|
||||
if (! SERVER_IN_MAINT(ptr->server)) {
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, size_t interval)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
memcpy(&handle->interval, &interval, sizeof(unsigned long));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(
|
||||
MONITOR_SERVERS* mon_srv)
|
||||
{
|
||||
bool succp;
|
||||
|
||||
if (mon_srv->mon_prev_status != mon_srv->server->status)
|
||||
{
|
||||
succp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
|
||||
static bool mon_print_fail_status(
|
||||
MONITOR_SERVERS* mon_srv)
|
||||
{
|
||||
bool succp;
|
||||
int errcount = mon_srv->mon_err_count;
|
||||
uint8_t modval;
|
||||
|
||||
modval = 1<<(MIN(errcount/10, 7));
|
||||
|
||||
if (SERVER_IS_DOWN(mon_srv->server) && errcount%modval == 0)
|
||||
{
|
||||
succp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a pending status bit in the monior server
|
||||
*
|
||||
* @param server The server to update
|
||||
* @param bit The bit to clear for the server
|
||||
*/
|
||||
static void
|
||||
monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit)
|
||||
{
|
||||
ptr->pending_status |= bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a pending status bit in the monior server
|
||||
*
|
||||
* @param server The server to update
|
||||
* @param bit The bit to clear for the server
|
||||
*/
|
||||
static void
|
||||
monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit)
|
||||
{
|
||||
ptr->pending_status &= ~bit;
|
||||
}
|
||||
|
||||
/*******
|
||||
* This function returns the master server
|
||||
* from a set of MySQL Multi Master monitored servers
|
||||
* and returns the root server (that has SERVER_MASTER bit)
|
||||
* The server is returned even for servers in 'maintenance' mode.
|
||||
*
|
||||
* @param handle The monitor handle
|
||||
* @return The server at root level with SERVER_MASTER bit
|
||||
*/
|
||||
|
||||
static MONITOR_SERVERS *get_current_master(MYSQL_MONITOR *handle) {
|
||||
MONITOR_SERVERS *ptr;
|
||||
|
||||
ptr = handle->databases;
|
||||
|
||||
while (ptr)
|
||||
{
|
||||
/* The server could be in SERVER_IN_MAINT
|
||||
* that means SERVER_IS_RUNNING returns 0
|
||||
* Let's check only for SERVER_IS_DOWN: server is not running
|
||||
*/
|
||||
if (SERVER_IS_DOWN(ptr->server)) {
|
||||
ptr = ptr->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ptr->server->depth == 0) {
|
||||
handle->master = ptr;
|
||||
}
|
||||
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the root master
|
||||
*/
|
||||
|
||||
if (handle->master != NULL) {
|
||||
/* If the root master is in MAINT, return NULL */
|
||||
if (SERVER_IN_MAINT(handle->master->server)) {
|
||||
return NULL;
|
||||
} else {
|
||||
return handle->master;
|
||||
}
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,6 +84,7 @@ static void setInterval(void *, size_t);
|
||||
static void defaultId(void *, unsigned long);
|
||||
static void replicationHeartbeat(void *, int);
|
||||
static void detectStaleMaster(void *, int);
|
||||
static void setNetworkTimeout(void *, int, 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);
|
||||
@ -103,6 +104,7 @@ static MONITOR_OBJECT MyObject = {
|
||||
defaultUser,
|
||||
diagnostics,
|
||||
setInterval,
|
||||
NULL,
|
||||
defaultId,
|
||||
replicationHeartbeat,
|
||||
detectStaleMaster,
|
||||
@ -1204,3 +1206,15 @@ monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit)
|
||||
{
|
||||
ptr->pending_status &= ~bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default id to use in the monitor.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param id The id to set in monitor struct
|
||||
*/
|
||||
static void
|
||||
setNetworkTimeout(void *arg, int type, int value)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
}
|
||||
|
||||
@ -74,6 +74,7 @@ static MONITOR_OBJECT MyObject = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
@ -270,6 +270,7 @@ int n_connect = 0;
|
||||
if (client_dcb == NULL)
|
||||
|
||||
{
|
||||
close(so);
|
||||
return n_connect;
|
||||
}
|
||||
client_dcb->fd = so;
|
||||
|
||||
@ -377,7 +377,7 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
dcb,
|
||||
ERRACT_REPLY_CLIENT,
|
||||
&succp);
|
||||
|
||||
gwbuf_free(errbuf);
|
||||
ss_dassert(!succp);
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
@ -453,13 +453,15 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
0,
|
||||
"Read from backend failed");
|
||||
|
||||
router->handleError(router_instance,
|
||||
session->router_session,
|
||||
errbuf,
|
||||
dcb,
|
||||
ERRACT_NEW_CONNECTION,
|
||||
&succp);
|
||||
|
||||
router->handleError(
|
||||
router_instance,
|
||||
session->router_session,
|
||||
errbuf,
|
||||
dcb,
|
||||
ERRACT_NEW_CONNECTION,
|
||||
&succp);
|
||||
gwbuf_free(errbuf);
|
||||
|
||||
if (!succp)
|
||||
{
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
@ -848,7 +850,8 @@ static int gw_error_backend_event(DCB *dcb)
|
||||
dcb,
|
||||
ERRACT_NEW_CONNECTION,
|
||||
&succp);
|
||||
|
||||
gwbuf_free(errbuf);
|
||||
|
||||
/** There are not required backends available, close session. */
|
||||
if (!succp) {
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
@ -1031,7 +1034,8 @@ gw_backend_hangup(DCB *dcb)
|
||||
ERRACT_NEW_CONNECTION,
|
||||
&succp);
|
||||
|
||||
/** There are not required backends available, close session. */
|
||||
gwbuf_free(errbuf);
|
||||
/** There are no required backends available, close session. */
|
||||
if (!succp)
|
||||
{
|
||||
#if defined(SS_DEBUG)
|
||||
@ -1039,7 +1043,6 @@ gw_backend_hangup(DCB *dcb)
|
||||
LOGFILE_ERROR,
|
||||
"Backend hangup -> closing session.")));
|
||||
#endif
|
||||
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
session->state = SESSION_STATE_STOPPING;
|
||||
spinlock_release(&session->ses_lock);
|
||||
@ -1168,7 +1171,7 @@ static int backend_write_delayqueue(DCB *dcb)
|
||||
0,
|
||||
"Failed to write buffered data to back-end server. "
|
||||
"Buffer was empty or back-end was disconnected during "
|
||||
"operation. Session will be closed.");
|
||||
"operation. Attempting to find a new backend.");
|
||||
|
||||
router->handleError(router_instance,
|
||||
rsession,
|
||||
@ -1176,7 +1179,8 @@ static int backend_write_delayqueue(DCB *dcb)
|
||||
dcb,
|
||||
ERRACT_NEW_CONNECTION,
|
||||
&succp);
|
||||
|
||||
gwbuf_free(errbuf);
|
||||
|
||||
if (!succp)
|
||||
{
|
||||
if (session != NULL)
|
||||
@ -1327,7 +1331,7 @@ static int gw_change_user(
|
||||
/** Create an incoming event for backend DCB */
|
||||
poll_add_epollin_event_to_dcb(backend, gwbuf_clone(buf));
|
||||
gwbuf_free(buf);
|
||||
rv = 0;
|
||||
rv = 1;
|
||||
} else {
|
||||
rv = gw_send_change_user_to_backend(database, username, client_sha1, backend_protocol);
|
||||
/*
|
||||
|
||||
@ -430,7 +430,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
||||
|
||||
connect_with_db =
|
||||
GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB & gw_mysql_get_byte4(
|
||||
&protocol->client_capabilities);
|
||||
(uint32_t *)&protocol->client_capabilities);
|
||||
/*
|
||||
compress =
|
||||
GW_MYSQL_CAPABILITIES_COMPRESS & gw_mysql_get_byte4(
|
||||
@ -859,6 +859,7 @@ int gw_read_client_event(
|
||||
dcb,
|
||||
ERRACT_REPLY_CLIENT,
|
||||
&succp);
|
||||
gwbuf_free(errbuf);
|
||||
ss_dassert(!succp);
|
||||
|
||||
dcb_close(dcb);
|
||||
@ -1017,7 +1018,7 @@ int gw_MySQLListener(
|
||||
errno,
|
||||
strerror(errno));
|
||||
fprintf(stderr, "* Can't bind to %s\n\n", config_bind);
|
||||
|
||||
close(l_so);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1039,13 +1040,14 @@ int gw_MySQLListener(
|
||||
errno,
|
||||
strerror(errno));
|
||||
fprintf(stderr, "* Can't bind to %s\n\n", config_bind);
|
||||
|
||||
close(l_so);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "* Socket Family %i not supported\n", current_addr->sa_family);
|
||||
close(l_so);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -298,6 +298,7 @@ int n_connect = 0;
|
||||
if (client_dcb == NULL)
|
||||
|
||||
{
|
||||
close(so);
|
||||
return n_connect;
|
||||
}
|
||||
client_dcb->fd = so;
|
||||
|
||||
@ -16,3 +16,6 @@ install(TARGETS cli DESTINATION modules)
|
||||
|
||||
add_subdirectory(readwritesplit)
|
||||
|
||||
if(BUILD_TESTS)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
@ -631,7 +631,7 @@ char *ptr, *lptr;
|
||||
else
|
||||
{
|
||||
i++;
|
||||
if (i >= MAXARGS)
|
||||
if (i >= MAXARGS-1)
|
||||
break;
|
||||
args[i] = ptr + 1;
|
||||
}
|
||||
|
||||
@ -700,8 +700,8 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
rc = 0;
|
||||
goto return_rc;
|
||||
}
|
||||
|
||||
switch(mysql_command) {
|
||||
|
||||
switch(mysql_command) {
|
||||
case MYSQL_COM_CHANGE_USER:
|
||||
rc = backend_dcb->func.auth(
|
||||
backend_dcb,
|
||||
@ -714,7 +714,7 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
rc = backend_dcb->func.write(backend_dcb, queue);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
CHK_PROTOCOL(((MySQLProtocol*)backend_dcb->protocol));
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
@ -815,21 +815,33 @@ clientReply(
|
||||
* @param action The action: REPLY, REPLY_AND_CLOSE, NEW_CONNECTION
|
||||
*
|
||||
*/
|
||||
static void
|
||||
handleError(
|
||||
ROUTER *instance,
|
||||
void *router_session,
|
||||
GWBUF *errbuf,
|
||||
DCB *backend_dcb,
|
||||
error_action_t action,
|
||||
bool *succp)
|
||||
static void handleError(
|
||||
ROUTER *instance,
|
||||
void *router_session,
|
||||
GWBUF *errbuf,
|
||||
DCB *backend_dcb,
|
||||
error_action_t action,
|
||||
bool *succp)
|
||||
|
||||
{
|
||||
DCB *client = NULL;
|
||||
DCB *client_dcb;
|
||||
SESSION *session = backend_dcb->session;
|
||||
client = session->client;
|
||||
session_state_t sesstate;
|
||||
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
sesstate = session->state;
|
||||
client_dcb = session->client;
|
||||
spinlock_release(&session->ses_lock);
|
||||
ss_dassert(client_dcb != NULL);
|
||||
|
||||
if (sesstate == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
CHK_DCB(client_dcb);
|
||||
client_dcb->func.write(client_dcb, gwbuf_clone(errbuf));
|
||||
}
|
||||
|
||||
/** false because connection is not available anymore */
|
||||
*succp = false;
|
||||
ss_dassert(client != NULL);
|
||||
}
|
||||
|
||||
/** to be inline'd */
|
||||
|
||||
@ -1356,7 +1356,7 @@ static route_target_t get_route_target (
|
||||
hint = hint->next;
|
||||
} /*< while (hint != NULL) */
|
||||
/** If nothing matches then choose the master */
|
||||
if ((target & (TARGET_ALL|TARGET_SLAVE|TARGET_MASTER)) == target)
|
||||
if ((target & (TARGET_ALL|TARGET_SLAVE|TARGET_MASTER)) == 0)
|
||||
{
|
||||
target = TARGET_MASTER;
|
||||
}
|
||||
@ -1406,7 +1406,7 @@ void check_drop_tmp_table(
|
||||
{
|
||||
|
||||
int tsize = 0, klen = 0,i;
|
||||
char** tbl;
|
||||
char** tbl = NULL;
|
||||
char *hkey,*dbname;
|
||||
MYSQL_session* data;
|
||||
|
||||
@ -1425,29 +1425,31 @@ void check_drop_tmp_table(
|
||||
if (is_drop_table_query(querybuf))
|
||||
{
|
||||
tbl = skygw_get_table_names(querybuf,&tsize,false);
|
||||
|
||||
for(i = 0; i<tsize; i++)
|
||||
{
|
||||
klen = strlen(dbname) + strlen(tbl[i]) + 2;
|
||||
hkey = calloc(klen,sizeof(char));
|
||||
strcpy(hkey,dbname);
|
||||
strcat(hkey,".");
|
||||
strcat(hkey,tbl[i]);
|
||||
if(tbl != NULL){
|
||||
for(i = 0; i<tsize; i++)
|
||||
{
|
||||
klen = strlen(dbname) + strlen(tbl[i]) + 2;
|
||||
hkey = calloc(klen,sizeof(char));
|
||||
strcpy(hkey,dbname);
|
||||
strcat(hkey,".");
|
||||
strcat(hkey,tbl[i]);
|
||||
|
||||
if (rses_prop_tmp &&
|
||||
rses_prop_tmp->rses_prop_data.temp_tables)
|
||||
{
|
||||
if (hashtable_delete(rses_prop_tmp->rses_prop_data.temp_tables,
|
||||
(void *)hkey))
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(LOGFILE_TRACE,
|
||||
"Temporary table dropped: %s",hkey)));
|
||||
}
|
||||
}
|
||||
free(tbl[i]);
|
||||
free(hkey);
|
||||
}
|
||||
free(tbl);
|
||||
if (rses_prop_tmp &&
|
||||
rses_prop_tmp->rses_prop_data.temp_tables)
|
||||
{
|
||||
if (hashtable_delete(rses_prop_tmp->rses_prop_data.temp_tables,
|
||||
(void *)hkey))
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(LOGFILE_TRACE,
|
||||
"Temporary table dropped: %s",hkey)));
|
||||
}
|
||||
}
|
||||
free(tbl[i]);
|
||||
free(hkey);
|
||||
}
|
||||
|
||||
free(tbl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1468,7 +1470,7 @@ skygw_query_type_t is_read_tmp_table(
|
||||
|
||||
bool target_tmp_table = false;
|
||||
int tsize = 0, klen = 0,i;
|
||||
char** tbl;
|
||||
char** tbl = NULL;
|
||||
char *hkey,*dbname;
|
||||
MYSQL_session* data;
|
||||
|
||||
@ -1493,7 +1495,7 @@ skygw_query_type_t is_read_tmp_table(
|
||||
{
|
||||
tbl = skygw_get_table_names(querybuf,&tsize,false);
|
||||
|
||||
if (tsize > 0)
|
||||
if (tbl != NULL && tsize > 0)
|
||||
{
|
||||
/** Query targets at least one table */
|
||||
for(i = 0; i<tsize && !target_tmp_table && tbl[i]; i++)
|
||||
@ -1520,14 +1522,21 @@ skygw_query_type_t is_read_tmp_table(
|
||||
}
|
||||
|
||||
free(hkey);
|
||||
free(tbl[i]);
|
||||
}
|
||||
|
||||
free(tbl);
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i<tsize;i++)
|
||||
{
|
||||
free(tbl[i]);
|
||||
}
|
||||
|
||||
if(tbl != NULL){
|
||||
free(tbl);
|
||||
}
|
||||
|
||||
return qtype;
|
||||
return qtype;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1535,7 +1544,6 @@ skygw_query_type_t is_read_tmp_table(
|
||||
* the database and table name, create a hashvalue and
|
||||
* add it to the router client session's property. If property
|
||||
* doesn't exist then create it first.
|
||||
*
|
||||
* @param instance Router instance
|
||||
* @param router_session Router client session
|
||||
* @param querybuf GWBUF containing the query
|
||||
@ -1711,27 +1719,41 @@ static int routeQuery(
|
||||
LOGIF(LE,
|
||||
(skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error: Failed to route %s:%s:\"%s\" to "
|
||||
"backend server. %s.",
|
||||
"Error: Can't route %s:%s:\"%s\" to "
|
||||
"backend server. Router is closed.",
|
||||
STRPACKETTYPE(packet_type),
|
||||
STRQTYPE(qtype),
|
||||
(query_str == NULL ? "(empty)" : query_str),
|
||||
(rses_is_closed ? "Router was closed" :
|
||||
"Router has no backend servers where to "
|
||||
"route to"))));
|
||||
(query_str == NULL ? "(empty)" : query_str))));
|
||||
free(query_str);
|
||||
}
|
||||
ret = 0;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
/** Read stored master DCB pointer */
|
||||
if ((master_dcb = router_cli_ses->rses_master_ref->bref_dcb) == NULL)
|
||||
{
|
||||
char* query_str = modutil_get_query(querybuf);
|
||||
CHK_DCB(master_dcb);
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error: Can't route %s:%s:\"%s\" to "
|
||||
"backend server. Session doesn't have a Master "
|
||||
"node",
|
||||
STRPACKETTYPE(packet_type),
|
||||
STRQTYPE(qtype),
|
||||
(query_str == NULL ? "(empty)" : query_str))));
|
||||
free(query_str);
|
||||
ret = 0;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
/** If buffer is not contiguous, make it such */
|
||||
if (querybuf->next != NULL)
|
||||
{
|
||||
querybuf = gwbuf_make_contiguous(querybuf);
|
||||
}
|
||||
|
||||
/** Read stored master DCB pointer */
|
||||
master_dcb = router_cli_ses->rses_master_ref->bref_dcb;
|
||||
CHK_DCB(master_dcb);
|
||||
|
||||
|
||||
switch(packet_type) {
|
||||
case MYSQL_COM_QUIT: /*< 1 QUIT will close all sessions */
|
||||
case MYSQL_COM_INIT_DB: /*< 2 DDL must go to the master */
|
||||
@ -1891,6 +1913,10 @@ static int routeQuery(
|
||||
/** Lock router session */
|
||||
if (!rses_begin_locked_router_action(router_cli_ses))
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Route query aborted! Routing session is closed <")));
|
||||
ret = 0;
|
||||
goto retblock;
|
||||
}
|
||||
/**
|
||||
@ -2023,13 +2049,13 @@ static int routeQuery(
|
||||
NULL,
|
||||
MAX_RLAG_UNDEFINED);
|
||||
|
||||
if (succp && (master_dcb == NULL || master_dcb == curr_master_dcb))
|
||||
if (succp && master_dcb == curr_master_dcb)
|
||||
{
|
||||
atomic_add(&inst->stats.n_master, 1);
|
||||
target_dcb = master_dcb;
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
if (succp && master_dcb != curr_master_dcb)
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(LOGFILE_TRACE,
|
||||
@ -2050,9 +2076,9 @@ static int routeQuery(
|
||||
}
|
||||
succp = false;
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (succp) /*< Have DCB of the target backend */
|
||||
{
|
||||
backend_ref_t* bref;
|
||||
@ -2631,7 +2657,12 @@ static bool select_connect_backend_servers(
|
||||
/* get the root Master */
|
||||
master_host = get_root_master(backend_ref, router_nservers);
|
||||
|
||||
/** Master is already chosen and connected. This is slave failure case */
|
||||
/**
|
||||
* Master is already chosen and connected. It means that the function
|
||||
* was called from error handling function or from some other similar
|
||||
* function where session was already established but new slaves needed
|
||||
* to be selected.
|
||||
*/
|
||||
if (*p_master_ref != NULL &&
|
||||
BREF_IS_IN_USE((*p_master_ref)))
|
||||
{
|
||||
@ -4023,7 +4054,7 @@ static void rwsplit_process_router_options(
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param router_session The router session
|
||||
* @param message The error message to reply
|
||||
* @param errmsgbuf The error message to reply
|
||||
* @param backend_dcb The backend DCB
|
||||
* @param action The action: REPLY, REPLY_AND_CLOSE, NEW_CONNECTION
|
||||
* @param succp Result of action.
|
||||
@ -4065,6 +4096,22 @@ static void handleError (
|
||||
return;
|
||||
}
|
||||
|
||||
if (rses->rses_master_ref->bref_dcb == backend_dcb)
|
||||
{
|
||||
/** Master failed, can't recover */
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Master node have failed. "
|
||||
"Session will be closed.")));
|
||||
|
||||
*succp = false;
|
||||
rses_end_locked_router_action(rses);
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* This is called in hope of getting replacement for
|
||||
* failed slave(s).
|
||||
*/
|
||||
*succp = handle_error_new_connection(inst,
|
||||
rses,
|
||||
backend_dcb,
|
||||
@ -4088,114 +4135,115 @@ static void handleError (
|
||||
|
||||
|
||||
static void handle_error_reply_client(
|
||||
SESSION* ses,
|
||||
GWBUF* errmsg)
|
||||
SESSION* ses,
|
||||
GWBUF* errmsg)
|
||||
{
|
||||
session_state_t sesstate;
|
||||
DCB* client_dcb;
|
||||
|
||||
spinlock_acquire(&ses->ses_lock);
|
||||
sesstate = ses->state;
|
||||
client_dcb = ses->client;
|
||||
spinlock_release(&ses->ses_lock);
|
||||
|
||||
if (sesstate == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
CHK_DCB(client_dcb);
|
||||
client_dcb->func.write(client_dcb, errmsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((errmsg=gwbuf_consume(errmsg, GWBUF_LENGTH(errmsg))) != NULL)
|
||||
;
|
||||
}
|
||||
session_state_t sesstate;
|
||||
DCB* client_dcb;
|
||||
|
||||
spinlock_acquire(&ses->ses_lock);
|
||||
sesstate = ses->state;
|
||||
client_dcb = ses->client;
|
||||
spinlock_release(&ses->ses_lock);
|
||||
|
||||
if (sesstate == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
CHK_DCB(client_dcb);
|
||||
client_dcb->func.write(client_dcb, gwbuf_clone(errmsg));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This must be called with router lock
|
||||
* Check if there is backend reference pointing at failed DCB, and reset its
|
||||
* flags. Then clear DCB's callback and finally : try to find replacement(s)
|
||||
* for failed slave(s).
|
||||
*
|
||||
* This must be called with router lock.
|
||||
*
|
||||
* @param inst router instance
|
||||
* @param rses router client session
|
||||
* @param dcb failed DCB
|
||||
* @param errmsg error message which is sent to client if it is waiting
|
||||
*
|
||||
* @return true if there are enough backend connections to continue, false if not
|
||||
*/
|
||||
static bool handle_error_new_connection(
|
||||
ROUTER_INSTANCE* inst,
|
||||
ROUTER_CLIENT_SES* rses,
|
||||
DCB* backend_dcb,
|
||||
GWBUF* errmsg)
|
||||
ROUTER_INSTANCE* inst,
|
||||
ROUTER_CLIENT_SES* rses,
|
||||
DCB* backend_dcb,
|
||||
GWBUF* errmsg)
|
||||
{
|
||||
SESSION* ses;
|
||||
int router_nservers;
|
||||
int max_nslaves;
|
||||
int max_slave_rlag;
|
||||
backend_ref_t* bref;
|
||||
bool succp;
|
||||
|
||||
ss_dassert(SPINLOCK_IS_LOCKED(&rses->rses_lock));
|
||||
|
||||
ses = backend_dcb->session;
|
||||
CHK_SESSION(ses);
|
||||
|
||||
bref = get_bref_from_dcb(rses, backend_dcb);
|
||||
|
||||
/** failed DCB has already been replaced */
|
||||
if (bref == NULL)
|
||||
{
|
||||
succp = true;
|
||||
goto return_succp;
|
||||
}
|
||||
/**
|
||||
* Error handler is already called for this DCB because
|
||||
* it's not polling anymore. It can be assumed that
|
||||
* it succeed because rses isn't closed.
|
||||
*/
|
||||
if (backend_dcb->state != DCB_STATE_POLLING)
|
||||
{
|
||||
succp = true;
|
||||
goto return_succp;
|
||||
}
|
||||
|
||||
CHK_BACKEND_REF(bref);
|
||||
|
||||
if (BREF_IS_WAITING_RESULT(bref))
|
||||
{
|
||||
DCB* client_dcb;
|
||||
client_dcb = ses->client;
|
||||
client_dcb->func.write(client_dcb, errmsg);
|
||||
bref_clear_state(bref, BREF_WAITING_RESULT);
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((errmsg=gwbuf_consume(errmsg, GWBUF_LENGTH(errmsg))) != NULL)
|
||||
;
|
||||
}
|
||||
bref_clear_state(bref, BREF_IN_USE);
|
||||
bref_set_state(bref, BREF_CLOSED);
|
||||
/**
|
||||
* Remove callback because this DCB won't be used
|
||||
* unless it is reconnected later, and then the callback
|
||||
* is set again.
|
||||
*/
|
||||
dcb_remove_callback(backend_dcb,
|
||||
DCB_REASON_NOT_RESPONDING,
|
||||
&router_handle_state_switch,
|
||||
(void *)bref);
|
||||
|
||||
router_nservers = router_get_servercount(inst);
|
||||
max_nslaves = rses_get_max_slavecount(rses, router_nservers);
|
||||
max_slave_rlag = rses_get_max_replication_lag(rses);
|
||||
/**
|
||||
* Try to get replacement slave or at least the minimum
|
||||
* number of slave connections for router session.
|
||||
*/
|
||||
succp = select_connect_backend_servers(
|
||||
&rses->rses_master_ref,
|
||||
rses->rses_backend_ref,
|
||||
router_nservers,
|
||||
max_nslaves,
|
||||
max_slave_rlag,
|
||||
rses->rses_config.rw_slave_select_criteria,
|
||||
ses,
|
||||
inst);
|
||||
SESSION* ses;
|
||||
int router_nservers;
|
||||
int max_nslaves;
|
||||
int max_slave_rlag;
|
||||
backend_ref_t* bref;
|
||||
bool succp;
|
||||
|
||||
ss_dassert(SPINLOCK_IS_LOCKED(&rses->rses_lock));
|
||||
|
||||
ses = backend_dcb->session;
|
||||
CHK_SESSION(ses);
|
||||
|
||||
/**
|
||||
* If bref == NULL it has been replaced already with another one.
|
||||
*/
|
||||
if ((bref = get_bref_from_dcb(rses, backend_dcb)) == NULL)
|
||||
{
|
||||
succp = true;
|
||||
goto return_succp;
|
||||
}
|
||||
CHK_BACKEND_REF(bref);
|
||||
|
||||
if (BREF_IS_WAITING_RESULT(bref))
|
||||
{
|
||||
DCB* client_dcb;
|
||||
client_dcb = ses->client;
|
||||
client_dcb->func.write(client_dcb, gwbuf_clone(errmsg));
|
||||
bref_clear_state(bref, BREF_WAITING_RESULT);
|
||||
}
|
||||
bref_clear_state(bref, BREF_IN_USE);
|
||||
bref_set_state(bref, BREF_CLOSED);
|
||||
|
||||
/**
|
||||
* Error handler is already called for this DCB because
|
||||
* it's not polling anymore. It can be assumed that
|
||||
* it succeed because rses isn't closed.
|
||||
*/
|
||||
if (backend_dcb->state != DCB_STATE_POLLING)
|
||||
{
|
||||
succp = true;
|
||||
goto return_succp;
|
||||
}
|
||||
/**
|
||||
* Remove callback because this DCB won't be used
|
||||
* unless it is reconnected later, and then the callback
|
||||
* is set again.
|
||||
*/
|
||||
dcb_remove_callback(backend_dcb,
|
||||
DCB_REASON_NOT_RESPONDING,
|
||||
&router_handle_state_switch,
|
||||
(void *)bref);
|
||||
|
||||
router_nservers = router_get_servercount(inst);
|
||||
max_nslaves = rses_get_max_slavecount(rses, router_nservers);
|
||||
max_slave_rlag = rses_get_max_replication_lag(rses);
|
||||
/**
|
||||
* Try to get replacement slave or at least the minimum
|
||||
* number of slave connections for router session.
|
||||
*/
|
||||
succp = select_connect_backend_servers(
|
||||
&rses->rses_master_ref,
|
||||
rses->rses_backend_ref,
|
||||
router_nservers,
|
||||
max_nslaves,
|
||||
max_slave_rlag,
|
||||
rses->rses_config.rw_slave_select_criteria,
|
||||
ses,
|
||||
inst);
|
||||
|
||||
return_succp:
|
||||
return succp;
|
||||
return succp;
|
||||
}
|
||||
|
||||
|
||||
@ -4291,8 +4339,8 @@ static bool have_enough_servers(
|
||||
}
|
||||
else
|
||||
{
|
||||
double pct = (*p_rses)->rses_config.rw_max_slave_conn_percent/100;
|
||||
double nservers = (double)router_nsrv*pct;
|
||||
int pct = (*p_rses)->rses_config.rw_max_slave_conn_percent/100;
|
||||
int nservers = router_nsrv*pct;
|
||||
|
||||
if ((*p_rses)->rses_config.rw_max_slave_conn_count < min_nsrv)
|
||||
{
|
||||
@ -4311,11 +4359,11 @@ static bool have_enough_servers(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unable to start %s service. There are "
|
||||
"too few backend servers configured in "
|
||||
"MaxScale.cnf. Found %d%% when at least %.0f%% "
|
||||
"MaxScale.cnf. Found %d%% when at least %d%% "
|
||||
"would be required.",
|
||||
router->service->name,
|
||||
(*p_rses)->rses_config.rw_max_slave_conn_percent,
|
||||
min_nsrv/(((double)router_nsrv)/100))));
|
||||
min_nsrv/(router_nsrv/100))));
|
||||
}
|
||||
}
|
||||
free(*p_rses);
|
||||
@ -4378,7 +4426,14 @@ static int rses_get_max_replication_lag(
|
||||
return conf_max_rlag;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds out if there is a backend reference pointing at the DCB given as
|
||||
* parameter.
|
||||
* @param rses router client session
|
||||
* @param dcb DCB
|
||||
*
|
||||
* @return backend reference pointer if succeed or NULL
|
||||
*/
|
||||
static backend_ref_t* get_bref_from_dcb(
|
||||
ROUTER_CLIENT_SES* rses,
|
||||
DCB* dcb)
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
add_test(NAME ReadWriteSplitTest COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/rwsplit.sh testrwsplit.log ${TEST_HOST} ${TEST_PORT_RW} ${TEST_MASTER_ID} ${TEST_USER} ${TEST_PASSWORD} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_test(NAME ReadWriteSplitLoginTest COMMAND $<TARGET_FILE:testconnect> 10000 ${TEST_HOST} ${MASTER_PORT} ${TEST_HOST} ${TEST_PORT_RW})
|
||||
add_subdirectory(test_hints)
|
||||
3
server/modules/routing/test/CMakeLists.txt
Normal file
3
server/modules/routing/test/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
add_executable(testconnect testconnect.c)
|
||||
target_link_libraries(testconnect mysql)
|
||||
add_test(NAME ReadConnRouterLoginTest COMMAND $<TARGET_FILE:testconnect> 10000 ${TEST_HOST} ${MASTER_PORT} ${TEST_HOST} ${TEST_PORT})
|
||||
79
server/modules/routing/test/testconnect.c
Normal file
79
server/modules/routing/test/testconnect.c
Normal file
@ -0,0 +1,79 @@
|
||||
#include <my_config.h>
|
||||
#include <mysql.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
||||
MYSQL* server;
|
||||
char *host;
|
||||
unsigned int port;
|
||||
int rval, iterations,i;
|
||||
clock_t begin,end;
|
||||
double baseline,test;
|
||||
|
||||
if(argc < 6){
|
||||
fprintf(stderr,"Usage: %s <iterations> <baseline host> <baseline port> <test host> <test port>\n",argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
iterations = atoi(argv[1]);
|
||||
host = strdup(argv[2]);
|
||||
port = atoi(argv[3]);
|
||||
rval = 0;
|
||||
|
||||
/**Testing direct connection to master*/
|
||||
|
||||
printf("Connecting to MySQL server through %s:%d.\n",host,port);
|
||||
begin = clock();
|
||||
|
||||
for(i = 0;i<iterations;i++)
|
||||
{
|
||||
if((server = mysql_init(NULL)) == NULL){
|
||||
return 1;
|
||||
}
|
||||
if(mysql_real_connect(server,host,"maxuser","maxpwd",NULL,port,NULL,0) == NULL){
|
||||
fprintf(stderr, "Failed to connect to database: Error: %s\n",
|
||||
mysql_error(server));
|
||||
rval = 1;
|
||||
break;
|
||||
}
|
||||
mysql_close(server);
|
||||
}
|
||||
|
||||
end = clock();
|
||||
baseline = (double)(end - begin)/CLOCKS_PER_SEC;
|
||||
|
||||
free(host);
|
||||
host = strdup(argv[4]);
|
||||
port = atoi(argv[5]);
|
||||
|
||||
/**Testing connection to master through MaxScale*/
|
||||
|
||||
printf("Connecting to MySQL server through %s:%d.\n",host,port);
|
||||
begin = clock();
|
||||
|
||||
for(i = 0;i<iterations;i++)
|
||||
{
|
||||
if((server = mysql_init(NULL)) == NULL){
|
||||
return 1;
|
||||
}
|
||||
if(mysql_real_connect(server,host,"maxuser","maxpwd",NULL,port,NULL,0) == NULL){
|
||||
rval = 1;
|
||||
fprintf(stderr, "Failed to connect to database: Error: %s\n",
|
||||
mysql_error(server));
|
||||
break;
|
||||
}
|
||||
mysql_close(server);
|
||||
}
|
||||
|
||||
end = clock();
|
||||
test = (double)(end - begin)/CLOCKS_PER_SEC;
|
||||
|
||||
printf("CPU time used in seconds:\nDirect connection: %f\nThrough MaxScale: %f\n",baseline,test);
|
||||
|
||||
free(host);
|
||||
return rval;
|
||||
}
|
||||
Reference in New Issue
Block a user