Merge branch 'develop' into MXS-936

This commit is contained in:
MassimilianoPinto
2016-11-10 15:42:09 +01:00
29 changed files with 2151 additions and 1817 deletions

View File

@ -378,6 +378,16 @@ Configure the directory where the executable files reside. All internal processe
execdir=/usr/local/bin/
```
#### `persistdir`
Configure the directory where persisted configurations are stored. When a new
server is created via MaxAdmin, it will be stored in this directory. Do not use
or modify the contents of this directory, use _/etc/maxscale.cnf.d/_ instead.
```
persistdir=/var/lib/maxscale/maxscale.cnf.d/
```
#### `language`
Set the folder where the errmsg.sys file is located in. MariaDB MaxScale will look for the errmsg.sys file installed with MariaDB MaxScale from this folder.

View File

@ -108,6 +108,7 @@ static struct option long_options[] =
#define MAXADMIN_DEFAULT_HOST "localhost"
#define MAXADMIN_DEFAULT_PORT "6603"
#define MAXADMIN_DEFAULT_USER "admin"
#define MAXADMIN_BUFFER_SIZE 2048
/**
* The main for the maxadmin client
@ -125,7 +126,7 @@ main(int argc, char **argv)
History *hist;
HistEvent ev;
#else
char buf[1024];
char buf[MAXADMIN_BUFFER_SIZE];
#endif
char *hostname = NULL;
char *port = NULL;

View File

@ -20,6 +20,7 @@ set(DEFAULT_CACHE_SUBPATH "cache/maxscale" CACHE PATH "Default cache subpath")
set(DEFAULT_LANG_SUBPATH "lib/maxscale" CACHE PATH "Default language file subpath")
set(DEFAULT_EXEC_SUBPATH "${MAXSCALE_BINDIR}" CACHE PATH "Default executable subpath")
set(DEFAULT_CONFIG_SUBPATH "etc" CACHE PATH "Default configuration subpath")
set(DEFAULT_CONFIG_PERSIST_SUBPATH "maxscale.cnf.d" CACHE PATH "Default persisted configuration subpath")
set(DEFAULT_PIDDIR ${MAXSCALE_VARDIR}/${DEFAULT_PID_SUBPATH} CACHE PATH "Default PID file directory")
set(DEFAULT_LOGDIR ${MAXSCALE_VARDIR}/${DEFAULT_LOG_SUBPATH} CACHE PATH "Default log directory")
@ -29,6 +30,7 @@ set(DEFAULT_CACHEDIR ${MAXSCALE_VARDIR}/${DEFAULT_CACHE_SUBPATH} CACHE PATH "Def
set(DEFAULT_LANGDIR ${MAXSCALE_VARDIR}/${DEFAULT_LANG_SUBPATH} CACHE PATH "Default language file directory")
set(DEFAULT_EXECDIR ${CMAKE_INSTALL_PREFIX}/${DEFAULT_EXEC_SUBPATH} CACHE PATH "Default executable directory")
set(DEFAULT_CONFIGDIR /${DEFAULT_CONFIG_SUBPATH} CACHE PATH "Default configuration directory")
set(DEFAULT_CONFIG_PERSISTDIR ${DEFAULT_DATADIR}/${DEFAULT_CONFIG_PERSIST_SUBPATH} CACHE PATH "Default persisted configuration directory")
# Massage TARGET_COMPONENT into a list
if (TARGET_COMPONENT)

View File

@ -32,10 +32,12 @@ MXS_BEGIN_DECLS
#define MXS_DEFAULT_LANG_SUBPATH "@DEFAULT_LANG_SUBPATH@"
#define MXS_DEFAULT_EXEC_SUBPATH "@DEFAULT_EXEC_SUBPATH@"
#define MXS_DEFAULT_CONFIG_SUBPATH "@DEFAULT_CONFIG_SUBPATH@"
#define MXS_DEFAULT_CONFIG_PERSIST_SUBPATH "@DEFAULT_CONFIG_PERSIST_SUBPATH@"
/** Default file locations, configured by CMake */
static const char* default_cnf_fname = "maxscale.cnf";
static const char* default_configdir = "@DEFAULT_CONFIGDIR@";
/*< This should be changed to just /run eventually,
* the /var/run folder is an old standard and the newer FSH 3.0
* uses /run for PID files.*/
@ -46,8 +48,10 @@ static const char* default_libdir = "@DEFAULT_LIBDIR@";
static const char* default_cachedir = "@DEFAULT_CACHEDIR@";
static const char* default_langdir = "@DEFAULT_LANGDIR@";
static const char* default_execdir = "@DEFAULT_EXECDIR@";
static const char* default_config_persistdir = "@DEFAULT_CONFIG_PERSISTDIR@";
static char* configdir = NULL;
static char* configdir = NULL; /*< Where the config file is found e.g. /etc/ */
static char* config_persistdir = NULL;/*< Persisted configs e.g. /var/lib/maxscale.cnf.d/ */
static char* logdir = NULL;
static char* libdir = NULL;
static char* cachedir = NULL;
@ -62,6 +66,7 @@ void set_datadir(char* param);
void set_process_datadir(char* param);
void set_cachedir(char* param);
void set_configdir(char* param);
void set_config_persistdir(char* param);
void set_logdir(char* param);
void set_langdir(char* param);
void set_piddir(char* param);
@ -71,6 +76,7 @@ char* get_datadir();
char* get_process_datadir();
char* get_cachedir();
char* get_configdir();
char* get_config_persistdir();
char* get_piddir();
char* get_logdir();
char* get_langdir();

View File

@ -137,6 +137,9 @@ typedef enum
#define MONITOR_INTERVAL 10000 // in milliseconds
#define MONITOR_DEFAULT_ID 1UL // unsigned long value
#define MAX_MONITOR_USER_LEN 512
#define MAX_MONITOR_PASSWORD_LEN 512
/*
* Create declarations of the enum for monitor events and also the array of
* structs containing the matching names. The data is taken from def_monitor_event.h
@ -177,8 +180,8 @@ typedef struct monitor_servers
struct monitor
{
char *name; /**< The name of the monitor module */
char *user; /*< Monitor username */
char *password; /*< Monitor password */
char user[MAX_MONITOR_USER_LEN]; /*< Monitor username */
char password[MAX_MONITOR_PASSWORD_LEN]; /*< Monitor password */
SPINLOCK lock;
CONFIG_PARAMETER* parameters; /*< configuration parameters */
MONITOR_SERVERS* databases; /*< List of databases the monitor monitors */
@ -201,7 +204,8 @@ struct monitor
extern MONITOR *monitor_alloc(char *, char *);
extern void monitor_free(MONITOR *);
extern MONITOR *monitor_find(char *);
extern void monitorAddServer(MONITOR *, SERVER *);
extern void monitorAddServer(MONITOR *mon, SERVER *server);
extern void monitorRemoveServer(MONITOR *mon, SERVER *server);
extern void monitorAddUser(MONITOR *, char *, char *);
extern void monitorAddParameters(MONITOR *monitor, CONFIG_PARAMETER *params);
extern void monitorStop(MONITOR *);

View File

@ -48,6 +48,8 @@
MXS_BEGIN_DECLS
#define MAX_SERVER_NAME_LEN 1024
#define MAX_SERVER_MONUSER_LEN 512
#define MAX_SERVER_MONPW_LEN 512
#define MAX_NUM_SLAVES 128 /**< Maximum number of slaves under a single server*/
/**
@ -86,15 +88,16 @@ typedef struct server
#endif
SPINLOCK lock; /**< Common access lock */
char *unique_name; /**< Unique name for the server */
char *name; /**< Server name/IP address*/
char name[MAX_SERVER_NAME_LEN]; /**< Server name/IP address*/
unsigned short port; /**< Port to listen on */
char *protocol; /**< Protocol module to use */
char *authenticator; /**< Authenticator module name */
void *auth_instance; /**< Authenticator instance */
char *auth_options; /**< Authenticator options */
SSL_LISTENER *server_ssl; /**< SSL data structure for server, if any */
unsigned int status; /**< Status flag bitmap for the server */
char *monuser; /**< User name to use to monitor the db */
char *monpw; /**< Password to use to monitor the db */
char monuser[MAX_SERVER_MONUSER_LEN]; /**< User name to use to monitor the db */
char monpw[MAX_SERVER_MONPW_LEN]; /**< Password to use to monitor the db */
SERVER_STATS stats; /**< The server statistics */
struct server *next; /**< Next server */
struct server *nextdb; /**< Next server in list attached to a service */
@ -216,7 +219,7 @@ extern void server_transfer_status(SERVER *dest_server, SERVER *source_server);
extern void serverAddMonUser(SERVER *, char *, char *);
extern void serverAddParameter(SERVER *, char *, char *);
extern char *serverGetParameter(SERVER *, char *);
extern void server_update(SERVER *, char *, char *, char *);
extern void server_update_credentials(SERVER *, char *, char *);
extern void server_set_unique_name(SERVER *, char *);
extern DCB *server_get_persistent(SERVER *, char *, const char *);
extern void server_update_address(SERVER *, char *);
@ -224,5 +227,19 @@ extern void server_update_port(SERVER *, unsigned short);
extern RESULTSET *serverGetList();
extern unsigned int server_map_status(char *str);
extern bool server_set_version_string(SERVER* server, const char* string);
extern bool server_is_ssl_parameter(const char *key);
extern void server_update_ssl(SERVER *server, const char *key, const char *value);
/**
* @brief Serialize a server to a file
*
* This converts @c server into an INI format file. This allows created servers
* to be persisted to disk. This will replace any existing files with the same
* name.
*
* @param server Server to serialize
* @return False if the serialization of the server fails, true if it was successful
*/
bool server_serialize(SERVER *server);
MXS_END_DECLS

View File

@ -101,8 +101,12 @@ typedef struct server_ref_t
SERVER* server; /**< The actual server */
int weight; /**< Weight of this server */
int connections; /**< Number of connections created through this reference */
bool active; /**< Whether this reference is valid and in use*/
} SERVER_REF;
/** Macro to check whether a SERVER_REF is active */
#define SERVER_REF_IS_ACTIVE(ref) (ref->active)
#define SERVICE_MAX_RETRY_INTERVAL 3600 /*< The maximum interval between service start retries */
/** Value of service timeout if timeout checks are disabled */
@ -146,6 +150,7 @@ typedef struct service
void *router_instance; /**< The router instance for this service */
char *version_string; /** version string for this service listeners */
SERVER_REF *dbref; /** server references */
int n_dbref; /** Number of server references */
SERVICE_USER credentials; /**< The cedentials of the service user */
SPINLOCK spin; /**< The service spinlock */
SERVICE_STATS stats; /**< The service statistics */
@ -194,6 +199,7 @@ extern int serviceAddProtocol(SERVICE *service, char *name, char *protocol,
extern int serviceHasProtocol(SERVICE *service, const char *protocol,
const char* address, unsigned short port);
extern void serviceAddBackend(SERVICE *, SERVER *);
extern void serviceRemoveBackend(SERVICE *, const SERVER *);
extern int serviceHasBackend(SERVICE *, SERVER *);
extern void serviceAddRouterOption(SERVICE *, char *);
extern void serviceClearRouterOptions(SERVICE *);

View File

@ -65,6 +65,7 @@
#include <maxscale/service.h>
#include <maxscale/spinlock.h>
#include <maxscale/utils.h>
#include <maxscale/gwdirs.h>
typedef struct duplicate_context
{
@ -540,6 +541,46 @@ static bool config_load_dir(const char *dir, DUPLICATE_CONTEXT *dcontext, CONFIG
return rv == 0;
}
/**
* Check if a directory exists
*
* This function also logs warnings if the directory cannot be accessed or if
* the file is not a directory.
* @param dir Directory to check
* @return True if the file is an existing directory
*/
static bool is_directory(const char *dir)
{
bool rval = false;
struct stat st;
if (stat(dir, &st) == -1)
{
if (errno == ENOENT)
{
MXS_NOTICE("%s does not exist, not reading.", dir);
}
else
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_WARNING("Could not access %s, not reading: %s",
dir, strerror_r(errno, errbuf, sizeof(errbuf)));
}
}
else
{
if (S_ISDIR(st.st_mode))
{
rval = true;
}
else
{
MXS_WARNING("%s exists, but it is not a directory. Ignoring.", dir);
}
}
return rval;
}
/**
* @brief Load the specified configuration file for MaxScale
*
@ -573,30 +614,18 @@ config_load_and_process(const char* filename, bool (*process_config)(CONFIG_CONT
rval = true;
struct stat st;
if (stat(dir, &st) == -1)
if (is_directory(dir))
{
if (errno == ENOENT)
{
MXS_NOTICE("%s does not exist, not reading.", dir);
}
else
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_WARNING("Could not access %s, not reading: %s",
dir, strerror_r(errno, errbuf, sizeof(errbuf)));
}
rval = config_load_dir(dir, &dcontext, &ccontext);
}
else
/** Create the persisted configuration directory if it doesn't exist */
const char* persist_cnf = get_config_persistdir();
mxs_mkdir_all(persist_cnf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (is_directory(persist_cnf))
{
if (S_ISDIR(st.st_mode))
{
rval = config_load_dir(dir, &dcontext, &ccontext);
}
else
{
MXS_WARNING("%s exists, but it is not a directory. Ignoring.", dir);
}
rval = config_load_dir(persist_cnf, &dcontext, &ccontext);
}
if (rval)
@ -1800,10 +1829,9 @@ process_config_update(CONFIG_CONTEXT *context)
if (address && port &&
(server = server_find(address, atoi(port))) != NULL)
{
char *protocol = config_get_value(obj->parameters, "protocol");
char *monuser = config_get_value(obj->parameters, "monuser");
char *monpw = config_get_value(obj->parameters, "monpw");
server_update(server, protocol, monuser, monpw);
server_update_credentials(server, monuser, monpw);
obj->element = server;
}
else
@ -2824,12 +2852,6 @@ int configure_new_service(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj)
s = strtok_r(NULL, ",", &lasts);
}
}
else if (servers == NULL && !is_internal_service(router))
{
MXS_ERROR("The service '%s' is missing a definition of the servers "
"that provide the service.", obj->object);
error_count++;
}
if (roptions)
{
@ -2882,12 +2904,6 @@ int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE*
}
char *servers = config_get_value(obj->parameters, "servers");
if (servers == NULL)
{
MXS_ERROR("Monitor '%s' is missing the 'servers' parameter that "
"lists the servers that it monitors.", obj->object);
error_count++;
}
if (error_count == 0)
{
@ -2934,36 +2950,39 @@ int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE*
}
}
/* get the servers to monitor */
char *s, *lasts;
s = strtok_r(servers, ",", &lasts);
while (s)
if (servers)
{
CONFIG_CONTEXT *obj1 = context;
int found = 0;
while (obj1)
/* get the servers to monitor */
char *s, *lasts;
s = strtok_r(servers, ",", &lasts);
while (s)
{
if (strcmp(trim(s), obj1->object) == 0 && obj->element && obj1->element)
CONFIG_CONTEXT *obj1 = context;
int found = 0;
while (obj1)
{
found = 1;
if (hashtable_add(monitorhash, obj1->object, "") == 0)
if (strcmp(trim(s), obj1->object) == 0 && obj->element && obj1->element)
{
MXS_WARNING("Multiple monitors are monitoring server [%s]. "
"This will cause undefined behavior.",
obj1->object);
found = 1;
if (hashtable_add(monitorhash, obj1->object, "") == 0)
{
MXS_WARNING("Multiple monitors are monitoring server [%s]. "
"This will cause undefined behavior.",
obj1->object);
}
monitorAddServer(obj->element, obj1->element);
}
monitorAddServer(obj->element, obj1->element);
obj1 = obj1->next;
}
if (!found)
{
MXS_ERROR("Unable to find server '%s' that is "
"configured in the monitor '%s'.", s, obj->object);
error_count++;
}
obj1 = obj1->next;
}
if (!found)
{
MXS_ERROR("Unable to find server '%s' that is "
"configured in the monitor '%s'.", s, obj->object);
error_count++;
}
s = strtok_r(NULL, ",", &lasts);
s = strtok_r(NULL, ",", &lasts);
}
}
char *user = config_get_value(obj->parameters, "user");

View File

@ -128,6 +128,7 @@ static struct option long_options[] =
{"configdir", required_argument, 0, 'C'},
{"datadir", required_argument, 0, 'D'},
{"execdir", required_argument, 0, 'E'},
{"persistdir", required_argument, 0, 'F'},
{"language", required_argument, 0, 'N'},
{"piddir", required_argument, 0, 'P'},
{"basedir", required_argument, 0, 'R'},
@ -919,8 +920,9 @@ static void usage(void)
" -B, --libdir=PATH path to module directory\n"
" -C, --configdir=PATH path to configuration file directory\n"
" -D, --datadir=PATH path to data directory,\n"
" stored embedded mysql tables\n"
" stores internal MaxScale data\n"
" -E, --execdir=PATH path to the maxscale and other executable files\n"
" -F, --persistdir=PATH path to persisted configuration directory\n"
" -N, --language=PATH path to errmsg.sys file\n"
" -P, --piddir=PATH path to PID file directory\n"
" -R, --basedir=PATH base path for all other paths\n"
@ -944,6 +946,7 @@ static void usage(void)
" execdir : %s\n"
" language : %s\n"
" piddir : %s\n"
" persistdir : %s\n"
"\n"
"If '--basedir' is provided then all other paths, including the default\n"
"configuration file path, are defined relative to that. As an example,\n"
@ -954,7 +957,8 @@ static void usage(void)
progname,
get_configdir(), default_cnf_fname,
get_configdir(), get_logdir(), get_cachedir(), get_libdir(),
get_datadir(), get_execdir(), get_langdir(), get_piddir());
get_datadir(), get_execdir(), get_langdir(), get_piddir(),
get_config_persistdir());
}
@ -1229,6 +1233,12 @@ bool set_dirs(const char *basedir)
set_piddir(path);
}
if (rv && (rv = handle_path_arg(&path, basedir, MXS_DEFAULT_DATA_SUBPATH "/"
MXS_DEFAULT_CONFIG_PERSIST_SUBPATH, true, true)))
{
set_config_persistdir(path);
}
return rv;
}
@ -1327,7 +1337,7 @@ int main(int argc, char **argv)
}
}
while ((opt = getopt_long(argc, argv, "dcf:l:vVs:S:?L:D:C:B:U:A:P:G:N:E:",
while ((opt = getopt_long(argc, argv, "dcf:l:vVs:S:?L:D:C:B:U:A:P:G:N:E:F:",
long_options, &option_index)) != -1)
{
bool succp = true;
@ -1477,6 +1487,16 @@ int main(int argc, char **argv)
succp = false;
}
break;
case 'F':
if (handle_path_arg(&tmp_path, optarg, NULL, true, true))
{
set_config_persistdir(tmp_path);
}
else
{
succp = false;
}
break;
case 'R':
if (handle_path_arg(&tmp_path, optarg, NULL, true, false))
{
@ -2524,6 +2544,20 @@ static int cnf_preparser(void* data, const char* section, const char* name, cons
}
}
}
else if (strcmp(name, "persistdir") == 0)
{
if (strcmp(get_config_persistdir(), default_config_persistdir) == 0)
{
if (handle_path_arg((char**)&tmp, (char*)value, NULL, true, false))
{
set_config_persistdir(tmp);
}
else
{
return 0;
}
}
}
else if (strcmp(name, "syslog") == 0)
{
if (!syslog_configured)

View File

@ -26,6 +26,17 @@ void set_configdir(char* str)
configdir = str;
}
/**
* Set the configuration parts file directory
* @param str Path to directory
*/
void set_config_persistdir(char* str)
{
MXS_FREE(config_persistdir);
clean_up_pathname(str);
config_persistdir = str;
}
/**
* Set the log file directory
* @param str Path to directory
@ -160,6 +171,15 @@ char* get_configdir()
return configdir ? configdir : (char*) default_configdir;
}
/**
* Get the configuration file directory
* @return The path to the configuration file directory
*/
char* get_config_persistdir()
{
return config_persistdir ? config_persistdir : (char*) default_config_persistdir;
}
/**
* Get the PID file directory which contains maxscale.pid
* @return Path to the PID file directory

View File

@ -58,7 +58,7 @@ const monitor_def_t monitor_event_definitions[MAX_MONITOR_EVENT] =
static MONITOR *allMonitors = NULL;
static SPINLOCK monLock = SPINLOCK_INIT;
static void monitor_servers_free(MONITOR_SERVERS *servers);
static void monitor_server_free_all(MONITOR_SERVERS *servers);
/**
* Allocate a new monitor, load the associated module for the monitor
@ -93,9 +93,8 @@ monitor_alloc(char *name, char *module)
mon->name = name;
mon->handle = NULL;
mon->databases = NULL;
mon->password = NULL;
mon->user = NULL;
mon->password = NULL;
*mon->password = '\0';
*mon->user = '\0';
mon->read_timeout = DEFAULT_READ_TIMEOUT;
mon->write_timeout = DEFAULT_WRITE_TIMEOUT;
mon->connect_timeout = DEFAULT_CONNECT_TIMEOUT;
@ -142,7 +141,7 @@ monitor_free(MONITOR *mon)
}
spinlock_release(&monLock);
free_config_parameter(mon->parameters);
monitor_servers_free(mon->databases);
monitor_server_free_all(mon->databases);
MXS_FREE(mon->name);
MXS_FREE(mon);
}
@ -258,6 +257,13 @@ monitorAddServer(MONITOR *mon, SERVER *server)
/* pending status is updated by get_replication_tree */
db->pending_status = 0;
monitor_state_t old_state = mon->state;
if (old_state == MONITOR_STATE_RUNNING)
{
monitorStop(mon);
}
spinlock_acquire(&mon->lock);
if (mon->databases == NULL)
@ -274,23 +280,88 @@ monitorAddServer(MONITOR *mon, SERVER *server)
ptr->next = db;
}
spinlock_release(&mon->lock);
if (old_state == MONITOR_STATE_RUNNING)
{
monitorStart(mon, mon->parameters);
}
}
static void monitor_server_free(MONITOR_SERVERS *tofree)
{
if (tofree)
{
if (tofree->con)
{
mysql_close(tofree->con);
}
MXS_FREE(tofree);
}
}
/**
* Free monitor server list
* @param servers Servers to free
*/
static void monitor_servers_free(MONITOR_SERVERS *servers)
static void monitor_server_free_all(MONITOR_SERVERS *servers)
{
while (servers)
{
MONITOR_SERVERS *tofree = servers;
servers = servers->next;
if (tofree->con)
monitor_server_free(tofree);
}
}
/**
* Remove a server from a monitor.
*
* @param mon The Monitor instance
* @param server The Server to remove
*/
void monitorRemoveServer(MONITOR *mon, SERVER *server)
{
monitor_state_t old_state = mon->state;
if (old_state == MONITOR_STATE_RUNNING)
{
monitorStop(mon);
}
spinlock_acquire(&mon->lock);
ss_dassert(mon->databases);
MONITOR_SERVERS *ptr = mon->databases;
if (ptr->server == server)
{
mon->databases = mon->databases->next;
}
else
{
MONITOR_SERVERS *prev = ptr;
while (ptr)
{
mysql_close(tofree->con);
if (ptr->server == server)
{
prev->next = ptr->next;
break;
}
prev = ptr;
ptr = ptr->next;
}
MXS_FREE(tofree);
}
spinlock_release(&mon->lock);
if (ptr)
{
monitor_server_free(ptr);
}
if (old_state == MONITOR_STATE_RUNNING)
{
monitorStart(mon, mon->parameters);
}
}
@ -305,8 +376,8 @@ static void monitor_servers_free(MONITOR_SERVERS *servers)
void
monitorAddUser(MONITOR *mon, char *user, char *passwd)
{
mon->user = MXS_STRDUP_A(user);
mon->password = MXS_STRDUP_A(passwd);
snprintf(mon->user, sizeof(mon->user), "%s", user);
snprintf(mon->password, sizeof(mon->password), "%s", passwd);
}
/**
@ -536,13 +607,8 @@ monitorGetList()
*/
bool check_monitor_permissions(MONITOR* monitor, const char* query)
{
if (monitor->databases == NULL)
{
MXS_ERROR("[%s] Monitor is missing the servers parameter.", monitor->name);
return false;
}
if (config_get_global_options()->skip_permission_checks)
if (monitor->databases == NULL || // No servers to check
config_get_global_options()->skip_permission_checks)
{
return true;
}
@ -992,8 +1058,15 @@ mon_connect_to_db(MONITOR* mon, MONITOR_SERVERS *database)
if ((database->con = mysql_init(NULL)))
{
char *uname = database->server->monuser ? database->server->monuser : mon->user;
char *passwd = database->server->monpw ? database->server->monpw : mon->password;
char *uname = mon->user;
char *passwd = mon->password;
if (database->server->monuser[0] && database->server->monpw[0])
{
uname = database->server->monuser;
passwd = database->server->monpw;
}
char *dpwd = decryptPassword(passwd);
mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *) &mon->connect_timeout);

View File

@ -35,6 +35,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <maxscale/session.h>
#include <maxscale/server.h>
#include <maxscale/spinlock.h>
@ -44,6 +47,7 @@
#include <maxscale/gw_ssl.h>
#include <maxscale/alloc.h>
#include <maxscale/modules.h>
#include <maxscale/gwdirs.h>
static SPINLOCK server_spin = SPINLOCK_INIT;
static SERVER *allServers = NULL;
@ -86,12 +90,17 @@ server_alloc(char *servname, char *protocol, unsigned short port, char *authenti
return NULL;
}
servname = MXS_STRNDUP(servname, MAX_SERVER_NAME_LEN);
if (auth_options && (auth_options = MXS_STRDUP(auth_options)) == NULL)
{
MXS_FREE(authenticator);
return NULL;
}
protocol = MXS_STRDUP(protocol);
SERVER *server = (SERVER *)MXS_CALLOC(1, sizeof(SERVER));
if (!servname || !protocol || !server || !authenticator)
if (!protocol || !server || !authenticator)
{
MXS_FREE(servname);
MXS_FREE(protocol);
@ -104,10 +113,11 @@ server_alloc(char *servname, char *protocol, unsigned short port, char *authenti
server->server_chk_top = CHK_NUM_SERVER;
server->server_chk_tail = CHK_NUM_SERVER;
#endif
server->name = servname;
snprintf(server->name, sizeof(server->name), "%s", servname);
server->protocol = protocol;
server->authenticator = authenticator;
server->auth_instance = auth_instance;
server->auth_options = auth_options;
server->port = port;
server->status = SERVER_RUNNING;
server->node_id = -1;
@ -121,6 +131,8 @@ server_alloc(char *servname, char *protocol, unsigned short port, char *authenti
server->persistmax = 0;
server->persistmaxtime = 0;
server->persistpoolmax = 0;
server->monuser[0] = '\0';
server->monpw[0] = '\0';
spinlock_init(&server->persistlock);
spinlock_acquire(&server_spin);
@ -164,7 +176,6 @@ server_free(SERVER *tofreeserver)
spinlock_release(&server_spin);
/* Clean up session and free the memory */
MXS_FREE(tofreeserver->name);
MXS_FREE(tofreeserver->protocol);
MXS_FREE(tofreeserver->unique_name);
MXS_FREE(tofreeserver->server_string);
@ -775,8 +786,8 @@ server_transfer_status(SERVER *dest_server, SERVER *source_server)
void
serverAddMonUser(SERVER *server, char *user, char *passwd)
{
server->monuser = MXS_STRDUP_A(user);
server->monpw = MXS_STRDUP_A(passwd);
snprintf(server->monuser, sizeof(server->monuser), "%s", user);
snprintf(server->monpw, sizeof(server->monpw), "%s", passwd);
}
/**
@ -793,28 +804,13 @@ serverAddMonUser(SERVER *server, char *user, char *passwd)
* @param passwd The password to use for the monitor user
*/
void
server_update(SERVER *server, char *protocol, char *user, char *passwd)
server_update_credentials(SERVER *server, char *user, char *passwd)
{
if (!strcmp(server->protocol, protocol))
{
MXS_NOTICE("Update server protocol for server %s to protocol %s.",
server->name,
protocol);
MXS_FREE(server->protocol);
server->protocol = MXS_STRDUP_A(protocol);
}
if (user != NULL && passwd != NULL)
{
if (strcmp(server->monuser, user) == 0 ||
strcmp(server->monpw, passwd) == 0)
{
MXS_NOTICE("Update server monitor credentials for server %s",
server->name);
MXS_FREE(server->monuser);
MXS_FREE(server->monpw);
serverAddMonUser(server, user, passwd);
}
snprintf(server->monuser, sizeof(server->monuser), "%s", user);
snprintf(server->monpw, sizeof(server->monpw), "%s", passwd);
MXS_NOTICE("Updated monitor credentials for server '%s'", server->name);
}
}
@ -979,11 +975,7 @@ server_update_address(SERVER *server, char *address)
spinlock_acquire(&server_spin);
if (server && address)
{
if (server->name)
{
MXS_FREE(server->name);
}
server->name = MXS_STRDUP_A(address);
strcpy(server->name, address);
}
spinlock_release(&server_spin);
}
@ -1070,3 +1062,158 @@ bool server_set_version_string(SERVER* server, const char* string)
return rval;
}
/**
* Creates a server configuration at the location pointed by @c filename
*
* @param server Server to serialize into a configuration
* @param filename Filename where configuration is written
* @return True on success, false on error
*/
static bool create_server_config(SERVER *server, const char *filename)
{
int file = open(filename, O_EXCL | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (file == -1)
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to open file '%s' when serializing server '%s': %d, %s",
filename, server->unique_name, errno, strerror_r(errno, errbuf, sizeof(errbuf)));
return false;
}
// TODO: Check for return values on all of the dprintf calls
dprintf(file, "[%s]\n", server->unique_name);
dprintf(file, "type=server\n");
dprintf(file, "protocol=%s\n", server->protocol);
dprintf(file, "address=%s\n", server->name);
dprintf(file, "port=%u\n", server->port);
dprintf(file, "authenticator=%s\n", server->authenticator);
if (server->auth_options)
{
dprintf(file, "authenticator_options=%s\n", server->auth_options);
}
if (*server->monpw && *server->monuser)
{
dprintf(file, "monitoruser=%s\n", server->monuser);
dprintf(file, "monitorpw=%s\n", server->monpw);
}
if (server->persistpoolmax)
{
dprintf(file, "persistpoolmax=%ld\n", server->persistpoolmax);
}
if (server->persistmaxtime)
{
dprintf(file, "persistmaxtime=%ld\n", server->persistmaxtime);
}
if (server->server_ssl)
{
dprintf(file, "ssl=required\n");
if (server->server_ssl->ssl_cert)
{
dprintf(file, "ssl_cert=%s\n", server->server_ssl->ssl_cert);
}
if (server->server_ssl->ssl_key)
{
dprintf(file, "ssl_key=%s\n", server->server_ssl->ssl_key);
}
if (server->server_ssl->ssl_ca_cert)
{
dprintf(file, "ssl_ca_cert=%s\n", server->server_ssl->ssl_ca_cert);
}
if (server->server_ssl->ssl_cert_verify_depth)
{
dprintf(file, "ssl_cert_verify_depth=%d\n", server->server_ssl->ssl_cert_verify_depth);
}
const char *version = NULL;
switch (server->server_ssl->ssl_method_type)
{
case SERVICE_TLS10:
version = "TLSV10";
break;
#ifdef OPENSSL_1_0
case SERVICE_TLS11:
version = "TLSV11";
break;
case SERVICE_TLS12:
version = "TLSV12";
break;
#endif
case SERVICE_SSL_TLS_MAX:
version = "MAX";
break;
default:
break;
}
if (version)
{
dprintf(file, "ssl_version=%s\n", version);
}
}
close(file);
return true;
}
bool server_serialize(SERVER *server)
{
bool rval = false;
char filename[PATH_MAX];
snprintf(filename, sizeof(filename), "%s/%s.cnf.tmp", get_config_persistdir(),
server->unique_name);
if (unlink(filename) == -1 && errno != ENOENT)
{
char err[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to remove temporary server configuration at '%s': %d, %s",
filename, errno, strerror_r(errno, err, sizeof(err)));
}
else if (create_server_config(server, filename))
{
char final_filename[PATH_MAX];
strcpy(final_filename, filename);
char *dot = strrchr(final_filename, '.');
ss_dassert(dot);
*dot = '\0';
if (rename(filename, final_filename) == 0)
{
rval = true;
}
else
{
char err[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to rename temporary server configuration at '%s': %d, %s",
filename, errno, strerror_r(errno, err, sizeof(err)));
}
}
return rval;
}
bool server_is_ssl_parameter(const char *key)
{
// TODO: Implement this
return false;
}
void server_update_ssl(SERVER *server, const char *key, const char *value)
{
// TODO: Implement this
}

View File

@ -150,6 +150,7 @@ service_alloc(const char *servname, const char *router)
service->capabilities = service->router->getCapabilities();
service->client_count = 0;
service->n_dbref = 0;
service->name = (char*)servname;
service->routerModule = (char*)router;
service->users_from_all = false;
@ -743,7 +744,7 @@ int serviceHasProtocol(SERVICE *service, const char *protocol,
* @param server Server to refer to
* @return Server reference or NULL on error
*/
static SERVER_REF* server_ref_alloc(SERVER *server)
static SERVER_REF* server_ref_create(SERVER *server)
{
SERVER_REF *sref = MXS_MALLOC(sizeof(SERVER_REF));
@ -753,6 +754,7 @@ static SERVER_REF* server_ref_alloc(SERVER *server)
sref->server = server;
sref->weight = SERVICE_BASE_SERVER_WEIGHT;
sref->connections = 0;
sref->active = true;
}
return sref;
@ -767,28 +769,71 @@ static SERVER_REF* server_ref_alloc(SERVER *server)
void
serviceAddBackend(SERVICE *service, SERVER *server)
{
SERVER_REF *sref = server_ref_alloc(server);
SERVER_REF *new_ref = server_ref_create(server);
if (sref)
if (new_ref)
{
spinlock_acquire(&service->spin);
service->n_dbref++;
if (service->dbref)
{
SERVER_REF *ref = service->dbref;
while (ref->next)
SERVER_REF *prev = ref;
while (ref)
{
if (ref->server == server)
{
ref->active = true;
break;
}
prev = ref;
ref = ref->next;
}
ref->next = sref;
if (ref == NULL)
{
/** A new server that hasn't been used by this service */
atomic_synchronize();
prev->next = new_ref;
}
}
else
{
service->dbref = sref;
atomic_synchronize();
service->dbref = new_ref;
}
spinlock_release(&service->spin);
}
}
/**
* @brief Remove a server from a service
*
* This function sets the server reference into an inactive state. This does not
* remove the server from the list or free any of the memory.
*
* @param service Service to modify
* @param server Server to remove
*/
void serviceRemoveBackend(SERVICE *service, const SERVER *server)
{
spinlock_acquire(&service->spin);
for (SERVER_REF *ref = service->dbref; ref; ref = ref->next)
{
if (ref->server == server)
{
ref->active = false;
service->n_dbref--;
break;
}
}
spinlock_release(&service->spin);
}
/**
* Test if a server is part of a service
*

View File

@ -37,6 +37,10 @@
#include <maxscale/server.h>
#include <maxscale/log_manager.h>
#include <maxscale/gwdirs.h>
// This is pretty ugly but it's required to test internal functions
#include "../config.c"
/**
* test1 Allocate a server and do lots of other things
*
@ -103,11 +107,84 @@ test1()
}
#define TEST(A, B) do { if(!(A)){ printf(B"\n"); return false; }} while(false)
bool test_load_config(const char *input, SERVER *server)
{
DUPLICATE_CONTEXT dcontext;
if (duplicate_context_init(&dcontext))
{
CONFIG_CONTEXT ccontext = {.object = ""};
if (config_load_single_file(input, &dcontext, &ccontext))
{
CONFIG_CONTEXT *obj = ccontext.next;
CONFIG_PARAMETER *param = obj->parameters;
TEST(strcmp(obj->object, server->unique_name) == 0, "Server names differ");
TEST(strcmp(server->name, config_get_param(param, "address")->value) == 0, "Server addresses differ");
TEST(strcmp(server->protocol, config_get_param(param, "protocol")->value) == 0, "Server protocols differ");
TEST(strcmp(server->authenticator, config_get_param(param, "authenticator")->value) == 0,
"Server authenticators differ");
TEST(strcmp(server->auth_options, config_get_param(param, "authenticator_options")->value) == 0,
"Server authenticator options differ");
TEST(server->port == atoi(config_get_param(param, "port")->value), "Server ports differ");
TEST(create_new_server(obj) == 0, "Failed to create server from loaded config");
}
}
return true;
}
bool test_serialize()
{
char name[] = "serialized-server";
char config_name[] = "serialized-server.cnf";
char old_config_name[] = "serialized-server.cnf.old";
char *persist_dir = MXS_STRDUP_A("./");
set_config_persistdir(persist_dir);
SERVER *server = server_alloc("127.0.0.1", "HTTPD", 9876, "NullAuthAllow", "fake=option");
TEST(server, "Server allocation failed");
server_set_unique_name(server, name);
/** Make sure the files don't exist */
unlink(config_name);
unlink(old_config_name);
/** Serialize server to disk */
TEST(server_serialize(server), "Failed to synchronize original server");
/** Load it again */
TEST(test_load_config(config_name, server), "Failed to load the serialized server");
/** We should have two identical servers */
SERVER *created = server_find_by_unique_name(name);
TEST(created->next == server, "We should end up with two servers");
rename(config_name, old_config_name);
/** Serialize the loaded server to disk */
TEST(server_serialize(created), "Failed to synchronize the copied server");
/** Check that they serialize to identical files */
char cmd[1024];
sprintf(cmd, "diff ./%s ./%s", config_name, old_config_name);
TEST(system(cmd) == 0, "The files are not identical");
return true;
}
int main(int argc, char **argv)
{
int result = 0;
result += test1();
if (!test_serialize())
{
result++;
}
exit(result);
}

View File

@ -2649,17 +2649,12 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
bool check_service_permissions(SERVICE* service)
{
if (is_internal_service(service->routerModule) ||
config_get_global_options()->skip_permission_checks)
config_get_global_options()->skip_permission_checks ||
service->dbref == NULL) // No servers to check
{
return true;
}
if (service->dbref == NULL)
{
MXS_ERROR("[%s] Service is missing the servers parameter.", service->name);
return false;
}
char *user, *password;
if (serviceGetUser(service, &user, &password) == 0)

View File

@ -782,9 +782,119 @@ static bool cache_rule_compare_n(CACHE_RULE *self, const char *value, size_t len
static bool cache_rule_matches_column(CACHE_RULE *self, const char *default_db, const GWBUF *query)
{
ss_dassert(self->attribute == CACHE_ATTRIBUTE_COLUMN);
ss_info_dassert(!true, "Column matching not implemented yet.");
return false;
// TODO: Do this "parsing" when the rule item is created.
char buffer[strlen(self->value) + 1];
strcpy(buffer, self->value);
const char* rule_column = NULL;
const char* rule_table = NULL;
const char* rule_database = NULL;
char* dot1 = strchr(buffer, '.');
char* dot2 = dot1 ? strchr(buffer, '.') : NULL;
if (dot1 && dot2)
{
rule_database = buffer;
*dot1 = 0;
rule_table = dot1 + 1;
*dot2 = 0;
rule_column = dot2 + 1;
}
else if (dot1)
{
rule_table = buffer;
*dot1 = 0;
rule_column = dot1 + 1;
}
else
{
rule_column = buffer;
}
const QC_FIELD_INFO *infos;
size_t n_infos;
int n_tables;
char** tables = qc_get_table_names((GWBUF*)query, &n_tables, false);
const char* default_table = NULL;
if (n_tables == 1)
{
// Only if we have exactly one table can we assume anything
// about a table that has not been mentioned explicitly.
default_table = tables[0];
}
qc_get_field_info((GWBUF*)query, &infos, &n_infos);
bool matches = false;
size_t i = 0;
while (!matches && (i < n_infos))
{
const QC_FIELD_INFO *info = (infos + i);
if ((strcmp(info->column, rule_column) == 0) || (strcmp(info->column, "*") == 0))
{
if (rule_table)
{
const char* check_table = info->table ? info->table : default_table;
if (check_table && (strcmp(check_table, rule_table) == 0))
{
if (rule_database)
{
const char *check_database = info->database ? info->database : default_db;
if (check_database && (strcmp(check_database, rule_database) == 0))
{
matches = true;
}
else
{
// If the rules specifies a database and either the database
// does not match or we do not know the database, the rule
// does *not* match.
matches = false;
}
}
else
{
// If the rule specifies no table, then if the table and column matches,
// the rule matches.
matches = true;
}
}
else
{
// The rules specifies a table and either the table does not match
// or we do not know the table, the rule does *not* match.
matches = false;
}
}
else
{
// If the rule specifies no table, then if the column matches, the
// rule matches.
matches = true;
}
}
++i;
}
if (tables)
{
for (i = 0; i < (size_t)n_tables; ++i)
{
MXS_FREE(tables[i]);
}
MXS_FREE(tables);
}
return matches;
}
/**

View File

@ -14,12 +14,37 @@
#include <stdlib.h>
#include "rules.h"
#include <maxscale/log_manager.h>
#include <maxscale/query_classifier.h>
#include <maxscale/protocol/mysql.h>
#if !defined(SS_DEBUG)
#define SS_DEBUG
#endif
#include <maxscale/debug.h>
struct test_case
GWBUF* create_gwbuf(const char* s)
{
size_t query_len = strlen(s);
size_t payload_len = query_len + 1;
size_t gwbuf_len = MYSQL_HEADER_LEN + payload_len;
GWBUF* gwbuf = gwbuf_alloc(gwbuf_len);
ss_dassert(gwbuf);
*((unsigned char*)((char*)GWBUF_DATA(gwbuf))) = payload_len;
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 1)) = (payload_len >> 8);
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 2)) = (payload_len >> 16);
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 3)) = 0x00;
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 4)) = 0x03;
memcpy((char*)GWBUF_DATA(gwbuf) + MYSQL_HEADER_LEN + 1, s, query_len);
return gwbuf;
}
//
// Test user rules. Basically tests that a user specification is translated
// into the correct pcre2 regex.
//
struct user_test_case
{
const char* json;
struct
@ -29,31 +54,33 @@ struct test_case
} expect;
};
#define TEST_CASE(op_from, from, op_to, to) \
#define USER_TEST_CASE(op_from, from, op_to, to) \
{ "{ \"use\": [ { \"attribute\": \"user\", \"op\": \"" #op_from "\", \"value\": \"" #from "\" } ] }",\
{ op_to, #to } }
const struct test_case test_cases[] =
#define COLUMN_
const struct user_test_case user_test_cases[] =
{
TEST_CASE(=, bob, CACHE_OP_LIKE, bob@.*),
TEST_CASE(=, 'bob', CACHE_OP_LIKE, bob@.*),
TEST_CASE(=, bob@%, CACHE_OP_LIKE, bob@.*),
TEST_CASE(=, 'bob'@'%.52', CACHE_OP_LIKE, bob@.*\\.52),
TEST_CASE(=, bob@127.0.0.1, CACHE_OP_EQ, bob@127.0.0.1),
TEST_CASE(=, b*b@127.0.0.1, CACHE_OP_EQ, b*b@127.0.0.1),
TEST_CASE(=, b*b@%.0.0.1, CACHE_OP_LIKE, b\\*b@.*\\.0\\.0\\.1),
TEST_CASE(=, b*b@%.0.%.1, CACHE_OP_LIKE, b\\*b@.*\\.0\\..*\\.1),
USER_TEST_CASE(=, bob, CACHE_OP_LIKE, bob@.*),
USER_TEST_CASE(=, 'bob', CACHE_OP_LIKE, bob@.*),
USER_TEST_CASE(=, bob@%, CACHE_OP_LIKE, bob@.*),
USER_TEST_CASE(=, 'bob'@'%.52', CACHE_OP_LIKE, bob@.*\\.52),
USER_TEST_CASE(=, bob@127.0.0.1, CACHE_OP_EQ, bob@127.0.0.1),
USER_TEST_CASE(=, b*b@127.0.0.1, CACHE_OP_EQ, b*b@127.0.0.1),
USER_TEST_CASE(=, b*b@%.0.0.1, CACHE_OP_LIKE, b\\*b@.*\\.0\\.0\\.1),
USER_TEST_CASE(=, b*b@%.0.%.1, CACHE_OP_LIKE, b\\*b@.*\\.0\\..*\\.1),
};
const size_t n_test_cases = sizeof(test_cases) / sizeof(test_cases[0]);
const size_t n_user_test_cases = sizeof(user_test_cases) / sizeof(user_test_cases[0]);
int test()
int test_user()
{
int errors = 0;
for (int i = 0; i < n_test_cases; ++i)
for (int i = 0; i < n_user_test_cases; ++i)
{
const struct test_case *test_case = &test_cases[i];
const struct user_test_case *test_case = &user_test_cases[i];
CACHE_RULES *rules = cache_rules_parse(test_case->json, 0);
ss_dassert(rules);
@ -78,9 +105,86 @@ int test()
rule->value);
++errors;
}
cache_rules_free(rules);
}
return errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
return errors;
}
//
//
//
struct store_test_case
{
const char *rule; // The rule in JSON format.
bool matches; // Whether or not the rule should match the query.
const char *default_db; // The current default db.
const char *query; // The query to be matched against the rule.
};
#define STORE_TEST_CASE(attribute, op, value, matches, default_db, query) \
{ "{ \"store\": [ { \"attribute\": \"" attribute "\", \"op\": \"" op "\", \"value\": \"" value "\" } ] }",\
matches, default_db, query }
// In the following,
// true: The query SHOULD match the rule,
// false: The query should NOT match the rule.
const struct store_test_case store_test_cases[] =
{
STORE_TEST_CASE("column", "=", "a", true, NULL, "SELECT a FROM tbl"),
STORE_TEST_CASE("column", "=", "b", false, NULL, "SELECT a FROM tbl")
};
const size_t n_store_test_cases = sizeof(store_test_cases) / sizeof(store_test_cases[0]);
int test_store()
{
int errors = 0;
for (int i = 0; i < n_store_test_cases; ++i)
{
const struct store_test_case *test_case = &store_test_cases[i];
CACHE_RULES *rules = cache_rules_parse(test_case->rule, 0);
ss_dassert(rules);
CACHE_RULE *rule = rules->store_rules;
ss_dassert(rule);
GWBUF *packet = create_gwbuf(test_case->query);
bool matches = cache_rules_should_store(rules, test_case->default_db, packet);
if (matches != test_case->matches)
{
printf("Query : %s\n"
"Rule : %s\n"
"Expected: %s\n"
"Result : %s\n\n",
test_case->query,
test_case->rule,
test_case->matches ? "A match" : "Not a match",
matches ? "A match" : "Not a match");
}
gwbuf_free(packet);
cache_rules_free(rules);
}
return errors;
}
int test()
{
int errors = 0;
errors += test_user();
errors += test_store();
return errors ? EXIT_FAILURE : EXIT_SUCCESS;
}
int main()
@ -89,7 +193,14 @@ int main()
if (mxs_log_init(NULL, ".", MXS_LOG_TARGET_DEFAULT))
{
rc = test();
if (qc_init("qc_sqlite", ""))
{
rc = test();
}
else
{
MXS_ERROR("Could not initialize query classifier.");
}
mxs_log_finish();
}

View File

@ -55,7 +55,7 @@ typedef struct cli_instance
* The CLI_SESSION structure. As CLI_SESSION is created for each user that logs into
* the DEBUG CLI.
*/
enum { CMDBUFLEN = 80 };
#define CMDBUFLEN 2048
typedef struct cli_session
{

View File

@ -215,7 +215,7 @@ bool init_server_info(MYSQL_MONITOR *handle, MONITOR_SERVERS *database)
while (database)
{
/** Delete any existing structures and replace them with empty ones */
hashtable_delete(handle->server_info, database->server);
hashtable_delete(handle->server_info, database->server->unique_name);
if (!hashtable_add(handle->server_info, database->server->unique_name, &info))
{
@ -688,20 +688,9 @@ monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database)
MYSQL_MONITOR* handle = mon->handle;
MYSQL_ROW row;
MYSQL_RES *result;
char *uname = mon->user;
unsigned long int server_version = 0;
char *server_string;
if (database->server->monuser != NULL)
{
uname = database->server->monuser;
}
if (uname == NULL)
{
return;
}
/* Don't probe servers in maintenance mode */
if (SERVER_IN_MAINT(database->server))
{

File diff suppressed because it is too large Load Diff

View File

@ -32,18 +32,6 @@
MXS_BEGIN_DECLS
/**
* Internal structure used to define the set of backend servers we are routing
* connections to. This provides the storage for routing module specific data
* that is required for each of the backend servers.
*/
typedef struct backend
{
SERVER *server; /*< The server itself */
int current_connection_count; /*< Number of connections to the server */
int weight; /*< Desired routing weight */
} BACKEND;
/**
* The client session structure used within this router.
*/
@ -55,7 +43,7 @@ typedef struct router_client_session
SPINLOCK rses_lock; /*< protects rses_deleted */
int rses_versno; /*< even = no active update, else odd */
bool rses_closed; /*< true when closeSession is called */
BACKEND *backend; /*< Backend used by the client session */
SERVER_REF *backend; /*< Backend used by the client session */
DCB *backend_dcb; /*< DCB Connection to the backend */
DCB *client_dcb; /**< Client DCB */
struct router_client_session *next;
@ -79,9 +67,7 @@ typedef struct
typedef struct router_instance
{
SERVICE *service; /*< Pointer to the service using this router */
ROUTER_CLIENT_SES *connections; /*< Link list of all the client connections */
SPINLOCK lock; /*< Spinlock for the instance data */
BACKEND **servers; /*< List of backend servers */
unsigned int bitmask; /*< Bitmask to apply to server->status */
unsigned int bitvalue; /*< Required value of server->status */
ROUTER_STATS stats; /*< Statistics for this router */

View File

@ -133,7 +133,7 @@ static bool rses_begin_locked_router_action(ROUTER_CLIENT_SES* rses);
static void rses_end_locked_router_action(ROUTER_CLIENT_SES* rses);
static BACKEND *get_root_master(BACKEND **servers);
static SERVER_REF *get_root_master(SERVER_REF *servers);
static int handle_state_switch(DCB* dcb, DCB_REASON reason, void * routersession);
static SPINLOCK instlock;
static ROUTER_INSTANCE *instances;
@ -179,14 +179,6 @@ static inline void free_readconn_instance(ROUTER_INSTANCE *router)
{
if (router)
{
if (router->servers)
{
for (int i = 0; router->servers[i]; i++)
{
MXS_FREE(router->servers[i]);
}
}
MXS_FREE(router->servers);
MXS_FREE(router);
}
}
@ -215,37 +207,6 @@ createInstance(SERVICE *service, char **options)
inst->service = service;
spinlock_init(&inst->lock);
/*
* We need an array of the backend servers in the instance structure so
* that we can maintain a count of the number of connections to each
* backend server.
*/
for (sref = service->dbref, n = 0; sref; sref = sref->next)
{
n++;
}
inst->servers = (BACKEND **) MXS_CALLOC(n + 1, sizeof(BACKEND *));
if (!inst->servers)
{
free_readconn_instance(inst);
return NULL;
}
for (sref = service->dbref, n = 0; sref; sref = sref->next)
{
if ((inst->servers[n] = MXS_MALLOC(sizeof(BACKEND))) == NULL)
{
free_readconn_instance(inst);
return NULL;
}
inst->servers[n]->server = sref->server;
inst->servers[n]->current_connection_count = 0;
inst->servers[n]->weight = sref->weight;
n++;
}
inst->servers[n] = NULL;
/*
* Process the options
*/
@ -330,9 +291,9 @@ newSession(ROUTER *instance, SESSION *session)
{
ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *) instance;
ROUTER_CLIENT_SES *client_rses;
BACKEND *candidate = NULL;
SERVER_REF *candidate = NULL;
int i;
BACKEND *master_host = NULL;
SERVER_REF *master_host = NULL;
MXS_DEBUG("%lu [newSession] new router session with session "
"%p, and inst %p.",
@ -340,7 +301,6 @@ newSession(ROUTER *instance, SESSION *session)
session,
inst);
client_rses = (ROUTER_CLIENT_SES *) MXS_CALLOC(1, sizeof(ROUTER_CLIENT_SES));
if (client_rses == NULL)
@ -357,7 +317,7 @@ newSession(ROUTER *instance, SESSION *session)
/**
* Find the Master host from available servers
*/
master_host = get_root_master(inst->servers);
master_host = get_root_master(inst->service->dbref);
/**
* Find a backend server to connect to. This is the extent of the
@ -377,52 +337,43 @@ newSession(ROUTER *instance, SESSION *session)
* become the new candidate. This has the effect of spreading the
* connections over different servers during periods of very low load.
*/
for (i = 0; inst->servers[i]; i++)
for (SERVER_REF *ref = inst->service->dbref; ref; ref = ref->next)
{
if (inst->servers[i])
if (!SERVER_REF_IS_ACTIVE(ref) || SERVER_IN_MAINT(ref->server) || ref->weight == 0)
{
continue;
}
else
{
MXS_DEBUG("%lu [newSession] Examine server in port %d with "
"%d connections. Status is %s, "
"inst->bitvalue is %d",
pthread_self(),
inst->servers[i]->server->port,
inst->servers[i]->current_connection_count,
STRSRVSTATUS(inst->servers[i]->server),
ref->server->port,
ref->connections,
STRSRVSTATUS(ref->server),
inst->bitmask);
}
if (SERVER_IN_MAINT(inst->servers[i]->server))
{
continue;
}
if (inst->servers[i]->weight == 0)
{
continue;
}
/* Check server status bits against bitvalue from router_options */
if (inst->servers[i] &&
SERVER_IS_RUNNING(inst->servers[i]->server) &&
(inst->servers[i]->server->status & inst->bitmask & inst->bitvalue))
if (ref && SERVER_IS_RUNNING(ref->server) &&
(ref->server->status & inst->bitmask & inst->bitvalue))
{
if (master_host)
{
if (inst->servers[i] == master_host && (inst->bitvalue & SERVER_SLAVE))
if (ref == master_host && (inst->bitvalue & SERVER_SLAVE))
{
/* skip root Master here, as it could also be slave of an external server
* that is not in the configuration.
* Intermediate masters (Relay Servers) are also slave and will be selected
* as Slave(s)
/* Skip root master here, as it could also be slave of an external server that
* is not in the configuration. Intermediate masters (Relay Servers) are also
* slave and will be selected as Slave(s)
*/
continue;
}
if (inst->servers[i] == master_host && (inst->bitvalue & SERVER_MASTER))
if (ref == master_host && (inst->bitvalue & SERVER_MASTER))
{
/* If option is "master" return only the root Master as there
* could be intermediate masters (Relay Servers)
* and they must not be selected.
/* If option is "master" return only the root Master as there could be
* intermediate masters (Relay Servers) and they must not be selected.
*/
candidate = master_host;
@ -431,8 +382,7 @@ newSession(ROUTER *instance, SESSION *session)
}
else
{
/* master_host is NULL, no master server.
* If requested router_option is 'master'
/* Master_host is NULL, no master server. If requested router_option is 'master'
* candidate wll be NULL.
*/
if (inst->bitvalue & SERVER_MASTER)
@ -442,40 +392,31 @@ newSession(ROUTER *instance, SESSION *session)
}
}
/* If no candidate set, set first running server as
our initial candidate server */
/* If no candidate set, set first running server as our initial candidate server */
if (candidate == NULL)
{
candidate = inst->servers[i];
candidate = ref;
}
else if (((inst->servers[i]->current_connection_count + 1)
* 1000) / inst->servers[i]->weight <
((candidate->current_connection_count + 1) *
1000) / candidate->weight)
else if (((ref->connections + 1) * 1000) / ref->weight <
((candidate->connections + 1) * 1000) / candidate->weight)
{
/* This running server has fewer
connections, set it as a new candidate */
candidate = inst->servers[i];
/* This running server has fewer connections, set it as a new candidate */
candidate = ref;
}
else if (((inst->servers[i]->current_connection_count + 1)
* 1000) / inst->servers[i]->weight ==
((candidate->current_connection_count + 1) *
1000) / candidate->weight &&
inst->servers[i]->server->stats.n_connections <
candidate->server->stats.n_connections)
else if (((ref->connections + 1) * 1000) / ref->weight ==
((candidate->connections + 1) * 1000) / candidate->weight &&
ref->server->stats.n_connections < candidate->server->stats.n_connections)
{
/* This running server has the same number
of connections currently as the candidate
but has had fewer connections over time
than candidate, set this server to candidate*/
candidate = inst->servers[i];
/* This running server has the same number of connections currently as the candidate
but has had fewer connections over time than candidate, set this server to
candidate*/
candidate = ref;
}
}
}
/* There is no candidate server here!
* With router_option=slave a master_host could be set, so route traffic there.
* Otherwise, just clean up and return NULL
/* If we haven't found a proper candidate yet but a master server is available, we'll pick that
* with the assumption that it is "better" than a slave.
*/
if (!candidate)
{
@ -485,9 +426,8 @@ newSession(ROUTER *instance, SESSION *session)
}
else
{
MXS_ERROR("Failed to create new routing session. "
"Couldn't find eligible candidate server. Freeing "
"allocated resources.");
MXS_ERROR("Failed to create new routing session. Couldn't find eligible"
" candidate server. Freeing allocated resources.");
MXS_FREE(client_rses);
return NULL;
}
@ -497,48 +437,32 @@ newSession(ROUTER *instance, SESSION *session)
* We now have the server with the least connections.
* Bump the connection count for this server
*/
atomic_add(&candidate->current_connection_count, 1);
client_rses->backend = candidate;
MXS_DEBUG("%lu [newSession] Selected server in port %d. "
"Connections : %d\n",
pthread_self(),
candidate->server->port,
candidate->current_connection_count);
/*
* Open a backend connection, putting the DCB for this
* connection in the client_rses->backend_dcb
*/
client_rses->backend_dcb = dcb_connect(candidate->server,
session,
/** Open the backend connection */
client_rses->backend_dcb = dcb_connect(candidate->server, session,
candidate->server->protocol);
if (client_rses->backend_dcb == NULL)
{
atomic_add(&candidate->current_connection_count, -1);
/** The failure is reported in dcb_connect() */
MXS_FREE(client_rses);
return NULL;
}
dcb_add_callback(
client_rses->backend_dcb,
atomic_add(&candidate->connections, 1);
// TODO: Remove this as it is never called
dcb_add_callback(client_rses->backend_dcb,
DCB_REASON_NOT_RESPONDING,
&handle_state_switch,
client_rses);
inst->stats.n_sessions++;
/**
* Add this session to the list of active sessions.
*/
spinlock_acquire(&inst->lock);
client_rses->next = inst->connections;
inst->connections = client_rses;
spinlock_release(&inst->lock);
CHK_CLIENT_RSES(client_rses);
MXS_INFO("Readconnroute: New session for server %s. "
"Connections : %d",
candidate->server->unique_name,
candidate->current_connection_count);
MXS_INFO("Readconnroute: New session for server %s. Connections : %d",
candidate->server->unique_name, candidate->connections);
return(void *) client_rses;
}
@ -563,42 +487,11 @@ newSession(ROUTER *instance, SESSION *session)
static void freeSession(ROUTER* router_instance, void* router_client_ses)
{
ROUTER_INSTANCE* router = (ROUTER_INSTANCE *) router_instance;
ROUTER_CLIENT_SES* router_cli_ses =
(ROUTER_CLIENT_SES *) router_client_ses;
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *) router_client_ses;
ss_debug(int prev_val = ) atomic_add(&router_cli_ses->backend->current_connection_count, -1);
ss_debug(int prev_val = ) atomic_add(&router_cli_ses->backend->connections, -1);
ss_dassert(prev_val > 0);
spinlock_acquire(&router->lock);
if (router->connections == router_cli_ses)
{
router->connections = router_cli_ses->next;
}
else
{
ROUTER_CLIENT_SES *ptr = router->connections;
while (ptr != NULL && ptr->next != router_cli_ses)
{
ptr = ptr->next;
}
if (ptr != NULL)
{
ptr->next = router_cli_ses->next;
}
}
spinlock_release(&router->lock);
MXS_DEBUG("%lu [freeSession] Unlinked router_client_session %p from "
"router %p and from server on port %d. Connections : %d. ",
pthread_self(),
router_cli_ses,
router,
router_cli_ses->backend->server->port,
prev_val - 1);
MXS_FREE(router_cli_ses);
}
@ -640,6 +533,29 @@ closeSession(ROUTER *instance, void *router_session)
}
}
/** Log routing failure due to closed session */
static void log_closed_session(mysql_server_cmd_t mysql_command, bool is_closed,
SERVER_REF *ref)
{
char msg[MAX_SERVER_NAME_LEN + 200] = ""; // Extra space for message
if (is_closed)
{
sprintf(msg, "Session is closed.");
}
else if (SERVER_IS_DOWN(ref->server))
{
sprintf(msg, "Server '%s' is down.", ref->server->unique_name);
}
else if (!SERVER_REF_IS_ACTIVE(ref))
{
sprintf(msg, "Server '%s' was removed from the service.", ref->server->unique_name);
}
MXS_ERROR("Failed to route MySQL command %d to backend server. %s",
mysql_command, msg);
}
/**
* We have data from the client, we must route it to the backend.
* This is simply a case of sending it to the connection that was
@ -655,7 +571,7 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
{
ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *) instance;
ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *) router_session;
int rc;
int rc = 0;
DCB* backend_dcb;
MySQLProtocol *proto = (MySQLProtocol*)router_cli_ses->client_dcb->protocol;
mysql_server_cmd_t mysql_command = proto->current_command;
@ -684,16 +600,11 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
}
if (rses_is_closed || backend_dcb == NULL ||
!SERVER_REF_IS_ACTIVE(router_cli_ses->backend) ||
SERVER_IS_DOWN(router_cli_ses->backend->server))
{
MXS_ERROR("Failed to route MySQL command %d to backend "
"server.%s",
mysql_command, rses_is_closed ? " Session is closed." : "");
rc = 0;
while ((queue = GWBUF_CONSUME_ALL(queue)) != NULL)
{
;
}
log_closed_session(mysql_command, rses_is_closed, router_cli_ses->backend);
gwbuf_free(queue);
goto return_rc;
}
@ -738,23 +649,12 @@ static void
diagnostics(ROUTER *router, DCB *dcb)
{
ROUTER_INSTANCE *router_inst = (ROUTER_INSTANCE *) router;
ROUTER_CLIENT_SES *session;
int i = 0;
BACKEND *backend;
char *weightby;
spinlock_acquire(&router_inst->lock);
session = router_inst->connections;
while (session)
{
i++;
session = session->next;
}
spinlock_release(&router_inst->lock);
dcb_printf(dcb, "\tNumber of router sessions: %d\n",
router_inst->stats.n_sessions);
dcb_printf(dcb, "\tCurrent no. of router sessions: %d\n", i);
dcb_printf(dcb, "\tCurrent no. of router sessions: %d\n",
router_inst->service->stats.n_current);
dcb_printf(dcb, "\tNumber of queries forwarded: %d\n",
router_inst->stats.n_queries);
if ((weightby = serviceGetWeightingParameter(router_inst->service))
@ -765,15 +665,13 @@ diagnostics(ROUTER *router, DCB *dcb)
weightby);
dcb_printf(dcb,
"\t\tServer Target %% Connections\n");
for (i = 0; router_inst->servers[i]; i++)
for (SERVER_REF *ref = router_inst->service->dbref; ref; ref = ref->next)
{
backend = router_inst->servers[i];
dcb_printf(dcb, "\t\t%-20s %3.1f%% %d\n",
backend->server->unique_name,
(float) backend->weight / 10,
backend->current_connection_count);
ref->server->unique_name,
(float) ref->weight / 10,
ref->connections);
}
}
}
@ -934,28 +832,28 @@ static uint64_t getCapabilities(void)
*
*/
static BACKEND *get_root_master(BACKEND **servers)
static SERVER_REF *get_root_master(SERVER_REF *servers)
{
int i = 0;
BACKEND *master_host = NULL;
SERVER_REF *master_host = NULL;
for (i = 0; servers[i]; i++)
for (SERVER_REF *ref = servers; ref; ref = ref->next)
{
if (servers[i] && (servers[i]->server->status & (SERVER_MASTER | SERVER_MAINT)) == SERVER_MASTER)
if (ref->active && SERVER_IS_MASTER(ref->server))
{
if (master_host == NULL)
{
master_host = servers[i];
master_host = ref;
}
else if (servers[i]->server->depth < master_host->server->depth ||
(servers[i]->server->depth == master_host->server->depth &&
servers[i]->weight > master_host->weight))
else if (ref->server->depth < master_host->server->depth ||
(ref->server->depth == master_host->server->depth &&
ref->weight > master_host->weight))
{
/**
* This master has a lower depth than the candidate master or
* the depths are equal but this master has a higher weight
*/
master_host = servers[i];
master_host = ref;
}
}
}

View File

@ -43,7 +43,7 @@ MODULE_INFO info =
* by the entry point functions. Some of these are used by functions in other
* modules of the read write split router, others are used only within this
* module.
*
*
* @verbatim
* Revision History
*
@ -65,6 +65,9 @@ MODULE_INFO info =
* @endverbatim
*/
/** Maximum number of slaves */
#define MAX_SLAVE_COUNT 255
static char *version_str = "V1.1.0";
/*
@ -105,13 +108,6 @@ static ROUTER_OBJECT MyObject =
NULL
};
/*
* A couple of static variables that are used throughout the router
*/
static SPINLOCK instlock;
static ROUTER_INSTANCE *instances;
/*
* Declaration of functions that are used only within this module, and are
* not part of the API.
@ -127,10 +123,9 @@ static void handle_error_reply_client(SESSION *ses, ROUTER_CLIENT_SES *rses,
static bool handle_error_new_connection(ROUTER_INSTANCE *inst,
ROUTER_CLIENT_SES **rses,
DCB *backend_dcb, GWBUF *errmsg);
static int router_get_servercount(ROUTER_INSTANCE *inst);
static bool have_enough_servers(ROUTER_CLIENT_SES **p_rses, const int min_nsrv,
static bool have_enough_servers(ROUTER_CLIENT_SES *rses, const int min_nsrv,
int router_nsrv, ROUTER_INSTANCE *router);
static bool create_backends(ROUTER_CLIENT_SES *rses, backend_ref_t** dest, int* n_backend);
/**
* Implementation of the mandatory version entry point
*
@ -148,8 +143,6 @@ char *version()
void ModuleInit()
{
MXS_NOTICE("Initializing statement-based read/write split router module.");
spinlock_init(&instlock);
instances = NULL;
}
/**
@ -171,7 +164,7 @@ ROUTER_OBJECT *GetModuleObject()
/**
* @brief Create an instance of the read/write router (API).
*
*
* Create an instance of read/write statement router within the MaxScale. One
* instance of the router is required for each service that is defined in the
* configuration as using this router. One instance of the router will handle
@ -219,19 +212,12 @@ static ROUTER *createInstance(SERVICE *service, char **options)
router->rwsplit_config.rw_max_sescmd_history_size = 0;
}
int nservers = 0;
for (SERVER_REF *ref = service->dbref; ref; ref = ref->next)
{
nservers++;
}
/**
* Set default value for max_slave_connections as 100%. This way
* LEAST_CURRENT_OPERATIONS allows us to balance evenly across all the
* configured slaves.
*/
router->rwsplit_config.rw_max_slave_conn_count = nservers;
router->rwsplit_config.rw_max_slave_conn_count = MAX_SLAVE_COUNT;
if (router->rwsplit_config.rw_slave_select_criteria == UNDEFINED_CRITERIA)
{
@ -267,15 +253,6 @@ static ROUTER *createInstance(SERVICE *service, char **options)
{
refreshInstance(router, param);
}
/**
* We have completed the creation of the router data, so now
* insert this router into the linked list of routers
* that have been created with this module.
*/
spinlock_acquire(&instlock);
router->next = instances;
instances = router;
spinlock_release(&instlock);
return (ROUTER *)router;
}
@ -283,14 +260,14 @@ static ROUTER *createInstance(SERVICE *service, char **options)
/**
* @brief Associate a new session with this instance of the router (API).
*
* The session is used to store all the data required by the router for a
* particular client connection. The instance of the router that relates to a
* The session is used to store all the data required by the router for a
* particular client connection. The instance of the router that relates to a
* particular service is passed as the first parameter. The second parameter is
* the session that has been created in response to the request from a client
* for a connection. The passed session contains generic information; this
* function creates the session structure that holds router specific data.
* There is often a one to one relationship between sessions and router
* sessions, although it is possible to create configurations where a
* sessions, although it is possible to create configurations where a
* connection is handled by multiple routers, one after another.
*
* @param instance The router instance data
@ -299,23 +276,12 @@ static ROUTER *createInstance(SERVICE *service, char **options)
*/
static void *newSession(ROUTER *router_inst, SESSION *session)
{
backend_ref_t *backend_ref; /*< array of backend references (DCB,BACKEND,cursor) */
backend_ref_t *master_ref = NULL; /*< pointer to selected master */
ROUTER_CLIENT_SES *client_rses = NULL;
ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)router_inst;
bool succp;
int router_nservers = 0; /*< # of servers in total */
int max_nslaves; /*< max # of slaves used in this session */
int max_slave_rlag; /*< max allowed replication lag for any slave */
int i;
const int min_nservers = 1; /*< hard-coded for now */
client_rses = (ROUTER_CLIENT_SES *)MXS_CALLOC(1, sizeof(ROUTER_CLIENT_SES));
ROUTER_CLIENT_SES *client_rses = (ROUTER_CLIENT_SES *)MXS_CALLOC(1, sizeof(ROUTER_CLIENT_SES));
if (client_rses == NULL)
{
ss_dassert(false);
goto return_rses;
return NULL;
}
#if defined(SS_DEBUG)
client_rses->rses_chk_top = CHK_NUM_ROUTER_SES;
@ -324,119 +290,57 @@ static void *newSession(ROUTER *router_inst, SESSION *session)
client_rses->router = router;
client_rses->client_dcb = session->client_dcb;
/**
* If service config has been changed, reload config from service to
* router instance first.
*/
spinlock_acquire(&router->lock);
if (router->service->svc_config_version > router->rwsplit_version)
{
/** re-read all parameters to rwsplit config structure */
refreshInstance(router, NULL); /*< scan through all parameters */
/** increment rwsplit router's config version number */
router->rwsplit_version = router->service->svc_config_version;
/** Read options */
rwsplit_process_router_options(router, router->service->routerOptions);
}
/** Copy config struct from router instance */
memcpy(&client_rses->rses_config, &router->rwsplit_config, sizeof(rwsplit_config_t));
spinlock_release(&router->lock);
/**
* Set defaults to session variables.
*/
client_rses->rses_autocommit_enabled = true;
client_rses->rses_transaction_active = false;
client_rses->have_tmp_tables = false;
client_rses->forced_node = NULL;
spinlock_init(&client_rses->rses_lock);
memcpy(&client_rses->rses_config, &router->rwsplit_config, sizeof(client_rses->rses_config));
router_nservers = router_get_servercount(router);
int router_nservers = router->service->n_dbref;
const int min_nservers = 1; /*< hard-coded for now */
if (!have_enough_servers(&client_rses, min_nservers, router_nservers, router))
if (!have_enough_servers(client_rses, min_nservers, router_nservers, router))
{
goto return_rses;
MXS_FREE(client_rses);
return NULL;
}
/**
* Create backend reference objects for this session.
*/
backend_ref = (backend_ref_t *)MXS_CALLOC(1, router_nservers * sizeof(backend_ref_t));
backend_ref_t *backend_ref;
if (backend_ref == NULL)
if (!create_backends(client_rses, &backend_ref, &router_nservers))
{
/** log this */
MXS_FREE(client_rses);
MXS_FREE(backend_ref);
client_rses = NULL;
goto return_rses;
return NULL;
}
/**
* Initialize backend references with BACKEND ptr.
* Initialize session command cursors for each backend reference.
*/
i = 0;
for (SERVER_REF *sref = router->service->dbref; sref; sref = sref->next)
{
#if defined(SS_DEBUG)
backend_ref[i].bref_chk_top = CHK_NUM_BACKEND_REF;
backend_ref[i].bref_chk_tail = CHK_NUM_BACKEND_REF;
backend_ref[i].bref_sescmd_cur.scmd_cur_chk_top = CHK_NUM_SESCMD_CUR;
backend_ref[i].bref_sescmd_cur.scmd_cur_chk_tail = CHK_NUM_SESCMD_CUR;
#endif
backend_ref[i].bref_state = 0;
backend_ref[i].ref = sref;
/** store pointers to sescmd list to both cursors */
backend_ref[i].bref_sescmd_cur.scmd_cur_rses = client_rses;
backend_ref[i].bref_sescmd_cur.scmd_cur_active = false;
backend_ref[i].bref_sescmd_cur.scmd_cur_ptr_property =
&client_rses->rses_properties[RSES_PROP_TYPE_SESCMD];
backend_ref[i].bref_sescmd_cur.scmd_cur_cmd = NULL;
i++;
}
max_nslaves = rses_get_max_slavecount(client_rses, router_nservers);
max_slave_rlag = rses_get_max_replication_lag(client_rses);
spinlock_init(&client_rses->rses_lock);
int max_nslaves = rses_get_max_slavecount(client_rses, router_nservers);
int max_slave_rlag = rses_get_max_replication_lag(client_rses);
client_rses->rses_backend_ref = backend_ref;
client_rses->rses_nbackends = router_nservers; /*< # of backend servers */
/**
* Find a backend servers to connect to.
* This command requires that rsession's lock is held.
*/
succp = rses_begin_locked_router_action(client_rses);
if (!succp)
backend_ref_t *master_ref = NULL; /*< pointer to selected master */
if (!select_connect_backend_servers(&master_ref, backend_ref, router_nservers,
max_nslaves, max_slave_rlag,
client_rses->rses_config.rw_slave_select_criteria,
session, router))
{
/**
* Master and at least <min_nslaves> slaves must be found if the router is
* in the strict mode. If sessions without master are allowed, only
* <min_nslaves> slaves must be found.
*/
MXS_FREE(client_rses->rses_backend_ref);
MXS_FREE(client_rses);
client_rses = NULL;
goto return_rses;
}
succp = select_connect_backend_servers(&master_ref, backend_ref, router_nservers,
max_nslaves, max_slave_rlag,
client_rses->rses_config.rw_slave_select_criteria,
session, router);
rses_end_locked_router_action(client_rses);
/**
* Master and at least <min_nslaves> slaves must be found if the router is
* in the strict mode. If sessions without master are allowed, only
* <min_nslaves> slaves must be found.
*/
if (!succp)
{
MXS_FREE(client_rses->rses_backend_ref);
MXS_FREE(client_rses);
client_rses = NULL;
goto return_rses;
return NULL;
}
/** Copy backend pointers to router session. */
client_rses->rses_master_ref = master_ref;
client_rses->rses_backend_ref = backend_ref;
client_rses->rses_nbackends = router_nservers; /*< # of backend servers */
if (client_rses->rses_config.rw_max_slave_conn_percent)
{
@ -448,35 +352,15 @@ static void *newSession(ROUTER *router_inst, SESSION *session)
router->stats.n_sessions += 1;
/**
* Version is bigger than zero once initialized.
*/
atomic_add(&client_rses->rses_versno, 2);
ss_dassert(client_rses->rses_versno == 2);
/**
* Add this session to end of the list of active sessions in router.
*/
spinlock_acquire(&router->lock);
client_rses->next = router->connections;
router->connections = client_rses;
spinlock_release(&router->lock);
return_rses:
#if defined(SS_DEBUG)
if (client_rses != NULL)
{
CHK_CLIENT_RSES(client_rses);
}
#endif
return (void *)client_rses;
}
/**
* @brief Close a router session (API).
*
* Close a session with the router, this is the mechanism by which a router
* may cleanup data structure etc. The instance of the router that relates to
* the relevant service is passed, along with the router session that is to
*
* Close a session with the router, this is the mechanism by which a router
* may cleanup data structure etc. The instance of the router that relates to
* the relevant service is passed, along with the router session that is to
* be closed. Typically the function is used in conjunction with freeSession
* which will release the resources used by a router session (see below).
*
@ -485,66 +369,38 @@ return_rses:
*/
static void closeSession(ROUTER *instance, void *router_session)
{
ROUTER_CLIENT_SES *router_cli_ses;
backend_ref_t *backend_ref;
MXS_DEBUG("%lu [RWSplit:closeSession]", pthread_self());
/**
* router session can be NULL if newSession failed and it is discarding
* its connections and DCB's.
*/
if (router_session == NULL)
{
return;
}
router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
CHK_CLIENT_RSES(router_cli_ses);
backend_ref = router_cli_ses->rses_backend_ref;
/**
* Lock router client session for secure read and update.
*/
if (!router_cli_ses->rses_closed &&
rses_begin_locked_router_action(router_cli_ses))
if (!router_cli_ses->rses_closed && rses_begin_locked_router_action(router_cli_ses))
{
int i;
/**
* This sets router closed. Nobody is allowed to use router
* without checking this first.
* Mark router session as closed. @c rses_closed is checked at the start
* of every API function to quickly stop the processing of closed sessions.
*/
router_cli_ses->rses_closed = true;
for (i = 0; i < router_cli_ses->rses_nbackends; i++)
for (int i = 0; i < router_cli_ses->rses_nbackends; i++)
{
backend_ref_t *bref = &backend_ref[i];
DCB *dcb = bref->bref_dcb;
/** Close those which had been connected */
backend_ref_t *bref = &router_cli_ses->rses_backend_ref[i];
if (BREF_IS_IN_USE(bref))
{
/** This backend is in use and it needs to be closed */
DCB *dcb = bref->bref_dcb;
CHK_DCB(dcb);
#if defined(SS_DEBUG)
/**
* session must be moved to SESSION_STATE_STOPPING state before
* router session is closed.
*/
if (dcb->session != NULL)
{
ss_dassert(dcb->session->state == SESSION_STATE_STOPPING);
}
#endif
/** Clean operation counter in bref and in SERVER */
ss_dassert(dcb->session->state == SESSION_STATE_STOPPING);
if (BREF_IS_WAITING_RESULT(bref))
{
/** This backend was executing a query when the session was closed */
bref_clear_state(bref, BREF_WAITING_RESULT);
}
bref_clear_state(bref, BREF_IN_USE);
bref_set_state(bref, BREF_CLOSED);
/**
* closes protocol and dcb
*/
dcb_close(dcb);
/** decrease server current connection counters */
/** Decrease server reference connection count */
atomic_add(&bref->ref->connections, -1);
}
else
@ -561,15 +417,15 @@ static void closeSession(ROUTER *instance, void *router_session)
}
}
}
/** Unlock */
rses_end_locked_router_action(router_cli_ses);
}
}
/**
* @brief Free a router session (API).
*
* When a router session has been closed, freeSession can be called to free
*
* When a router session has been closed, freeSession can be called to free
* allocated resources.
*
* @param router_instance The router instance the session belongs to
@ -578,40 +434,13 @@ static void closeSession(ROUTER *instance, void *router_session)
*/
static void freeSession(ROUTER *router_instance, void *router_client_session)
{
ROUTER_CLIENT_SES *router_cli_ses;
ROUTER_INSTANCE *router;
int i;
router_cli_ses = (ROUTER_CLIENT_SES *)router_client_session;
router = (ROUTER_INSTANCE *)router_instance;
spinlock_acquire(&router->lock);
if (router->connections == router_cli_ses)
{
router->connections = router_cli_ses->next;
}
else
{
ROUTER_CLIENT_SES *ptr = router->connections;
while (ptr && ptr->next != router_cli_ses)
{
ptr = ptr->next;
}
if (ptr)
{
ptr->next = router_cli_ses->next;
}
}
spinlock_release(&router->lock);
ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *)router_client_session;
/**
* For each property type, walk through the list, finalize properties
* and free the allocated memory.
*/
for (i = RSES_PROP_TYPE_FIRST; i < RSES_PROP_TYPE_COUNT; i++)
for (int i = RSES_PROP_TYPE_FIRST; i < RSES_PROP_TYPE_COUNT; i++)
{
rses_property_t *p = router_cli_ses->rses_properties[i];
rses_property_t *q = p;
@ -623,11 +452,7 @@ static void freeSession(ROUTER *router_instance, void *router_client_session)
p = q;
}
}
/*
* We are no longer in the linked list, free
* all the memory and other resources associated
* to the client session.
*/
MXS_FREE(router_cli_ses->rses_backend_ref);
MXS_FREE(router_cli_ses);
return;
@ -685,20 +510,8 @@ static int routeQuery(ROUTER *instance, void *router_session, GWBUF *querybuf)
*/
static void diagnostics(ROUTER *instance, DCB *dcb)
{
ROUTER_CLIENT_SES *router_cli_ses;
ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance;
int i = 0;
char *weightby;
spinlock_acquire(&router->lock);
router_cli_ses = router->connections;
while (router_cli_ses)
{
i++;
router_cli_ses = router_cli_ses->next;
}
spinlock_release(&router->lock);
double master_pct = 0.0, slave_pct = 0.0, all_pct = 0.0;
if (router->stats.n_queries > 0)
@ -710,7 +523,8 @@ static void diagnostics(ROUTER *instance, DCB *dcb)
dcb_printf(dcb, "\tNumber of router sessions: %d\n",
router->stats.n_sessions);
dcb_printf(dcb, "\tCurrent no. of router sessions: %d\n", i);
dcb_printf(dcb, "\tCurrent no. of router sessions: %d\n",
router->service->stats.n_current);
dcb_printf(dcb, "\tNumber of queries forwarded: %d\n",
router->stats.n_queries);
dcb_printf(dcb, "\tNumber of queries forwarded to master: %d (%.2f%%)\n",
@ -731,9 +545,9 @@ static void diagnostics(ROUTER *instance, DCB *dcb)
for (SERVER_REF *ref = router->service->dbref; ref; ref = ref->next)
{
dcb_printf(dcb, "\t\t%-20s %3.1f%% %-6d %-6d %d\n",
ref->server->unique_name, (float)ref->weight / 10,
ref->server->stats.n_current, ref->connections,
ref->server->stats.n_current_ops);
ref->server->unique_name, (float)ref->weight / 10,
ref->server->stats.n_current, ref->connections,
ref->server->stats.n_current_ops);
}
}
}
@ -767,7 +581,7 @@ static void clientReply(ROUTER *instance, void *router_session, GWBUF *writebuf,
*/
if (!rses_begin_locked_router_action(router_cli_ses))
{
print_error_packet(router_cli_ses, writebuf, backend_dcb);
gwbuf_free(writebuf);
goto lock_failed;
}
/** Holding lock ensures that router session remains open */
@ -818,7 +632,7 @@ static void clientReply(ROUTER *instance, void *router_session, GWBUF *writebuf,
if (sescmd_cursor_is_active(scur))
{
check_session_command_reply(writebuf, scur, bref);
if (GWBUF_IS_TYPE_SESCMD_RESPONSE(writebuf))
{
/**
@ -898,7 +712,7 @@ static void clientReply(ROUTER *instance, void *router_session, GWBUF *writebuf,
CHK_GWBUF(bref->bref_pending_cmd);
if ((ret = bref->bref_dcb->func.write(bref->bref_dcb,
gwbuf_clone(bref->bref_pending_cmd))) == 1)
gwbuf_clone(bref->bref_pending_cmd))) == 1)
{
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;
atomic_add(&inst->stats.n_queries, 1);
@ -1014,12 +828,12 @@ void rses_end_locked_router_action(ROUTER_CLIENT_SES *rses)
/*
* @brief Clear one or more bits in the backend reference state
*
* The router session holds details of the backend servers that are
* involved in the routing for this particular service. Each backend
* server has a state bit string, and this function (along with
*
* The router session holds details of the backend servers that are
* involved in the routing for this particular service. Each backend
* server has a state bit string, and this function (along with
* bref_set_state) is used to manage the state.
*
*
* @param bref The backend reference to be modified
* @param state A bit string where the 1 bits indicate bits that should
* be turned off in the bref state.
@ -1063,12 +877,12 @@ void bref_clear_state(backend_ref_t *bref, bref_state_t state)
/*
* @brief Set one or more bits in the backend reference state
*
* The router session holds details of the backend servers that are
* involved in the routing for this particular service. Each backend
* server has a state bit string, and this function (along with
*
* The router session holds details of the backend servers that are
* involved in the routing for this particular service. Each backend
* server has a state bit string, and this function (along with
* bref_clear_state) is used to manage the state.
*
*
* @param bref The backend reference to be modified
* @param state A bit string where the 1 bits indicate bits that should
* be turned on in the bref state.
@ -1110,9 +924,9 @@ void bref_set_state(backend_ref_t *bref, bref_state_t state)
/**
* @brief Free resources belonging to a property
*
*
* Property is freed at the end of router client session.
*
*
* @param prop The property whose resources are to be released
*/
void rses_property_done(rses_property_t *prop)
@ -1146,16 +960,16 @@ void rses_property_done(rses_property_t *prop)
/**
* @brief Get count of backend servers that are slaves.
*
*
* Find out the number of read backend servers.
* Depending on the configuration value type, either copy direct count
* of slave connections or calculate the count from percentage value.
*
*
* @param rses Router client session
* @param router_nservers The number of backend servers in total
*/
int rses_get_max_slavecount(ROUTER_CLIENT_SES *rses,
int router_nservers)
int router_nservers)
{
int conf_max_nslaves;
int max_nslaves;
@ -1177,7 +991,7 @@ int rses_get_max_slavecount(ROUTER_CLIENT_SES *rses,
/*
* @brief Get the maximum replication lag for this router
*
*
* @param rses Router client session
* @return Replication lag from configuration or very large number
*/
@ -1202,10 +1016,10 @@ int rses_get_max_replication_lag(ROUTER_CLIENT_SES *rses)
/**
* @brief Find a back end reference that matches the given DCB
*
*
* Finds out if there is a backend reference pointing at the DCB given as
* parameter.
*
*
* @param rses router client session
* @param dcb DCB
*
@ -1239,14 +1053,14 @@ backend_ref_t *get_bref_from_dcb(ROUTER_CLIENT_SES *rses, DCB *dcb)
/**
* @brief Call hang up function
*
*
* Calls hang-up function for DCB if it is not both running and in
* master/slave/joined/ndb role. Called by DCB's callback routine.
*
*
* @param dcb DCB relating to a backend server
* @param reason The reason for the state change
* @param data Data is a backend reference structure belonging to this router
*
*
* @return 1 for success, 0 for failure
*/
int router_handle_state_switch(DCB *dcb, DCB_REASON reason, void *data)
@ -1346,10 +1160,10 @@ static bool rwsplit_process_router_options(ROUTER_INSTANCE *router,
if (c == UNDEFINED_CRITERIA)
{
MXS_ERROR("Unknown slave selection criteria \"%s\". "
"Allowed values are LEAST_GLOBAL_CONNECTIONS, "
"LEAST_ROUTER_CONNECTIONS, LEAST_BEHIND_MASTER,"
"and LEAST_CURRENT_OPERATIONS.",
STRCRITERIA(router->rwsplit_config.rw_slave_select_criteria));
"Allowed values are LEAST_GLOBAL_CONNECTIONS, "
"LEAST_ROUTER_CONNECTIONS, LEAST_BEHIND_MASTER,"
"and LEAST_CURRENT_OPERATIONS.",
STRCRITERIA(router->rwsplit_config.rw_slave_select_criteria));
success = false;
}
else
@ -1407,9 +1221,9 @@ static bool rwsplit_process_router_options(ROUTER_INSTANCE *router,
/**
* @brief Router error handling routine (API)
*
*
* Error Handler routine to resolve _backend_ failures. If it succeeds then
* there are enough operative backends available and connected. Otherwise it
* there are enough operative backends available and connected. Otherwise it
* fails, and session is terminated.
*
* @param instance The router instance
@ -1472,8 +1286,8 @@ static void handleError(ROUTER *instance, void *router_session,
if (!rses_begin_locked_router_action(rses))
{
close_dcb = false; /* With the assumption that if the router session is closed,
* then so is the dcb.
*/
* then so is the dcb.
*/
*succp = false;
break;
}
@ -1570,8 +1384,8 @@ static void handleError(ROUTER *instance, void *router_session,
if (close_dcb)
{
dcb_close(problem_dcb);
}
dcb_close(problem_dcb);
}
}
/**
@ -1657,7 +1471,6 @@ static bool handle_error_new_connection(ROUTER_INSTANCE *inst,
{
ROUTER_CLIENT_SES *myrses;
SESSION *ses;
int router_nservers;
int max_nslaves;
int max_slave_rlag;
backend_ref_t *bref;
@ -1711,8 +1524,8 @@ static bool handle_error_new_connection(ROUTER_INSTANCE *inst,
*/
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(myrses, router_nservers);
max_nslaves = rses_get_max_slavecount(myrses, myrses->rses_nbackends);
max_slave_rlag = rses_get_max_replication_lag(myrses);
/**
* Try to get replacement slave or at least the minimum
@ -1720,13 +1533,13 @@ static bool handle_error_new_connection(ROUTER_INSTANCE *inst,
*/
if (inst->rwsplit_config.rw_disable_sescmd_hist)
{
succp = have_enough_servers(&myrses, 1, router_nservers, inst) ? true : false;
succp = have_enough_servers(myrses, 1, myrses->rses_nbackends, inst) ? true : false;
}
else
{
succp = select_connect_backend_servers(&myrses->rses_master_ref,
myrses->rses_backend_ref,
router_nservers,
myrses->rses_nbackends,
max_nslaves, max_slave_rlag,
myrses->rses_config.rw_slave_select_criteria,
ses, inst);
@ -1736,25 +1549,6 @@ return_succp:
return succp;
}
/**
* @brief Calculate the number of backend servers
*
* @param inst Router instance
*
* @return int - count of servers
*/
static int router_get_servercount(ROUTER_INSTANCE *inst)
{
int router_nservers = 0;
for (SERVER_REF *ref = inst->service->dbref; ref; ref = ref->next)
{
router_nservers++;
}
return router_nservers;
}
/**
* @brief Calculate whether we have enough servers to route a query
*
@ -1765,16 +1559,16 @@ static int router_get_servercount(ROUTER_INSTANCE *inst)
*
* @return bool - whether enough, side effect is error logging
*/
static bool have_enough_servers(ROUTER_CLIENT_SES **p_rses, const int min_nsrv,
static bool have_enough_servers(ROUTER_CLIENT_SES *rses, const int min_nsrv,
int router_nsrv, ROUTER_INSTANCE *router)
{
bool succp;
/** With too few servers session is not created */
if (router_nsrv < min_nsrv ||
MXS_MAX((*p_rses)->rses_config.rw_max_slave_conn_count,
(router_nsrv * (*p_rses)->rses_config.rw_max_slave_conn_percent) /
100) < min_nsrv)
MXS_MAX((rses)->rses_config.rw_max_slave_conn_count,
(router_nsrv * (rses)->rses_config.rw_max_slave_conn_percent) /
100) < min_nsrv)
{
if (router_nsrv < min_nsrv)
{
@ -1785,16 +1579,16 @@ static bool have_enough_servers(ROUTER_CLIENT_SES **p_rses, const int min_nsrv,
}
else
{
int pct = (*p_rses)->rses_config.rw_max_slave_conn_percent / 100;
int pct = (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)
if ((rses)->rses_config.rw_max_slave_conn_count < min_nsrv)
{
MXS_ERROR("Unable to start %s service. There are "
"too few backend servers configured in "
"MaxScale.cnf. Found %d when %d is required.",
router->service->name,
(*p_rses)->rses_config.rw_max_slave_conn_count, min_nsrv);
(rses)->rses_config.rw_max_slave_conn_count, min_nsrv);
}
if (nservers < min_nsrv)
{
@ -1804,11 +1598,9 @@ static bool have_enough_servers(ROUTER_CLIENT_SES **p_rses, const int min_nsrv,
"MaxScale.cnf. Found %d%% when at least %.0f%% "
"would be required.",
router->service->name,
(*p_rses)->rses_config.rw_max_slave_conn_percent, dbgpct);
(rses)->rses_config.rw_max_slave_conn_percent, dbgpct);
}
}
MXS_FREE(*p_rses);
*p_rses = NULL;
succp = false;
}
else
@ -1818,62 +1610,11 @@ static bool have_enough_servers(ROUTER_CLIENT_SES **p_rses, const int min_nsrv,
return succp;
}
#if defined(PREP_STMT_CACHING)
#define MAX_STMT_LEN 1024
static prep_stmt_t *prep_stmt_init(prep_stmt_type_t type, void *id)
{
prep_stmt_t *pstmt;
pstmt = (prep_stmt_t *)MXS_CALLOC(1, sizeof(prep_stmt_t));
if (pstmt != NULL)
{
#if defined(SS_DEBUG)
pstmt->pstmt_chk_top = CHK_NUM_PREP_STMT;
pstmt->pstmt_chk_tail = CHK_NUM_PREP_STMT;
#endif
pstmt->pstmt_state = PREP_STMT_ALLOC;
pstmt->pstmt_type = type;
if (type == PREP_STMT_NAME)
{
pstmt->pstmt_id.name = strndup((char *)id, MAX_STMT_LEN);
}
else
{
pstmt->pstmt_id.seq = 0;
}
}
CHK_PREP_STMT(pstmt);
return pstmt;
}
static void prep_stmt_done(prep_stmt_t *pstmt)
{
CHK_PREP_STMT(pstmt);
if (pstmt->pstmt_type == PREP_STMT_NAME)
{
MXS_FREE(pstmt->pstmt_id.name);
}
MXS_FREE(pstmt);
}
static bool prep_stmt_drop(prep_stmt_t *pstmt)
{
CHK_PREP_STMT(pstmt);
pstmt->pstmt_state = PREP_STMT_DROPPED;
return true;
}
#endif /*< PREP_STMT_CACHING */
/**
* @brief Refresh the instance by the given parameter value.
*
* Used by createInstance and newSession
*
*
* @param router Router instance
* @param singleparam Parameter fo be reloaded
*
@ -1973,53 +1714,15 @@ static void refreshInstance(ROUTER_INSTANCE *router,
}
param = param->next;
}
#if defined(NOT_USED) /*< can't read monitor config parameters */
if ((*router->servers)->backend_server->rlag == -2)
{
rlag_enabled = false;
}
else
{
rlag_enabled = true;
}
/**
* If replication lag detection is not enabled the measure can't be
* used in slave selection.
*/
if (!rlag_enabled)
{
if (rlag_limited)
{
MXS_WARNING("Configuration Failed, max_slave_replication_lag "
"is set to %d,\n\t\t but detect_replication_lag "
"is not enabled. Replication lag will not be checked.",
router->rwsplit_config.rw_max_slave_replication_lag);
}
if (router->rwsplit_config.rw_slave_select_criteria ==
LEAST_BEHIND_MASTER)
{
MXS_WARNING("Configuration Failed, router option "
"\n\t\t slave_selection_criteria=LEAST_BEHIND_MASTER "
"is specified, but detect_replication_lag "
"is not enabled.\n\t\t "
"slave_selection_criteria=%s will be used instead.",
STRCRITERIA(DEFAULT_CRITERIA));
router->rwsplit_config.rw_slave_select_criteria = DEFAULT_CRITERIA;
}
}
#endif /*< NOT_USED */
}
/*
* @brief Release resources when createInstance fails to complete
*
*
* Internal to createInstance
*
*
* @param router Router instance
*
*
*/
static void free_rwsplit_instance(ROUTER_INSTANCE *router)
{
@ -2029,3 +1732,57 @@ static void free_rwsplit_instance(ROUTER_INSTANCE *router)
}
}
/**
* @brief Create backend server references
*
* This creates a new set of backend references for the client session. Currently
* this is only used on startup but it could be used to dynamically change the
* set of used servers.
*
* @param rses Client router session
* @param dest Destination where the array of backens is stored
* @param n_backend Number of items in the array
* @return True on success, false on error
*/
static bool create_backends(ROUTER_CLIENT_SES *rses, backend_ref_t** dest, int* n_backend)
{
backend_ref_t *backend_ref = (backend_ref_t *)MXS_CALLOC(1, *n_backend * sizeof(backend_ref_t));
if (backend_ref == NULL)
{
return false;
}
int i = 0;
for (SERVER_REF *sref = rses->router->service->dbref; sref && i < *n_backend; sref = sref->next)
{
if (sref->active)
{
#if defined(SS_DEBUG)
backend_ref[i].bref_chk_top = CHK_NUM_BACKEND_REF;
backend_ref[i].bref_chk_tail = CHK_NUM_BACKEND_REF;
backend_ref[i].bref_sescmd_cur.scmd_cur_chk_top = CHK_NUM_SESCMD_CUR;
backend_ref[i].bref_sescmd_cur.scmd_cur_chk_tail = CHK_NUM_SESCMD_CUR;
#endif
backend_ref[i].bref_state = 0;
backend_ref[i].ref = sref;
/** store pointers to sescmd list to both cursors */
backend_ref[i].bref_sescmd_cur.scmd_cur_rses = rses;
backend_ref[i].bref_sescmd_cur.scmd_cur_active = false;
backend_ref[i].bref_sescmd_cur.scmd_cur_ptr_property =
&rses->rses_properties[RSES_PROP_TYPE_SESCMD];
backend_ref[i].bref_sescmd_cur.scmd_cur_cmd = NULL;
i++;
}
}
if (i < *n_backend)
{
MXS_INFO("The service reported %d servers but only took %d into use.", *n_backend, i);
*n_backend = i;
}
*dest = backend_ref;
return true;
}

View File

@ -32,26 +32,6 @@
MXS_BEGIN_DECLS
#undef PREP_STMT_CACHING
#if defined(PREP_STMT_CACHING)
typedef enum prep_stmt_type
{
PREP_STMT_NAME,
PREP_STMT_ID
} prep_stmt_type_t;
typedef enum prep_stmt_state
{
PREP_STMT_ALLOC,
PREP_STMT_SENT,
PREP_STMT_RECV,
PREP_STMT_DROPPED
} prep_stmt_state_t;
#endif /*< PREP_STMT_CACHING */
typedef enum bref_state
{
BREF_IN_USE = 0x01,
@ -281,7 +261,6 @@ struct router_client_session
skygw_chk_t rses_chk_top;
#endif
SPINLOCK rses_lock; /*< protects rses_deleted */
int rses_versno; /*< even = no active update, else odd. not used 4/14 */
bool rses_closed; /*< true when closeSession is called */
rses_property_t* rses_properties[RSES_PROP_TYPE_COUNT]; /*< Properties listed by their type */
backend_ref_t* rses_master_ref;
@ -325,12 +304,10 @@ typedef struct
typedef struct router_instance
{
SERVICE* service; /*< Pointer to service */
ROUTER_CLIENT_SES* connections; /*< List of client connections */
SPINLOCK lock; /*< Lock for the instance data */
rwsplit_config_t rwsplit_config; /*< expanded config info from SERVICE */
int rwsplit_version; /*< version number for router's config */
ROUTER_STATS stats; /*< Statistics for this router */
struct router_instance* next; /*< Next router on the list */
bool available_slaves; /*< The router has some slaves avialable */
} ROUTER_INSTANCE;

View File

@ -366,66 +366,6 @@ void live_session_reply(GWBUF **querybuf, ROUTER_CLIENT_SES *rses)
}
}
/*
* Uses MySQL specific mechanisms
*/
/**
* @brief Write an error message to the log for session lock failure
*
* This happens when processing a client reply and the session cannot be
* locked.
*
* @param rses Router session
* @param buf Query buffer containing reply data
* @param dcb The backend DCB that sent the reply
*/
void print_error_packet(ROUTER_CLIENT_SES *rses, GWBUF *buf, DCB *dcb)
{
#if defined(SS_DEBUG)
if (GWBUF_IS_TYPE_MYSQL(buf))
{
while (gwbuf_length(buf) > 0)
{
/**
* This works with MySQL protocol only !
* Protocol specific packet print functions would be nice.
*/
uint8_t *ptr = GWBUF_DATA(buf);
size_t len = MYSQL_GET_PACKET_LEN(ptr);
if (MYSQL_GET_COMMAND(ptr) == 0xff)
{
SERVER *srv = NULL;
backend_ref_t *bref = rses->rses_backend_ref;
int i;
char *bufstr;
for (i = 0; i < rses->rses_nbackends; i++)
{
if (bref[i].bref_dcb == dcb)
{
srv = bref[i].ref->server;
}
}
ss_dassert(srv != NULL);
char *str = (char *)&ptr[7];
bufstr = strndup(str, len - 3);
MXS_ERROR("Backend server %s:%d responded with "
"error : %s",
srv->name, srv->port, bufstr);
MXS_FREE(bufstr);
}
buf = gwbuf_consume(buf, len + 4);
}
}
else
{
gwbuf_free(buf);
}
#endif /*< SS_DEBUG */
}
/*
* Uses MySQL specific mechanisms
*/

View File

@ -543,6 +543,7 @@ bool rwsplit_get_dcb(DCB **p_dcb, ROUTER_CLIENT_SES *rses, backend_type_t btype,
* server, or master.
*/
if (BREF_IS_IN_USE((&backend_ref[i])) &&
SERVER_REF_IS_ACTIVE(b) &&
(strncasecmp(name, b->server->unique_name, PATH_MAX) == 0) &&
(SERVER_IS_SLAVE(&server) || SERVER_IS_RELAY_SERVER(&server) ||
SERVER_IS_MASTER(&server)))
@ -577,7 +578,7 @@ bool rwsplit_get_dcb(DCB **p_dcb, ROUTER_CLIENT_SES *rses, backend_type_t btype,
* Unused backend or backend which is not master nor
* slave can't be used
*/
if (!BREF_IS_IN_USE(&backend_ref[i]) ||
if (!BREF_IS_IN_USE(&backend_ref[i]) || !SERVER_REF_IS_ACTIVE(b) ||
(!SERVER_IS_MASTER(&server) && !SERVER_IS_SLAVE(&server)))
{
continue;
@ -665,27 +666,37 @@ bool rwsplit_get_dcb(DCB **p_dcb, ROUTER_CLIENT_SES *rses, backend_type_t btype,
*/
if (btype == BE_MASTER)
{
if (master_bref)
if (master_bref && SERVER_REF_IS_ACTIVE(master_bref->ref))
{
/** It is possible for the server status to change at any point in time
* so copying it locally will make possible error messages
* easier to understand */
SERVER server;
server.status = master_bref->ref->server->status;
if (BREF_IS_IN_USE(master_bref) && SERVER_IS_MASTER(&server))
if (BREF_IS_IN_USE(master_bref))
{
*p_dcb = master_bref->bref_dcb;
succp = true;
/** if bref is in use DCB should not be closed */
ss_dassert(master_bref->bref_dcb->state != DCB_STATE_ZOMBIE);
if (SERVER_IS_MASTER(&server))
{
*p_dcb = master_bref->bref_dcb;
succp = true;
/** if bref is in use DCB should not be closed */
ss_dassert(master_bref->bref_dcb->state != DCB_STATE_ZOMBIE);
}
else
{
MXS_ERROR("Server '%s' should be master but "
"is %s instead and can't be chosen as the master.",
master_bref->ref->server->unique_name,
STRSRVSTATUS(&server));
succp = false;
}
}
else
{
MXS_ERROR("Server at %s:%d should be master but "
"is %s instead and can't be chosen to master.",
master_bref->ref->server->name,
master_bref->ref->server->port,
STRSRVSTATUS(&server));
MXS_ERROR("Server '%s' is not in use and can't be "
"chosen as the master.",
master_bref->ref->server->unique_name);
succp = false;
}
}
@ -891,9 +902,6 @@ route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
hint = hint->next;
} /*< while (hint != NULL) */
#if defined(SS_EXTRA_DEBUG)
MXS_INFO("Selected target \"%s\"", STRTARGET(target));
#endif
return target;
}
@ -1098,9 +1106,6 @@ bool handle_slave_is_target(ROUTER_INSTANCE *inst, ROUTER_CLIENT_SES *rses,
*/
if (rwsplit_get_dcb(target_dcb, rses, BE_SLAVE, NULL, rlag_max))
{
#if defined(SS_EXTRA_DEBUG)
MXS_INFO("Found DCB for slave.");
#endif
atomic_add(&inst->stats.n_slave, 1);
return true;
}

View File

@ -106,7 +106,8 @@ bool select_connect_backend_servers(backend_ref_t **p_master_ref,
SERVER_REF *master_host = get_root_master(backend_ref, router_nservers);
if (router->rwsplit_config.rw_master_failure_mode == RW_FAIL_INSTANTLY &&
(master_host == NULL || SERVER_IS_DOWN(master_host->server)))
(master_host == NULL || !SERVER_REF_IS_ACTIVE(master_host) ||
SERVER_IS_DOWN(master_host->server)))
{
MXS_ERROR("Couldn't find suitable Master from %d candidates.", router_nservers);
return false;
@ -147,7 +148,9 @@ bool select_connect_backend_servers(backend_ref_t **p_master_ref,
{
SERVER *serv = backend_ref[i].ref->server;
if (!BREF_HAS_FAILED(&backend_ref[i]) && SERVER_IS_RUNNING(serv))
if (!BREF_HAS_FAILED(&backend_ref[i]) &&
SERVER_REF_IS_ACTIVE(backend_ref[i].ref) &&
SERVER_IS_RUNNING(serv))
{
/* check also for relay servers and don't take the master_host */
if (slaves_found < max_nslaves &&

File diff suppressed because it is too large Load Diff

View File

@ -261,7 +261,7 @@ typedef struct backend_ref_st
#endif
int n_mapping_eof;
GWBUF* map_queue;
BACKEND* bref_backend; /*< Backend server */
SERVER_REF* bref_backend; /*< Backend server */
DCB* bref_dcb; /*< Backend DCB */
bref_state_t bref_state; /*< State of the backend */
bool bref_mapped; /*< Whether the backend has been mapped */
@ -357,8 +357,6 @@ typedef struct router_instance
SERVICE* service; /*< Pointer to service */
ROUTER_CLIENT_SES* connections; /*< List of client connections */
SPINLOCK lock; /*< Lock for the instance data */
BACKEND** servers; /*< Backend servers */
BACKEND* master; /*< NULL or pointer */
schemarouter_config_t schemarouter_config; /*< expanded config info from SERVICE */
int schemarouter_version;/*< version number for router's config */
unsigned int bitmask; /*< Bitmask to apply to server->status */