This commit is contained in:
VilhoRaatikka
2014-11-07 17:57:06 +02:00
22 changed files with 1092 additions and 82 deletions

View File

@ -45,8 +45,9 @@ int main(int argc, char** argv)
} }
block_size = atoi(argv[3]); block_size = atoi(argv[3]);
if(block_size < 1){ if(block_size < 1 || block_size > 1024){
fprintf(stderr,"Message size too small, must be at least 1 byte long."); fprintf(stderr,"Message size too small or large, must be at least 1 byte long and must not exceed 1024 bytes.");
return 1;
} }

View File

@ -27,7 +27,7 @@ macro(set_variables)
set(TEST_HOST "127.0.0.1" CACHE STRING "hostname or IP address of MaxScale's host") set(TEST_HOST "127.0.0.1" CACHE STRING "hostname or IP address of MaxScale's host")
# port of read connection router module # port of read connection router module
set(TEST_PORT_RW "4008" CACHE STRING "port of read connection router module") set(TEST_PORT "4008" CACHE STRING "port of read connection router module")
# port of read/write split router module # port of read/write split router module
set(TEST_PORT_RW "4006" CACHE STRING "port of read/write split router module") set(TEST_PORT_RW "4006" CACHE STRING "port of read/write split router module")
@ -38,6 +38,9 @@ macro(set_variables)
# master test server server_id # master test server server_id
set(TEST_MASTER_ID "3000" CACHE STRING "master test server server_id") set(TEST_MASTER_ID "3000" CACHE STRING "master test server server_id")
# master test server port
set(MASTER_PORT "3000" CACHE STRING "master test server port")
# username of MaxScale user # username of MaxScale user
set(TEST_USER "maxuser" CACHE STRING "username of MaxScale user") set(TEST_USER "maxuser" CACHE STRING "username of MaxScale user")

View File

@ -1088,7 +1088,7 @@ char** skygw_get_table_names(GWBUF* querybuf,int* tblsize, bool fullnames)
} }
} }
if(tmp != NULL){
char *catnm = NULL; char *catnm = NULL;
if(fullnames) if(fullnames)
@ -1113,6 +1113,7 @@ char** skygw_get_table_names(GWBUF* querybuf,int* tblsize, bool fullnames)
tbl=tbl->next_local; tbl=tbl->next_local;
} }
}
lex->current_select = lex->current_select->next_select_in_list(); lex->current_select = lex->current_select->next_select_in_list();
} }

View File

@ -57,6 +57,9 @@ int main(int argc, char** argv)
{ {
fgets(readbuff,4092,infile); fgets(readbuff,4092,infile);
psize = strlen(readbuff); psize = strlen(readbuff);
if(psize < 0 || psize > 4092){
continue;
}
qbuff = gwbuf_alloc(psize + 7); qbuff = gwbuf_alloc(psize + 7);
*(qbuff->sbuf->data + 0) = (unsigned char)psize; *(qbuff->sbuf->data + 0) = (unsigned char)psize;
*(qbuff->sbuf->data + 1) = (unsigned char)(psize>>8); *(qbuff->sbuf->data + 1) = (unsigned char)(psize>>8);

View File

@ -38,6 +38,7 @@
* 09/09/14 Massimiliano Pinto Added localhost_match_wildcard_host parameter * 09/09/14 Massimiliano Pinto Added localhost_match_wildcard_host parameter
* 12/09/14 Mark Riddoch Addition of checks on servers list and * 12/09/14 Mark Riddoch Addition of checks on servers list and
* internal router suppression of messages * internal router suppression of messages
* 07/11/14 Massimiliano Pinto Addition of monitor timeouts for connect/read/write
* *
* @endverbatim * @endverbatim
*/ */
@ -58,6 +59,7 @@
extern int lm_enabled_logfiles_bitmask; extern int lm_enabled_logfiles_bitmask;
extern int setipaddress(struct in_addr *, char *);
static int process_config_context(CONFIG_CONTEXT *); static int process_config_context(CONFIG_CONTEXT *);
static int process_config_update(CONFIG_CONTEXT *); static int process_config_update(CONFIG_CONTEXT *);
static void free_config_context(CONFIG_CONTEXT *); static void free_config_context(CONFIG_CONTEXT *);
@ -772,6 +774,9 @@ int error_count = 0;
unsigned long interval = 0; unsigned long interval = 0;
int replication_heartbeat = 0; int replication_heartbeat = 0;
int detect_stale_master = 0; int detect_stale_master = 0;
int connect_timeout = 0;
int read_timeout = 0;
int write_timeout = 0;
module = config_get_value(obj->parameters, "module"); module = config_get_value(obj->parameters, "module");
servers = config_get_value(obj->parameters, "servers"); servers = config_get_value(obj->parameters, "servers");
@ -789,6 +794,17 @@ int error_count = 0;
detect_stale_master = atoi(config_get_value(obj->parameters, "detect_stale_master")); detect_stale_master = atoi(config_get_value(obj->parameters, "detect_stale_master"));
} }
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) if (module)
{ {
obj->element = monitor_alloc(obj->object, module); obj->element = monitor_alloc(obj->object, module);
@ -816,6 +832,14 @@ int error_count = 0;
if(detect_stale_master == 1) if(detect_stale_master == 1)
monitorDetectStaleMaster(obj->element, detect_stale_master); monitorDetectStaleMaster(obj->element, detect_stale_master);
/* 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 */ /* get the servers to monitor */
s = strtok(servers, ","); s = strtok(servers, ",");
while (s) while (s)
@ -1268,7 +1292,7 @@ SERVER *server;
(PERCENT_TYPE|COUNT_TYPE)); (PERCENT_TYPE|COUNT_TYPE));
} }
if (!succp) if (!succp && param != NULL)
{ {
LOGIF(LM, (skygw_log_write( LOGIF(LM, (skygw_log_write(
LOGFILE_MESSAGE, LOGFILE_MESSAGE,
@ -1359,10 +1383,10 @@ SERVER *server;
serviceSetUser(obj->element, serviceSetUser(obj->element,
user, user,
auth); auth);
if (enable_root_user && service) if (enable_root_user)
serviceEnableRootUser(service, atoi(enable_root_user)); serviceEnableRootUser(service, atoi(enable_root_user));
if (allow_localhost_match_wildcard_host) if (allow_localhost_match_wildcard_host && service)
serviceEnableLocalhostMatchWildcardHost( serviceEnableLocalhostMatchWildcardHost(
service, service,
atoi(allow_localhost_match_wildcard_host)); atoi(allow_localhost_match_wildcard_host));
@ -1625,6 +1649,9 @@ static char *monitor_params[] =
"monitor_interval", "monitor_interval",
"detect_replication_lag", "detect_replication_lag",
"detect_stale_master", "detect_stale_master",
"backend_connect_timeout",
"backend_read_timeout",
"backend_write_timeout",
NULL NULL
}; };
/** /**

View File

@ -1002,7 +1002,7 @@ int main(int argc, char **argv)
int n_services; int n_services;
int eno = 0; /*< local variable for errno */ int eno = 0; /*< local variable for errno */
int opt; int opt;
void** threads; /*< thread list */ void** threads = NULL; /*< thread list */
char mysql_home[PATH_MAX+1]; char mysql_home[PATH_MAX+1];
char datadir_arg[10+PATH_MAX+1]; /*< '--datadir=' + PATH_MAX */ char datadir_arg[10+PATH_MAX+1]; /*< '--datadir=' + PATH_MAX */
char language_arg[11+PATH_MAX+1]; /*< '--language=' + PATH_MAX */ char language_arg[11+PATH_MAX+1]; /*< '--language=' + PATH_MAX */
@ -1479,7 +1479,14 @@ int main(int argc, char **argv)
bool succp; bool succp;
sprintf(buf, "%s/log", home_dir); 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[0] = "MaxScale";
argv[1] = "-j"; argv[1] = "-j";
argv[2] = buf; argv[2] = buf;
@ -1733,8 +1740,11 @@ int main(int argc, char **argv)
unlink_pidfile(); unlink_pidfile();
return_main: return_main:
if (threads)
free(threads); free(threads);
if (home_dir)
free(home_dir); free(home_dir);
if (cnf_file_path)
free(cnf_file_path); free(cnf_file_path);
return rc; return rc;

View File

@ -39,7 +39,7 @@
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
char *enc; char *enc, *pw;
if (argc != 2) if (argc != 2)
{ {
@ -47,9 +47,21 @@ char *enc;
exit(1); 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); printf("%s\n", enc);
else }else{
fprintf(stderr, "Failed to encode the password\n"); fprintf(stderr, "Failed to encode the password\n");
}
free(pw);
return 0; return 0;
} }

View File

@ -26,6 +26,7 @@
* 08/07/13 Mark Riddoch Initial implementation * 08/07/13 Mark Riddoch Initial implementation
* 23/05/14 Massimiliano Pinto Addition of monitor_interval parameter * 23/05/14 Massimiliano Pinto Addition of monitor_interval parameter
* and monitor id * and monitor id
* 07/11/14 Massimiliano Pinto Addition of monitor network timeouts
* *
* @endverbatim * @endverbatim
*/ */
@ -329,3 +330,17 @@ monitorDetectStaleMaster(MONITOR *mon, int enable)
mon->module->detectStaleMaster(mon->handle, enable); mon->module->detectStaleMaster(mon->handle, enable);
} }
} }
/**
* 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);
}
}

View File

@ -61,7 +61,9 @@ int buflen;
ss_info_dassert(0 == GWBUF_EMPTY(buffer), "Buffer should not be empty"); 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_info_dassert(GWBUF_IS_TYPE_UNDEFINED(buffer), "Buffer type should be undefined");
ss_dfprintf(stderr, "\t..done\nSet a hint for the buffer"); 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); gwbuf_add_hint(buffer, hint);
ss_info_dassert(hint == buffer->hint, "Buffer should point to first and only 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"); ss_dfprintf(stderr, "\t..done\nSet a property for the buffer");

View File

@ -33,6 +33,7 @@
* 23/05/14 Massimiliano Pinto Addition of defaultId and setInterval * 23/05/14 Massimiliano Pinto Addition of defaultId and setInterval
* 23/06/14 Massimiliano Pinto Addition of replicationHeartbeat * 23/06/14 Massimiliano Pinto Addition of replicationHeartbeat
* 28/08/14 Massimiliano Pinto Addition of detectStaleMaster * 28/08/14 Massimiliano Pinto Addition of detectStaleMaster
* 07/11/14 Massimiliano Pinto Addition of setNetworkTimeout
* *
* @endverbatim * @endverbatim
*/ */
@ -70,6 +71,7 @@ typedef struct {
void (*defaultUser)(void *, char *, char *); void (*defaultUser)(void *, char *, char *);
void (*diagnostics)(DCB *, void *); void (*diagnostics)(DCB *, void *);
void (*setInterval)(void *, size_t); void (*setInterval)(void *, size_t);
void (*setNetworkTimeout)(void *, int, int);
void (*defaultId)(void *, unsigned long); void (*defaultId)(void *, unsigned long);
void (*replicationHeartbeat)(void *, int); void (*replicationHeartbeat)(void *, int);
void (*detectStaleMaster)(void *, int); void (*detectStaleMaster)(void *, int);
@ -96,6 +98,16 @@ typedef enum
MONITOR_STATE_FREED = 0x08 MONITOR_STATE_FREED = 0x08
} monitor_state_t; } 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. * Representation of the running monitor.
*/ */
@ -123,4 +135,5 @@ extern void monitorSetId(MONITOR *, unsigned long);
extern void monitorSetInterval (MONITOR *, unsigned long); extern void monitorSetInterval (MONITOR *, unsigned long);
extern void monitorSetReplicationHeartbeat(MONITOR *, int); extern void monitorSetReplicationHeartbeat(MONITOR *, int);
extern void monitorDetectStaleMaster(MONITOR *, int); extern void monitorDetectStaleMaster(MONITOR *, int);
extern void monitorSetNetworkTimeout(MONITOR *, int, int);
#endif #endif

View File

@ -60,7 +60,7 @@ static void setDownstream(FILTER *instance, void *fsession, DOWNSTREAM *downstre
static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue); static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue);
static void diagnostic(FILTER *instance, void *fsession, DCB *dcb); 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 = { static FILTER_OBJECT MyObject = {
createInstance, createInstance,
@ -302,7 +302,6 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
REGEX_INSTANCE *my_instance = (REGEX_INSTANCE *)instance; REGEX_INSTANCE *my_instance = (REGEX_INSTANCE *)instance;
REGEX_SESSION *my_session = (REGEX_SESSION *)session; REGEX_SESSION *my_session = (REGEX_SESSION *)session;
char *sql, *newsql; char *sql, *newsql;
int length;
if (modutil_is_SQL(queue)) if (modutil_is_SQL(queue))
{ {
@ -312,7 +311,7 @@ int length;
} }
if ((sql = modutil_get_SQL(queue)) != NULL) if ((sql = modutil_get_SQL(queue)) != NULL)
{ {
newsql = regex_replace(sql, length, &my_instance->re, newsql = regex_replace(sql, &my_instance->re,
my_instance->replace); my_instance->replace);
if (newsql) if (newsql)
{ {
@ -371,26 +370,23 @@ REGEX_SESSION *my_session = (REGEX_SESSION *)fsession;
* Perform a regular expression match and subsititution on the SQL * Perform a regular expression match and subsititution on the SQL
* *
* @param sql The original SQL text * @param sql The original SQL text
* @param length The length of the SQL text
* @param re The compiled regular expression * @param re The compiled regular expression
* @param replace The replacement text * @param replace The replacement text
* @return The replaced text or NULL if no replacement was done. * @return The replaced text or NULL if no replacement was done.
*/ */
static char * 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; char *orig, *result, *ptr;
int i, res_size, res_length, rep_length; int i, res_size, res_length, rep_length;
int last_match; int last_match, length;
regmatch_t match[10]; regmatch_t match[10];
orig = strndup(sql, length); if (regexec(re, sql, 10, match, 0))
if (regexec(re, orig, 10, match, 0))
{ {
free(orig);
return NULL; return NULL;
} }
free(orig); length = strlen(sql);
res_size = 2 * length; res_size = 2 * length;
result = (char *)malloc(res_size); result = (char *)malloc(res_size);

View File

@ -415,6 +415,7 @@ GWBUF *clone = NULL;
modutil_MySQL_Query(queue, &dummy, &length, &residual); modutil_MySQL_Query(queue, &dummy, &length, &residual);
clone = gwbuf_clone(queue); clone = gwbuf_clone(queue);
my_session->residual = residual; my_session->residual = residual;
free(ptr);
} }
} }

View File

@ -137,7 +137,7 @@ FILTER_PARAMETER** read_params(int* paramc)
do_read = 0; do_read = 0;
} }
} }
FILTER_PARAMETER** params; FILTER_PARAMETER** params = NULL;
if((params = malloc(sizeof(FILTER_PARAMETER*)*(pc+1))) != NULL){ if((params = malloc(sizeof(FILTER_PARAMETER*)*(pc+1))) != NULL){
for(i = 0;i<pc;i++){ for(i = 0;i<pc;i++){
params[i] = malloc(sizeof(FILTER_PARAMETER)); params[i] = malloc(sizeof(FILTER_PARAMETER));
@ -148,9 +148,9 @@ FILTER_PARAMETER** read_params(int* paramc)
free(names[i]); free(names[i]);
free(values[i]); free(values[i]);
} }
}
params[pc] = NULL; params[pc] = NULL;
*paramc = pc; *paramc = pc;
}
return params; return params;
} }
@ -303,7 +303,7 @@ int load_query()
int i, qcount = 0, qbuff_sz = 10, rval = 0; int i, qcount = 0, qbuff_sz = 10, rval = 0;
int offset = 0; int offset = 0;
unsigned int qlen = 0; unsigned int qlen = 0;
buffer = (char*)malloc(4092*sizeof(char)); buffer = (char*)calloc(4092,sizeof(char));
if(buffer == NULL){ if(buffer == NULL){
printf("Error: cannot allocate enough memory.\n"); printf("Error: cannot allocate enough memory.\n");
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n"); skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n");
@ -925,8 +925,9 @@ GWBUF* gen_packet(PACKET pkt)
int process_opts(int argc, char** argv) int process_opts(int argc, char** argv)
{ {
int fd = open_file("harness.cnf",1), buffsize = 1024; unsigned int fd = open_file("harness.cnf",1), buffsize = 1024;
int rd,fsize,rdsz; int rd,rdsz;
unsigned int fsize;
char *buff = calloc(buffsize,sizeof(char)), *tok = NULL; char *buff = calloc(buffsize,sizeof(char)), *tok = NULL;
/**Parse 'harness.cnf' file*/ /**Parse 'harness.cnf' file*/

View File

@ -80,6 +80,7 @@ static MONITOR_OBJECT MyObject = {
NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL
}; };
/** /**

View File

@ -0,0 +1,823 @@
/*
* 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
};
/**
* 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;
}
}

View File

@ -84,6 +84,7 @@ static void setInterval(void *, size_t);
static void defaultId(void *, unsigned long); static void defaultId(void *, unsigned long);
static void replicationHeartbeat(void *, int); static void replicationHeartbeat(void *, int);
static void detectStaleMaster(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_status_changed(MONITOR_SERVERS* mon_srv);
static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv); static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv);
static MONITOR_SERVERS *getServerByNodeId(MONITOR_SERVERS *, long); static MONITOR_SERVERS *getServerByNodeId(MONITOR_SERVERS *, long);
@ -103,6 +104,7 @@ static MONITOR_OBJECT MyObject = {
defaultUser, defaultUser,
diagnostics, diagnostics,
setInterval, setInterval,
NULL,
defaultId, defaultId,
replicationHeartbeat, replicationHeartbeat,
detectStaleMaster detectStaleMaster
@ -1203,3 +1205,15 @@ monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit)
{ {
ptr->pending_status &= ~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;
}

View File

@ -73,6 +73,7 @@ static MONITOR_OBJECT MyObject = {
setInterval, setInterval,
NULL, NULL,
NULL, NULL,
NULL,
NULL NULL
}; };

View File

@ -16,3 +16,6 @@ install(TARGETS cli DESTINATION modules)
add_subdirectory(readwritesplit) add_subdirectory(readwritesplit)
if(BUILD_TESTS)
add_subdirectory(test)
endif()

View File

@ -1425,7 +1425,7 @@ void check_drop_tmp_table(
if (is_drop_table_query(querybuf)) if (is_drop_table_query(querybuf))
{ {
tbl = skygw_get_table_names(querybuf,&tsize,false); tbl = skygw_get_table_names(querybuf,&tsize,false);
if(tbl != NULL){
for(i = 0; i<tsize; i++) for(i = 0; i<tsize; i++)
{ {
klen = strlen(dbname) + strlen(tbl[i]) + 2; klen = strlen(dbname) + strlen(tbl[i]) + 2;
@ -1447,7 +1447,7 @@ void check_drop_tmp_table(
free(tbl[i]); free(tbl[i]);
free(hkey); free(hkey);
} }
if(tbl != NULL){
free(tbl); free(tbl);
} }
} }
@ -1495,7 +1495,7 @@ skygw_query_type_t is_read_tmp_table(
{ {
tbl = skygw_get_table_names(querybuf,&tsize,false); tbl = skygw_get_table_names(querybuf,&tsize,false);
if (tsize > 0) if (tbl != NULL && tsize > 0)
{ {
/** Query targets at least one table */ /** Query targets at least one table */
for(i = 0; i<tsize && !target_tmp_table && tbl[i]; i++) for(i = 0; i<tsize && !target_tmp_table && tbl[i]; i++)

View File

@ -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 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) add_subdirectory(test_hints)

View 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})

View 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;
}