diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index bf0b33fde..fc30ab168 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -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. diff --git a/client/maxadmin.c b/client/maxadmin.c index 96602b95e..028c4ad01 100644 --- a/client/maxadmin.c +++ b/client/maxadmin.c @@ -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; diff --git a/cmake/install_layout.cmake b/cmake/install_layout.cmake index 3eb9503f3..1abf7a7e2 100644 --- a/cmake/install_layout.cmake +++ b/cmake/install_layout.cmake @@ -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) diff --git a/include/maxscale/gwdirs.h.in b/include/maxscale/gwdirs.h.in index 909d324f0..695a57277 100644 --- a/include/maxscale/gwdirs.h.in +++ b/include/maxscale/gwdirs.h.in @@ -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(); diff --git a/include/maxscale/monitor.h b/include/maxscale/monitor.h index 106b17113..d5ca21740 100644 --- a/include/maxscale/monitor.h +++ b/include/maxscale/monitor.h @@ -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 *); diff --git a/include/maxscale/server.h b/include/maxscale/server.h index 8b85c2b13..b58377c5d 100644 --- a/include/maxscale/server.h +++ b/include/maxscale/server.h @@ -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 diff --git a/include/maxscale/service.h b/include/maxscale/service.h index b6d7940d5..b4bdfe637 100644 --- a/include/maxscale/service.h +++ b/include/maxscale/service.h @@ -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 *); diff --git a/server/core/config.c b/server/core/config.c index 7958cb175..e75196b71 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -65,6 +65,7 @@ #include #include #include +#include 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"); diff --git a/server/core/gateway.c b/server/core/gateway.c index 138bc60bd..ba02b48b5 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -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) diff --git a/server/core/gwdirs.c b/server/core/gwdirs.c index eb8ca6394..22de96bc6 100644 --- a/server/core/gwdirs.c +++ b/server/core/gwdirs.c @@ -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 diff --git a/server/core/monitor.c b/server/core/monitor.c index 60bc4e238..9df8401ff 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -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); diff --git a/server/core/server.c b/server/core/server.c index 632aa4a94..a2abb488d 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -35,6 +35,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -44,6 +47,7 @@ #include #include #include +#include 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 +} diff --git a/server/core/service.c b/server/core/service.c index 6f2de2811..942bc3e42 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -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 * diff --git a/server/core/test/testserver.c b/server/core/test/testserver.c index 9c10e6edf..bb195885e 100644 --- a/server/core/test/testserver.c +++ b/server/core/test/testserver.c @@ -37,6 +37,10 @@ #include #include #include + +// 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); } diff --git a/server/modules/authenticator/MySQLAuth/dbusers.c b/server/modules/authenticator/MySQLAuth/dbusers.c index ec4cbad6f..d08a0d40a 100644 --- a/server/modules/authenticator/MySQLAuth/dbusers.c +++ b/server/modules/authenticator/MySQLAuth/dbusers.c @@ -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) diff --git a/server/modules/filter/cache/rules.c b/server/modules/filter/cache/rules.c index cc2b3bdaa..7dd5bbdf0 100644 --- a/server/modules/filter/cache/rules.c +++ b/server/modules/filter/cache/rules.c @@ -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; } /** diff --git a/server/modules/filter/cache/test/testrules.c b/server/modules/filter/cache/test/testrules.c index 3039e6605..153f342a5 100644 --- a/server/modules/filter/cache/test/testrules.c +++ b/server/modules/filter/cache/test/testrules.c @@ -14,12 +14,37 @@ #include #include "rules.h" #include +#include +#include #if !defined(SS_DEBUG) #define SS_DEBUG #endif #include -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(); } diff --git a/server/modules/include/debugcli.h b/server/modules/include/debugcli.h index a32f47374..0e384c603 100644 --- a/server/modules/include/debugcli.h +++ b/server/modules/include/debugcli.h @@ -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 { diff --git a/server/modules/monitor/mysqlmon/mysql_mon.c b/server/modules/monitor/mysqlmon/mysql_mon.c index 86967962d..dbf19503f 100644 --- a/server/modules/monitor/mysqlmon/mysql_mon.c +++ b/server/modules/monitor/mysqlmon/mysql_mon.c @@ -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)) { diff --git a/server/modules/routing/debugcli/debugcmd.c b/server/modules/routing/debugcli/debugcmd.c index 3e828bc77..3c0fb17dc 100644 --- a/server/modules/routing/debugcli/debugcmd.c +++ b/server/modules/routing/debugcli/debugcmd.c @@ -75,8 +75,9 @@ #include #include -#define MAXARGS 6 +#define MAXARGS 12 +#define ARG_TYPE_NONE 0 #define ARG_TYPE_ADDRESS 1 #define ARG_TYPE_STRING 2 #define ARG_TYPE_SERVICE 3 @@ -95,186 +96,261 @@ extern LIST_CONFIG SESSIONlist; * * These are the options that may be passed to a command */ -struct subcommand { +struct subcommand +{ char *arg1; - int n_args; + int argc_min; + int argc_max; void (*fn)(); char *help; char *devhelp; - int arg_types[3]; + int arg_types[MAXARGS]; }; +#define EMPTY_OPTION + static void telnetdShowUsers(DCB *); static void show_log_throttling(DCB *); /** * The subcommands of the show command */ -struct subcommand showoptions[] = { +struct subcommand showoptions[] = +{ #if defined(BUFFER_TRACE) - { "buffers", 0, dprintAllBuffers, - "Show all buffers with backtrace", - "Show all buffers with backtrace", - {0, 0, 0} }, + { + "buffers", 0, dprintAllBuffers, + "Show all buffers with backtrace", + "Show all buffers with backtrace", + {0, 0, 0} + }, #endif - { "dcblist", 0, dprintDCBList, - "Show statistics for the list of all descriptor control blocks", - "Show statistics for the list of all descriptor control blocks", - {0, 0, 0} }, - { "dcbs", 0, dprintAllDCBs, - "Show all descriptor control blocks (network connections)", - "Show all descriptor control blocks (network connections)", - {0, 0, 0} }, - { "dcb", 1, dprintDCB, - "Show a single descriptor control block e.g. show dcb 0x493340", - "Show a single descriptor control block e.g. show dcb 0x493340", - {ARG_TYPE_DCB, 0, 0} }, - { "dbusers", 1, dcb_usersPrint, - "Show statistics and user names for a service's user table.\n" - "\t\tExample : show dbusers ", - "Show statistics and user names for a service's user table.\n" - "\t\tExample : show dbusers |", - {ARG_TYPE_DBUSERS, 0, 0} }, - { "epoll", 0, dprintPollStats, - "Show the poll statistics", - "Show the poll statistics", - {0, 0, 0} }, - { "eventq", 0, dShowEventQ, - "Show the queue of events waiting to be processed", - "Show the queue of events waiting to be processed", - {0, 0, 0} }, - { "eventstats", 0, dShowEventStats, - "Show the event statistics", - "Show the event statistics", - {0, 0, 0} }, - { "feedbackreport", 0, moduleShowFeedbackReport, - "Show the report of MaxScale loaded modules, suitable for Notification Service", - "Show the report of MaxScale loaded modules, suitable for Notification Service", - {0, 0, 0} }, - { "filter", 1, dprintFilter, - "Show details of a filter, called with a filter name", - "Show details of a filter, called with the address of a filter", - {ARG_TYPE_FILTER, 0, 0} }, - { "filters", 0, dprintAllFilters, - "Show all filters", - "Show all filters", - {0, 0, 0} }, - { "log_throttling", 0, show_log_throttling, - "Show the current log throttling setting (count, window (ms), suppression (ms))", - "Show the current log throttling setting (count, window (ms), suppression (ms))", - {0, 0, 0} }, - { "modules", 0, dprintAllModules, - "Show all currently loaded modules", - "Show all currently loaded modules", - {0, 0, 0} }, - { "monitor", 1, monitorShow, - "Show the monitor details", - "Show the monitor details", - {ARG_TYPE_MONITOR, 0, 0} }, - { "monitors", 0, monitorShowAll, - "Show the monitors that are configured", - "Show the monitors that are configured", - {0, 0, 0} }, - { "persistent", 1, dprintPersistentDCBs, - "Show persistent pool for a named server, e.g. show persistent dbnode1", - "Show persistent pool for a server, e.g. show persistent 0x485390. " - "The address may also be replaced with the server name from the configuration file", - {ARG_TYPE_SERVER, 0, 0} }, - { "server", 1, dprintServer, - "Show details for a named server, e.g. show server dbnode1", - "Show details for a server, e.g. show server 0x485390. The address may also be " - "repalced with the server name from the configuration file", - {ARG_TYPE_SERVER, 0, 0} }, - { "servers", 0, dprintAllServers, - "Show all configured servers", - "Show all configured servers", - {0, 0, 0} }, - { "serversjson", 0, dprintAllServersJson, - "Show all configured servers in JSON format", - "Show all configured servers in JSON format", - {0, 0, 0} }, - { "services", 0, dprintAllServices, - "Show all configured services in MaxScale", - "Show all configured services in MaxScale", - {0, 0, 0} }, - { "service", 1, dprintService, - "Show a single service in MaxScale, may be passed a service name", - "Show a single service in MaxScale, may be passed a service name or address of a service object", - {ARG_TYPE_SERVICE, 0, 0} }, - { "session", 1, dprintSession, - "Show a single session in MaxScale, e.g. show session 0x284830", - "Show a single session in MaxScale, e.g. show session 0x284830", - {ARG_TYPE_SESSION, 0, 0} }, - { "sessionlist", 0, dprintSessionList, - "Show statistics for the list of all sessions", - "Show statistics for the list of all sessions", - {0, 0, 0} }, - { "sessions", 0, dprintAllSessions, - "Show all active sessions in MaxScale", - "Show all active sessions in MaxScale", - {0, 0, 0} }, - { "tasks", 0, hkshow_tasks, - "Show all active housekeeper tasks in MaxScale", - "Show all active housekeeper tasks in MaxScale", - {0, 0, 0} }, - { "threads", 0, dShowThreads, - "Show the status of the polling threads in MaxScale", - "Show the status of the polling threads in MaxScale", - {0, 0, 0} }, - { "users", 0, telnetdShowUsers, - "Show all maxadmin enabled Linux accounts and created maxadmin users", - "Show all maxadmin enabled Linux accounts and created maxadmin users", - {0, 0, 0} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } + { + "dcblist", 0, 0, dprintDCBList, + "Show DCB statistics", + "Show statistics for the list of all DCBs(descriptor control blocks)", + {0} + }, + { + "dcbs", 0, 0, dprintAllDCBs, + "Show all DCBs", + "Show all descriptor control blocks (network connections)", + {0} + }, + { + "dcb", 1, 1, dprintDCB, + "Show a DCB", + "Show a single descriptor control block e.g. show dcb 0x493340", + {ARG_TYPE_DCB, 0, 0} + }, + { + "dbusers", 1, 1, dcb_usersPrint, + "Show user statistics", + "Show statistics and user names for a service's user table.\n" + "\t\tExample : show dbusers |", + {ARG_TYPE_DBUSERS, 0, 0} + }, + { + "epoll", 0, 0, dprintPollStats, + "Show the poll statistics", + "Show the epoll polling system statistics", + {0, 0, 0} + }, + { + "eventq", 0, 0, dShowEventQ, + "Show event queue", + "Show the queue of events waiting to be processed", + {0, 0, 0} + }, + { + "eventstats", 0, 0, dShowEventStats, + "Show event queue statistics", + "Show event queue statistics", + {0, 0, 0} + }, + { + "feedbackreport", 0, 0, moduleShowFeedbackReport, + "Show feedback report", + "Show the report of MaxScale loaded modules, suitable for Notification Service", + {0, 0, 0} + }, + { + "filter", 1, 1, dprintFilter, + "Show filter details", + "Show details of a filter, the parameter is filter name", + {ARG_TYPE_FILTER, 0, 0} + }, + { + "filters", 0, 0, dprintAllFilters, + "Show all filters", + "Show all filters that were read from the configuration file", + {0, 0, 0} + }, + { + "log_throttling", 0, 0, show_log_throttling, + "Show log throttling setting", + "Show the current log throttling setting (count, window (ms), suppression (ms))", + {0, 0, 0} + }, + { + "modules", 0, 0, dprintAllModules, + "Show loaded modules", + "Show all currently loaded modules", + {0, 0, 0} + }, + { + "monitor", 1, 1, monitorShow, + "Show monitor details", + "Show details about a specific monitor, the parameter is monitor name", + {ARG_TYPE_MONITOR, 0, 0} + }, + { + "monitors", 0, 0, monitorShowAll, + "Show all monitors", + "Show all the monitors", + {0, 0, 0} + }, + { + "persistent", 1, 1, dprintPersistentDCBs, + "Show persistent connection pool", + "Show persistent pool for a server, e.g. show persistent dbnode1. ", + {ARG_TYPE_SERVER, 0, 0} + }, + { + "server", 1, 1, dprintServer, + "Show server details", + "Show details for a server, e.g. show server dbnode1", + {ARG_TYPE_SERVER, 0, 0} + }, + { + "servers", 0, 0, dprintAllServers, + "Show all servers", + "Show all configured servers", + {0, 0, 0} + }, + { + "serversjson", 0, 0, dprintAllServersJson, + "Show all servers in JSON", + "Show all configured servers in JSON format", + {0, 0, 0} + }, + { + "services", 0, 0, dprintAllServices, + "Show all service", + "Show all configured services in MaxScale", + {0, 0, 0} + }, + { + "service", 1, 1, dprintService, + "Show service details", + "Show a single service in MaxScale, the parameter is the service name", + {ARG_TYPE_SERVICE, 0, 0} + }, + { + "session", 1, 1, dprintSession, + "Show session details", + "Show a single session in MaxScale, e.g. show session 0x284830", + {ARG_TYPE_SESSION, 0, 0} + }, + { + "sessionlist", 0, 0, dprintSessionList, + "Show session list statistics", + "Show statistics for the list of all sessions", + {0, 0, 0} + }, + { + "sessions", 0, 0, dprintAllSessions, + "Show all sessions", + "Show all active sessions in MaxScale", + {0, 0, 0} + }, + { + "tasks", 0, 0, hkshow_tasks, + "Show housekeeper tasks", + "Show all active housekeeper tasks in MaxScale", + {0, 0, 0} + }, + { + "threads", 0, 0, dShowThreads, + "Show workter thread status", + "Show the status of the worker threads in MaxScale", + {0, 0, 0} + }, + { + "users", 0, 0, telnetdShowUsers, + "Show enabled Linux accounts", + "Show all maxadmin enabled Linux accounts and created maxadmin users", + {0, 0, 0} + }, + { EMPTY_OPTION} }; /** * The subcommands of the list command */ -struct subcommand listoptions[] = { - { "clients", 0, dListClients, - "List all the client connections to MaxScale", - "List all the client connections to MaxScale", - {0, 0, 0} }, - { "dcbs", 0, dListDCBs, - "List all the DCBs active within MaxScale", - "List all the DCBs active within MaxScale", - {0, 0, 0} }, - { "filters", 0, dListFilters, - "List all the filters defined within MaxScale", - "List all the filters defined within MaxScale", - {0, 0, 0} }, - { "listeners", 0, dListListeners, - "List all the listeners defined within MaxScale", - "List all the listeners defined within MaxScale", - {0, 0, 0} }, - { "modules", 0, dprintAllModules, - "List all currently loaded modules", - "List all currently loaded modules", - {0, 0, 0} }, - { "monitors", 0, monitorList, - "List all monitors", - "List all monitors", - {0, 0, 0} }, - { "services", 0, dListServices, - "List all the services defined within MaxScale", - "List all the services defined within MaxScale", - {0, 0, 0} }, - { "servers", 0, dListServers, - "List all the servers defined within MaxScale", - "List all the servers defined within MaxScale", - {0, 0, 0} }, - { "sessions", 0, dListSessions, - "List all the active sessions within MaxScale", - "List all the active sessions within MaxScale", - {0, 0, 0} }, - { "threads", 0, dShowThreads, - "List the status of the polling threads in MaxScale", - "List the status of the polling threads in MaxScale", - {0, 0, 0} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } +struct subcommand listoptions[] = +{ + { + "clients", 0, 0, dListClients, + "List all clients", + "List all the client connections to MaxScale", + {0, 0, 0} + }, + { + "dcbs", 0, 0, dListDCBs, + "List all DCBs", + "List all the DCBs active within MaxScale", + {0, 0, 0} + }, + { + "filters", 0, 0, dListFilters, + "List all filters", + "List all the filters defined within MaxScale", + {0, 0, 0} + }, + { + "listeners", 0, 0, dListListeners, + "List all listeners", + "List all the listeners defined within MaxScale", + {0, 0, 0} + }, + { + "modules", 0, 0, dprintAllModules, + "List all currently loaded modules", + "List all currently loaded modules", + {0, 0, 0} + }, + { + "monitors", 0, 0, monitorList, + "List all monitors", + "List all monitors", + {0, 0, 0} + }, + { + "services", 0, 0, dListServices, + "List all the services", + "List all the services defined within MaxScale", + {0, 0, 0} + }, + { + "servers", 0, 0, dListServers, + "List all servers", + "List all the servers defined within MaxScale", + {0, 0, 0} + }, + { + "sessions", 0, 0, dListSessions, + "List all sessions", + "List all the active sessions within MaxScale", + {0, 0, 0} + }, + { + "threads", 0, 0, dShowThreads, + "List polling threads", + "List the status of the polling threads in MaxScale", + {0, 0, 0} + }, + { EMPTY_OPTION} }; static void shutdown_server() @@ -288,38 +364,34 @@ static void shutdown_monitor(DCB *dcb, MONITOR *monitor); /** * The subcommands of the shutdown command */ -struct subcommand shutdownoptions[] = { +struct subcommand shutdownoptions[] = +{ { "maxscale", - 0, + 0, 0, shutdown_server, "Shutdown MaxScale", - "Shutdown MaxScale", + "Initiate a controlled shutdown of MaxScale", {0, 0, 0} }, { "monitor", - 1, + 1, 1, shutdown_monitor, - "Shutdown a monitor, e.g. shutdown monitor 0x48381e0", - "Shutdown a monitor, e.g. shutdown monitor 0x48381e0", + "Shutdown a monitor", + "E.g. shutdown monitor db-cluster-monitor", {ARG_TYPE_MONITOR, 0, 0} }, { "service", - 1, + 1, 1, shutdown_service, - "Shutdown a service, e.g. shutdown service \"Sales Database\"", - "Shutdown a service, e.g. shutdown service 0x4838320 or shutdown service \"Sales Database\"", + "Stop a service", + "E.g. shutdown service \"Sales Database\"", {ARG_TYPE_SERVICE, 0, 0} }, { - NULL, - 0, - NULL, - NULL, - NULL, - {0, 0, 0} + EMPTY_OPTION } }; @@ -332,7 +404,7 @@ static void sync_logs(DCB *dcb) else { dcb_printf(dcb, "Failed to flush logs to disk. Read the error log for " - "more details.\n"); + "more details.\n"); } } @@ -340,19 +412,14 @@ struct subcommand syncoptions[] = { { "logs", - 0, + 0, 0, sync_logs, "Flush log files to disk", "Flush log files to disk", {0, 0, 0} }, { - NULL, - 0, - NULL, - NULL, - NULL, - {0, 0, 0} + EMPTY_OPTION } }; @@ -361,17 +428,21 @@ static void restart_monitor(DCB *dcb, MONITOR *monitor); /** * The subcommands of the restart command */ -struct subcommand restartoptions[] = { - { "monitor", 1, restart_monitor, - "Restart a monitor, e.g. restart monitor 0x48181e0", - "Restart a monitor, e.g. restart monitor 0x48181e0", - {ARG_TYPE_MONITOR, 0, 0} }, - { "service", 1, restart_service, - "Restart a service, e.g. restart service \"Test Service\"", - "Restart a service, e.g. restart service 0x4838320", - {ARG_TYPE_SERVICE, 0, 0} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } +struct subcommand restartoptions[] = +{ + { + "monitor", 1, 1, restart_monitor, + "Restart a monitor", + "E.g. restart monitor db-cluster-monitor", + {ARG_TYPE_MONITOR, 0, 0} + }, + { + "service", 1, 1, restart_service, + "Restart a service", + "E.g. restart service \"Sales Database\"", + {ARG_TYPE_SERVICE, 0, 0} + }, + { EMPTY_OPTION } }; static void set_server(DCB *dcb, SERVER *server, char *bit); @@ -381,38 +452,48 @@ static void set_log_throttling(DCB *dcb, int count, int window_ms, int suppress_ /** * The subcommands of the set command */ -struct subcommand setoptions[] = { - { "server", 2, set_server, - "Set the status of a server. E.g. set server dbnode4 master", - "Set the status of a server. E.g. set server 0x4838320 master", - {ARG_TYPE_SERVER, ARG_TYPE_STRING, 0} }, - { "pollsleep", 1, set_pollsleep, - "Set the maximum poll sleep period in milliseconds", - "Set the maximum poll sleep period in milliseconds", - {ARG_TYPE_NUMERIC, 0, 0} }, - { "nbpolls", 1, set_nbpoll, - "Set the number of non-blocking polls", - "Set the number of non-blocking polls", - {ARG_TYPE_NUMERIC, 0, 0} }, - { "log_throttling", 3, set_log_throttling, - "Set the log throttling configuration", - "Set the log throttling configuration", - {ARG_TYPE_NUMERIC, ARG_TYPE_NUMERIC, ARG_TYPE_NUMERIC} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } +struct subcommand setoptions[] = +{ + { + "server", 2, 2, set_server, + "Set the status of a server", + "Set the status of a server. E.g. set server dbnode4 master", + {ARG_TYPE_SERVER, ARG_TYPE_STRING, 0} + }, + { + "pollsleep", 1, 1, set_pollsleep, + "Set poll sleep period", + "Set the maximum poll sleep period in milliseconds", + {ARG_TYPE_NUMERIC, 0, 0} + }, + { + "nbpolls", 1, 1, set_nbpoll, + "Set non-blocking polls", + "Set the number of non-blocking polls", + {ARG_TYPE_NUMERIC, 0, 0} + }, + { + "log_throttling", 3, 3, set_log_throttling, + "Set log throttling", + "Set the log throttling configuration", + {ARG_TYPE_NUMERIC, ARG_TYPE_NUMERIC, ARG_TYPE_NUMERIC} + }, + { EMPTY_OPTION } }; static void clear_server(DCB *dcb, SERVER *server, char *bit); /** * The subcommands of the clear command */ -struct subcommand clearoptions[] = { - { "server", 2, clear_server, - "Clear the status of a server. E.g. clear server dbnode2 master", - "Clear the status of a server. E.g. clear server 0x4838320 master", - {ARG_TYPE_SERVER, ARG_TYPE_STRING, 0} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } +struct subcommand clearoptions[] = +{ + { + "server", 2, 2, clear_server, + "Clear server status", + "Clear the status of a server. E.g. clear server dbnode2 master", + {ARG_TYPE_SERVER, ARG_TYPE_STRING, 0} + }, + { EMPTY_OPTION } }; static void reload_dbusers(DCB *dcb, SERVICE *service); @@ -421,17 +502,21 @@ static void reload_config(DCB *dcb); /** * The subcommands of the reload command */ -struct subcommand reloadoptions[] = { - { "config", 0, reload_config, - "Reload the configuration data for MaxScale.", - "Reload the configuration data for MaxScale.", - {0, 0, 0} }, - { "dbusers", 1, reload_dbusers, - "Reload the dbuser data for a service. E.g. reload dbusers \"splitter service\"", - "Reload the dbuser data for a service. E.g. reload dbusers 0x849420", - {ARG_TYPE_SERVICE, 0, 0} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } +struct subcommand reloadoptions[] = +{ + { + "config", 0, 0, reload_config, + "Reload the configuration", + "Reload the configuration data for MaxScale", + {0, 0, 0} + }, + { + "dbusers", 1, 1, reload_dbusers, + "Reload users table", + "Reload the users for a service. E.g. reload dbusers \"splitter service\"", + {ARG_TYPE_SERVICE, 0, 0} + }, + { EMPTY_OPTION } }; static void enable_log_action(DCB *, char *); @@ -458,106 +543,95 @@ static void disable_account(DCB *, char *user); /** * * The subcommands of the enable command * */ -struct subcommand enableoptions[] = { +struct subcommand enableoptions[] = +{ { "heartbeat", - 1, + 1, 1, enable_monitor_replication_heartbeat, - "Enable the monitor replication heartbeat, pass a monitor name as argument", - "Enable the monitor replication heartbeat, pass a monitor name as argument", + "Enable monitor replication heartbeat", + "Enable the monitor replication heartbeat, the parameter is the monitor name", {ARG_TYPE_MONITOR, 0, 0} }, { "log", - 1, + 1, 1, enable_log_action, - "[deprecated] Enable Log options for MaxScale, options 'trace' | 'error' | 'message'." - "E.g. 'enable log message'.", - "[deprecated] Enable Log options for MaxScale, options 'trace' | 'error' | 'message'." - "E.g. 'enable log message'.", + "[deprecated] Enable a logging level", + "Options 'trace' | 'error' | 'message'. E.g. 'enable log message'.", {ARG_TYPE_STRING, 0, 0} }, { "log-priority", - 1, + 1, 1, enable_log_priority, - "Enable a logging priority; options 'err' | 'warning' | 'notice' | 'info' | 'debug'. " - "E.g.: 'enable log-priority info'.", - "Enable a logging priority; options 'err' | 'warning' | 'notice' | 'info' | 'debug'. " + "Enable a logging priority", + "Enable a logging priority for MaxScale, parameters must be one of " + "'err', 'warning', 'notice', 'info' or 'debug'. " "E.g.: 'enable log-priority info'.", {ARG_TYPE_STRING, 0, 0} }, { "sessionlog", - 2, + 2, 2, enable_sess_log_action, - "[deprecated] Enable Log options for a single session. Usage: enable sessionlog [trace | error | " - "message | debug] \t E.g. enable sessionlog message 123.", - "[deprecated] Enable Log options for a single session. Usage: enable sessionlog [trace | error | " + "[deprecated] Enable a logging level for a single session", + "Usage: enable sessionlog [trace | error | " "message | debug] \t E.g. enable sessionlog message 123.", {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} }, { "sessionlog-priority", - 2, + 2, 2, enable_sess_log_priority, - "Enable a logging priority for a particular session. " - "Usage: enable sessionlog-priority [err | warning | notice | info | debug] " - "message | debug] \t E.g. enable sessionlog-priority info 123.", - "Enable a logging priority for a particular session. " + "Enable a logging priority for a session", "Usage: enable sessionlog-priority [err | warning | notice | info | debug] " "message | debug] \t E.g. enable sessionlog-priority info 123.", {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} }, { "root", - 1, + 1, 1, enable_service_root, - "Enable root access to a service, pass a service name to enable root access", + "Enable root user access", "Enable root access to a service, pass a service name to enable root access", {ARG_TYPE_SERVICE, 0, 0} }, { "feedback", - 0, + 0, 0, enable_feedback_action, - "Enable MaxScale modules list sending via http to notification service", + "Enable MaxScale feedback", "Enable MaxScale modules list sending via http to notification service", {0, 0, 0} }, { "syslog", - 0, + 0, 0, enable_syslog, - "Enable syslog logging", + "Enable syslog", "Enable syslog logging", {0, 0, 0} }, { "maxlog", - 0, + 0, 0, enable_maxlog, - "Enable maxlog logging", - "Enable maxlog logging", + "Enable MaxScale logging", + "Enable MaxScale logging", {0, 0, 0} }, { "account", - 1, + 1, 1, enable_account, - "Enable maxadmin usage for Linux user. E.g.:\n" - " MaxScale> enable account alice", - "Enable maxadmin usage for Linux user. E.g.:\n" + "Activate a Linux user", + "Enable maxadmin usage for a Linux user. E.g.:\n" " MaxScale> enable account alice", {ARG_TYPE_STRING, 0, 0} }, { - NULL, - 0, - NULL, - NULL, - NULL, - {0, 0, 0} + EMPTY_OPTION } }; @@ -566,106 +640,95 @@ struct subcommand enableoptions[] = { /** * * The subcommands of the disable command * */ -struct subcommand disableoptions[] = { +struct subcommand disableoptions[] = +{ { "heartbeat", - 1, + 1, 1, disable_monitor_replication_heartbeat, - "Disable the monitor replication heartbeat", + "Disable replication heartbeat", "Disable the monitor replication heartbeat", {ARG_TYPE_MONITOR, 0, 0} }, { "log", - 1, + 1, 1, disable_log_action, - "[deprecated] Disable Log for MaxScale, Options: 'debug' | 'trace' | 'error' | 'message'." - "E.g. 'disable log debug'.", - "[deprecated] Disable Log for MaxScale, Options: 'debug' | 'trace' | 'error' | 'message'." - "E.g. 'disable log debug'.", + "[deprecated] Disable log for MaxScale", + "Options: 'debug' | 'trace' | 'error' | 'message'." + "E.g. 'disable log debug'", {ARG_TYPE_STRING, 0, 0} }, { "log-priority", - 1, + 1, 1, disable_log_priority, - "Disable a logging priority; options 'err' | 'warning' | 'notice' | 'info' | 'debug'. " - "E.g.: 'disable log-priority info'.", - "Disable a logging priority; options 'err' | 'warning' | 'notice' | 'info' | 'debug'. " - "E.g.: 'disable log-priority info'.", + "Disable a logging priority", + "Options 'err' | 'warning' | 'notice' | 'info' | 'debug'. " + "E.g.: 'disable log-priority info'", {ARG_TYPE_STRING, 0, 0} }, { "sessionlog", - 2, + 2, 2, disable_sess_log_action, - "[deprecated] Disable Log options for a single session. Usage: disable sessionlog [trace | error | " - "message | debug] \t E.g. disable sessionlog message 123.", - "[deprecated] Disable Log options for a single session. Usage: disable sessionlog [trace | error | " - "message | debug] \t E.g. disable sessionlog message 123.", + "[deprecated] Disable log options", + "Disable Log options for a single session. Usage: disable sessionlog [trace | error | " + "message | debug] \t E.g. disable sessionlog message 123", {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} }, { "sessionlog-priority", - 2, + 2, 2, disable_sess_log_priority, - "Disable a logging priority for a particular session. " + "Disable a logging priority for a particular session", "Usage: disable sessionlog-priority [err | warning | notice | info | debug] " - "message | debug] \t E.g. enable sessionlog-priority info 123.", - "Enable a logging priority for a particular session. " - "Usage: disable sessionlog-priority [err | warning | notice | info | debug] " - "message | debug] \t E.g. enable sessionlog-priority info 123.", + "message | debug] \t E.g. enable sessionlog-priority info 123", {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} }, { "root", - 1, + 1, 1, disable_service_root, - "Disable root access to a service", + "Disable root access", "Disable root access to a service", {ARG_TYPE_SERVICE, 0, 0} }, { "feedback", - 0, + 0, 0, disable_feedback_action, - "Disable MaxScale modules list sending via http to notification service", + "Disable feedback", "Disable MaxScale modules list sending via http to notification service", {0, 0, 0} }, { "syslog", - 0, + 0, 0, disable_syslog, - "Disable syslog logging", + "Disable syslog", "Disable syslog logging", {0, 0, 0} }, { "maxlog", - 0, + 0, 0, disable_maxlog, - "Disable maxlog logging", - "Disable maxlog logging", + "Disable MaxScale logging", + "Disable MaxScale logging", {0, 0, 0} }, { "account", - 1, + 1, 1, disable_account, - "Disable maxadmin usage for Linux user. E.g.:\n" - " MaxScale> disable account alice", + "Disable Linux user", "Disable maxadmin usage for Linux user. E.g.:\n" " MaxScale> disable account alice", {ARG_TYPE_STRING, 0, 0} }, { - NULL, - 0, - NULL, - NULL, - NULL, - {0, 0, 0} + EMPTY_OPTION } }; @@ -677,7 +740,8 @@ static void fail_accept(DCB* dcb, char* arg1, char* arg2); /** * * The subcommands of the fail command * */ -struct subcommand failoptions[] = { +struct subcommand failoptions[] = +{ { "backendfd", 0, @@ -715,39 +779,116 @@ struct subcommand failoptions[] = { static void telnetdAddUser(DCB *, char *user, char *password); +static void cmd_AddServer(DCB *dcb, void *a, void *b) +{ + SERVER *server = (SERVER*)a; + char *name = (char*)b; + + SERVICE *service = service_find(name); + MONITOR *monitor = monitor_find(name); + + if (service || monitor) + { + ss_dassert(service == NULL || monitor == NULL); + + if (service) + { + serviceAddBackend(service, server); + } + else if (monitor) + { + monitorAddServer(monitor, server); + } + + const char *target = service ? "service" : "monitor"; + + MXS_NOTICE("Added server '%s' to %s '%s'", server->unique_name, target, name); + dcb_printf(dcb, "Added server '%s' to %s '%s'\n", server->unique_name, target, name); + } + else + { + dcb_printf(dcb, "No service or monitor with the name '%s'\n", name); + } +} + /** * The subcommands of the add command */ -struct subcommand addoptions[] = { - { "user", 2, telnetdAddUser, - "Add insecure account for using maxadmin over the network. E.g.:\n" - " MaxScale> add user bob somepass", - "Add insecure account for using maxadmin over the network. E.g.:\n" - " MaxScale> add user bob somepass", - {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } +struct subcommand addoptions[] = +{ + { + "user", 2, 2, telnetdAddUser, + "Add account for maxadmin", + "Add insecure account for using maxadmin over the network. E.g.:\n" + " MaxScale> add user bob somepass", + {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} + }, + { + "server", 2, 2, cmd_AddServer, + "Add a new server to a service", + "Usage: add server SERVER TARGET\n" + "The TARGET must be either a service or a monitor", + {ARG_TYPE_SERVER, ARG_TYPE_STRING, 0} + }, + { EMPTY_OPTION} }; static void telnetdRemoveUser(DCB *, char *user, char *password); +static void cmd_RemoveServer(DCB *dcb, void *a, void *b) +{ + SERVER *server = (SERVER*)a; + char *name = (char*)b; + SERVICE *service = service_find(name); + MONITOR *monitor = monitor_find(name); + + if (service || monitor) + { + ss_dassert(service == NULL || monitor == NULL); + + if (service) + { + serviceRemoveBackend(service, server); + } + else if (monitor) + { + monitorRemoveServer(monitor, server); + } + + const char *target = service ? "service" : "monitor"; + MXS_NOTICE("Removed server '%s' from %s '%s'", server->unique_name, target, name); + dcb_printf(dcb, "Removed server '%s' from %s '%s'\n", server->unique_name, target, name); + } + else + { + dcb_printf(dcb, "No service or monitor with the name '%s'\n", name); + } +} + /** * The subcommands of the remove command */ -struct subcommand removeoptions[] = { +struct subcommand removeoptions[] = +{ { "user", - 2, + 2, 2, telnetdRemoveUser, + "Remove account from maxadmin", "Remove account for using maxadmin over the network. E.g.:\n" " MaxAdmin> remove user bob somepass", - "Remove account for using maxadmin over the network. E.g.:\n" - " MaxAdmin> remove user bob somepass", - {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} + {ARG_TYPE_STRING, ARG_TYPE_STRING} }, { - NULL, 0, NULL, NULL, NULL, {0, 0, 0} + "server", 2, 2, cmd_RemoveServer, + "Remove a server from a service or a monitor", + "Usage: remove server SERVER TARGET\n" + "The TARGET must be either a service or a monitor", + {ARG_TYPE_SERVER, ARG_TYPE_STRING} + }, + { + EMPTY_OPTION } }; @@ -820,37 +961,280 @@ flushlogs(DCB *pdcb) /** * The subcommands of the flush command */ -struct subcommand flushoptions[] = { +struct subcommand flushoptions[] = +{ { "log", - 1, + 1, 1, flushlog, - "Flush the content of a log file, close that log, rename it and open a new log file", + "Flush log files", "Flush the content of a log file, close that log, rename it and open a new log file", {ARG_TYPE_STRING, 0, 0} }, { "logs", - 0, + 0, 0, flushlogs, - "Flush the content of all log files, close those logs, rename them and open a new log files", + "Flush log files", "Flush the content of all log files, close those logs, rename them and open a new log files", {0, 0, 0} }, { - NULL, 0, NULL, NULL, NULL, {0, 0, 0} + EMPTY_OPTION } }; +/** This is used to prevent concurrent creation or removal of servers */ +static SPINLOCK server_mod_lock = SPINLOCK_INIT; + +/** + * Create a new server + * + * @param dcb Client DCB + * @param name Server name + * @param address Server network address + * @param port Server port + * @param protocol Protocol, NULL for default (MySQLBackend) + * @param authenticator Authenticator module, NULL for default (MySQLBackendAuth) + * @param authenticator_options Authenticator options, NULL for no options + */ +static void createServer(DCB *dcb, char *name, char *address, char *port, + char *protocol, char *authenticator, char *authenticator_options) +{ + spinlock_acquire(&server_mod_lock); + + if (server_find_by_unique_name(name) == NULL) + { + if (protocol == NULL) + { + protocol = "MySQLBackend"; + } + + SERVER *server = server_alloc(address, protocol, atoi(port), authenticator, + authenticator_options); + + if (server) + { + server_set_unique_name(server, name); + + if (server_serialize(server)) + { + dcb_printf(dcb, "Created server '%s'\n", name); + } + else + { + dcb_printf(dcb, "WARNING: The server was added to the runtime " + "configuration but persisting the new server to disk " + "failed. This server will NOT be loaded when MaxScale " + "is restarted. See log file for more details on why it failed.\n"); + } + } + else + { + dcb_printf(dcb, "Failed to create new server, see log file for more details\n"); + } + } + else + { + dcb_printf(dcb, "Server '%s' already exists.\n", name); + } + + spinlock_release(&server_mod_lock); +} + +struct subcommand createoptions[] = +{ + { + "server", 3, 6, createServer, + "Create a new server", + "Usage: create server NAME HOST PORT [PROTOCOL] [AUTHENTICATOR] [OPTIONS]\n" + "Create a new server from the following parameters.\n" + "NAME Server name\n" + "HOST Server host address\n" + "PORT Server port\n" + "PROTOCOL Server protocol (default MySQLBackend)\n" + "AUTHENTICATOR Authenticator module name (default MySQLAuth)\n" + "OPTIONS Options for the authenticator module\n\n" + "The first three parameters are required, the others are optional.\n", + { + ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING, + ARG_TYPE_STRING, ARG_TYPE_STRING + } + }, + { + EMPTY_OPTION + } +}; + +static void destroyServer(DCB *dcb, SERVER *server) +{ + dcb_printf(dcb, "Not yet implemented.\n"); +} + +struct subcommand destroyoptions[] = +{ + { + "server", 1, 1, destroyServer, + "Destroy a server", + "Usage: destroy server NAME", + {ARG_TYPE_STRING} + }, + { + EMPTY_OPTION + } +}; + +static void alterServer(DCB *dcb, SERVER *server, char *key, char *value) +{ + bool unknown = false; + + if (strcmp(key, "address") == 0) + { + server_update_address(server, value); + } + else if (strcmp(key, "port") == 0) + { + server_update_port(server, atoi(value)); + } + else if (strcmp(key, "monuser") == 0) + { + server_update_credentials(server, value, server->monpw); + } + else if (strcmp(key, "monpw") == 0) + { + server_update_credentials(server, server->monuser, value); + } + else if (server_is_ssl_parameter(key)) + { + server_update_ssl(server, key, value); + } + else + { + unknown = true; + } + + if (unknown) + { + dcb_printf(dcb, "Unknown parameter '%s'", key); + } +} + +/** + * @brief Convert a string value to a positive integer + * + * If the value is not a positive integer, an error is printed to @c dcb. + * + * @param dcb Client DCB + * @param value String value + * @return 0 on error, otherwise a positive integer + */ +static long get_positive_int(DCB *dcb, const char *value) +{ + char *endptr; + long ival = strtol(value, &endptr, 10); + + if (*endptr == '\0' && ival > 0) + { + return ival; + } + + dcb_printf(dcb, "Invalid value: %s", value); + return 0; +} + +static void alterMonitor(DCB *dcb, MONITOR *monitor, char *key, char *value) +{ + bool unknown = false; + if (strcmp(key, "user") == 0) + { + monitorAddUser(monitor, value, monitor->password); + } + else if (strcmp(key, "password") == 0) + { + monitorAddUser(monitor, monitor->user, value); + } + else if (strcmp(key, "monitor_interval") == 0) + { + long ival = get_positive_int(dcb, value); + if (ival) + { + monitorSetInterval(monitor, ival); + } + } + else if (strcmp(key, "backend_connect_timeout") == 0) + { + long ival = get_positive_int(dcb, value); + if (ival) + { + monitorSetNetworkTimeout(monitor, MONITOR_CONNECT_TIMEOUT, ival); + } + } + else if (strcmp(key, "backend_write_timeout") == 0) + { + long ival = get_positive_int(dcb, value); + if (ival) + { + monitorSetNetworkTimeout(monitor, MONITOR_READ_TIMEOUT, ival); + } + } + else if (strcmp(key, "backend_read_timeout") == 0) + { + long ival = get_positive_int(dcb, value); + if (ival) + { + monitorSetNetworkTimeout(monitor, MONITOR_WRITE_TIMEOUT, ival); + } + } + else + { + unknown = true; + } + + if (unknown) + { + dcb_printf(dcb, "Unknown parameter '%s'", key); + } +} + +struct subcommand alteroptions[] = +{ + { + "server", 3, 3, alterServer, + "Alter server parameters", + "Usage: alter server NAME KEY VALUE\n" + "This will alter an existing parameter of a server. The accepted values\n" + "for KEY are: 'address', 'port', 'monuser', 'monpw'", + {ARG_TYPE_SERVER, ARG_TYPE_STRING, ARG_TYPE_STRING} + }, + { + "monitor", 3, 3, alterMonitor, + "Alter monitor parameters", + "Usage: alter monitor NAME KEY VALUE\n" + "This will alter an existing parameter of a monitor. The accepted values\n" + "for KEY are: 'user', 'password', 'monitor_interval',\n" + "'backend_connect_timeout', 'backend_write_timeout', 'backend_read_timeout'", + {ARG_TYPE_MONITOR, ARG_TYPE_STRING, ARG_TYPE_STRING} + }, + { + EMPTY_OPTION + } +}; /** * The debug command table */ -static struct { +static struct +{ char *cmd; struct subcommand *options; -} cmds[] = { +} cmds[] = +{ { "add", addoptions }, + { "remove", removeoptions }, + { "create", createoptions }, + { "destroy", destroyoptions }, + { "alter", alteroptions }, + { "set", setoptions }, { "clear", clearoptions }, { "disable", disableoptions }, { "enable", enableoptions }, @@ -860,11 +1244,9 @@ static struct { { "flush", flushoptions }, { "list", listoptions }, { "reload", reloadoptions }, - { "remove", removeoptions }, { "restart", restartoptions }, - { "set", setoptions }, - { "show", showoptions }, { "shutdown", shutdownoptions }, + { "show", showoptions }, { "sync", syncoptions }, { NULL, NULL } }; @@ -887,63 +1269,63 @@ convert_arg(int mode, char *arg, int arg_type) switch (arg_type) { - case ARG_TYPE_ADDRESS: - return (unsigned long)strtol(arg, NULL, 0); - case ARG_TYPE_STRING: - return (unsigned long)arg; - case ARG_TYPE_SERVICE: - if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) - { - rval = (unsigned long)service_find(arg); - } - return rval; - case ARG_TYPE_SERVER: - if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) - { - rval = (unsigned long)server_find_by_unique_name(arg); - } - return rval; - case ARG_TYPE_DBUSERS: - if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) - { - service = service_find(arg); - if (service) + case ARG_TYPE_ADDRESS: + return (unsigned long)strtol(arg, NULL, 0); + case ARG_TYPE_STRING: + return (unsigned long)arg; + case ARG_TYPE_SERVICE: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) { - return (unsigned long)(service->ports->users); + rval = (unsigned long)service_find(arg); } - else + return rval; + case ARG_TYPE_SERVER: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) { - return 0; + rval = (unsigned long)server_find_by_unique_name(arg); } - } - return rval; - case ARG_TYPE_DCB: - rval = (unsigned long)strtol(arg, NULL, 0); - if (mode == CLIM_USER && dcb_isvalid((DCB *)rval) == 0) - { - rval = 0; - } - return rval; - case ARG_TYPE_SESSION: - rval = (unsigned long)strtol(arg, NULL, 0); - if (mode == CLIM_USER && session_isvalid((SESSION *)rval) == 0) - { - rval = 0; - } - return rval; - case ARG_TYPE_MONITOR: - if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) - { - rval = (unsigned long)monitor_find(arg); - } - return rval; - case ARG_TYPE_FILTER: - if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) - { - rval = (unsigned long)filter_find(arg); - } - return rval; - case ARG_TYPE_NUMERIC: + return rval; + case ARG_TYPE_DBUSERS: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) + { + service = service_find(arg); + if (service) + { + return (unsigned long)(service->ports->users); + } + else + { + return 0; + } + } + return rval; + case ARG_TYPE_DCB: + rval = (unsigned long)strtol(arg, NULL, 0); + if (mode == CLIM_USER && dcb_isvalid((DCB *)rval) == 0) + { + rval = 0; + } + return rval; + case ARG_TYPE_SESSION: + rval = (unsigned long)strtol(arg, NULL, 0); + if (mode == CLIM_USER && session_isvalid((SESSION *)rval) == 0) + { + rval = 0; + } + return rval; + case ARG_TYPE_MONITOR: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) + { + rval = (unsigned long)monitor_find(arg); + } + return rval; + case ARG_TYPE_FILTER: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) + { + rval = (unsigned long)filter_find(arg); + } + return rval; + case ARG_TYPE_NUMERIC: { int i; for (i = 0; arg[i]; i++) @@ -979,7 +1361,6 @@ execute_cmd(CLI_SESSION *cli) DCB *dcb = cli->session->client_dcb; int argc, i, j, found = 0; char *args[MAXARGS + 1]; - unsigned long arg1, arg2, arg3; int in_quotes = 0, escape_next = 0; char *ptr, *lptr; bool in_space = false; @@ -1102,8 +1483,9 @@ execute_cmd(CLI_SESSION *cli) dcb_printf(dcb, "Available options to the %s command:\n", args[1]); for (j = 0; cmds[i].options[j].arg1; j++) { - dcb_printf(dcb, " %-12s %s\n", cmds[i].options[j].arg1, - cmds[i].options[j].help); + dcb_printf(dcb, "'%s' - %s\n\n\t%s\n\n", cmds[i].options[j].arg1, + cmds[i].options[j].help, cmds[i].options[j].devhelp); + } } } @@ -1129,74 +1511,89 @@ execute_cmd(CLI_SESSION *cli) if (strcasecmp(args[1], cmds[i].options[j].arg1) == 0) { found = 1; /**< command and sub-command match */ - if (argc != cmds[i].options[j].n_args) + if (argc < cmds[i].options[j].argc_min) { - dcb_printf(dcb, "Incorrect number of arguments: %s %s expects %d arguments\n", + dcb_printf(dcb, "Incorrect number of arguments: %s %s expects at least %d arguments\n", cmds[i].cmd, cmds[i].options[j].arg1, - cmds[i].options[j].n_args); + cmds[i].options[j].argc_min); } else { - switch (cmds[i].options[j].n_args) - { - case 0: - cmds[i].options[j].fn(dcb); - break; - case 1: - arg1 = convert_arg(cli->mode, args[2], cmds[i].options[j].arg_types[0]); + unsigned long arg_list[MAXARGS] = {}; - if (arg1) + for (int k = 0; k < cmds[i].options[j].argc_max && k < argc; k++) + { + arg_list[k] = convert_arg(cli->mode, args[k + 2], cmds[i].options[j].arg_types[k]); + if (arg_list[k] == 0) { - cmds[i].options[j].fn(dcb, arg1); - } - else - { - dcb_printf(dcb, "Invalid argument: %s\n", - args[2]); - } - break; - case 2: - arg1 = convert_arg(cli->mode, args[2], cmds[i].options[j].arg_types[0]); - arg2 = convert_arg(cli->mode, args[3], cmds[i].options[j].arg_types[1]); - if (arg1 && arg2) - { - cmds[i].options[j].fn(dcb, arg1, arg2); - } - else if (arg1 == 0) - { - dcb_printf(dcb, "Invalid argument: %s\n", - args[2]); - } - else - { - dcb_printf(dcb, "Invalid argument: %s\n", - args[3]); - } - break; - case 3: - arg1 = convert_arg(cli->mode, args[2], cmds[i].options[j].arg_types[0]); - arg2 = convert_arg(cli->mode, args[3], cmds[i].options[j].arg_types[1]); - arg3 = convert_arg(cli->mode, args[4], cmds[i].options[j].arg_types[2]); - if (arg1 && arg2 && arg3) - { - cmds[i].options[j].fn(dcb, arg1, arg2, arg3); - } - else if (arg1 == 0) - { - dcb_printf(dcb, "Invalid argument: %s\n", - args[2]); - } - else if (arg2 == 0) - { - dcb_printf(dcb, "Invalid argument: %s\n", - args[3]); - } - else if (arg3 == 0) - { - dcb_printf(dcb, "Invalid argument: %s\n", - args[4]); + dcb_printf(dcb, "Invalid argument: %s\n", args[k + 2]); + return 0; } } + + switch (cmds[i].options[j].argc_max) + { + case 0: + cmds[i].options[j].fn(dcb); + break; + case 1: + cmds[i].options[j].fn(dcb, arg_list[0]); + break; + case 2: + cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1]); + break; + case 3: + cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2]); + break; + case 4: + cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2], + arg_list[3]); + break; + case 5: + cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2], + arg_list[3], arg_list[4]); + break; + case 6: + cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2], + arg_list[3], arg_list[4], arg_list[5]); + break; + case 7: + cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2], + arg_list[3], arg_list[4], arg_list[5], + arg_list[6]); + break; + case 8: + cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2], + arg_list[3], arg_list[4], arg_list[5], + arg_list[6], arg_list[7]); + break; + case 9: + cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2], + arg_list[3], arg_list[4], arg_list[5], + arg_list[6], arg_list[7], arg_list[8]); + break; + case 10: + cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2], + arg_list[3], arg_list[4], arg_list[5], + arg_list[6], arg_list[7], arg_list[8], + arg_list[9]); + break; + case 11: + cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2], + arg_list[3], arg_list[4], arg_list[5], + arg_list[6], arg_list[7], arg_list[8], + arg_list[9], arg_list[10]); + break; + case 12: + cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2], + arg_list[3], arg_list[4], arg_list[5], + arg_list[6], arg_list[7], arg_list[8], + arg_list[9], arg_list[10], arg_list[11]); + break; + default: + dcb_printf(dcb, "Error: Maximum argument count is %d.\n", MAXARGS); + break; + } } } } @@ -1509,11 +1906,11 @@ struct log_action_entry static bool get_log_action(const char* name, struct log_action_entry* entryp) { static const struct log_action_entry entries[] = - { - { "debug", LOG_DEBUG, "debug" }, - { "trace", LOG_INFO, "info" }, - { "message", LOG_NOTICE, "notice" }, - }; + { + { "debug", LOG_DEBUG, "debug" }, + { "trace", LOG_INFO, "info" }, + { "message", LOG_NOTICE, "notice" }, + }; const int n_entries = sizeof(entries) / sizeof(entries[0]); bool found = false; @@ -1624,13 +2021,13 @@ static int compare_log_priority_entries(const void* l, const void* r) static int string_to_priority(const char* name) { static const struct log_priority_entry LOG_PRIORITY_ENTRIES[] = - { - // NOTE: If you make changes to this array, ensure that it remains alphabetically ordered. - { "debug", LOG_DEBUG }, - { "info", LOG_INFO }, - { "notice", LOG_NOTICE }, - { "warning", LOG_WARNING }, - }; + { + // NOTE: If you make changes to this array, ensure that it remains alphabetically ordered. + { "debug", LOG_DEBUG }, + { "info", LOG_INFO }, + { "notice", LOG_NOTICE }, + { "warning", LOG_WARNING }, + }; const size_t N_LOG_PRIORITY_ENTRIES = sizeof(LOG_PRIORITY_ENTRIES) / sizeof(LOG_PRIORITY_ENTRIES[0]); @@ -1971,28 +2368,29 @@ static void fail_accept( fail_accept_errno = atoi(arg1); char errbuf[MXS_STRERROR_BUFLEN]; - switch(fail_accept_errno) { - case EAGAIN: + switch (fail_accept_errno) + { + case EAGAIN: // case EWOULDBLOCK: - case EBADF: - case EINTR: - case EINVAL: - case EMFILE: - case ENFILE: - case ENOTSOCK: - case EOPNOTSUPP: - case ENOBUFS: - case ENOMEM: - case EPROTO: - fail_next_accept = failcount; - break; + case EBADF: + case EINTR: + case EINVAL: + case EMFILE: + case ENFILE: + case ENOTSOCK: + case EOPNOTSUPP: + case ENOBUFS: + case ENOMEM: + case EPROTO: + fail_next_accept = failcount; + break; - default: - dcb_printf(dcb, - "[%d, %s] is not valid errno for accept.\n", - fail_accept_errno, - strerror_r(fail_accept_errno, errbuf, sizeof(errbuf))); - return ; + default: + dcb_printf(dcb, + "[%d, %s] is not valid errno for accept.\n", + fail_accept_errno, + strerror_r(fail_accept_errno, errbuf, sizeof(errbuf))); + return ; } } #endif /* FAKE_CODE */ diff --git a/server/modules/routing/readconnroute/readconnection.h b/server/modules/routing/readconnroute/readconnection.h index 13b4fa2ad..dfcf153fd 100644 --- a/server/modules/routing/readconnroute/readconnection.h +++ b/server/modules/routing/readconnroute/readconnection.h @@ -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 */ diff --git a/server/modules/routing/readconnroute/readconnroute.c b/server/modules/routing/readconnroute/readconnroute.c index e6d2ecfc3..45294044d 100644 --- a/server/modules/routing/readconnroute/readconnroute.c +++ b/server/modules/routing/readconnroute/readconnroute.c @@ -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; } } } diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index a7f6532e8..aab1775f5 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -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 slaves must be found if the router is + * in the strict mode. If sessions without master are allowed, only + * 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 slaves must be found if the router is - * in the strict mode. If sessions without master are allowed, only - * 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; +} diff --git a/server/modules/routing/readwritesplit/readwritesplit.h b/server/modules/routing/readwritesplit/readwritesplit.h index 5a8d9dd36..5e24a4162 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.h +++ b/server/modules/routing/readwritesplit/readwritesplit.h @@ -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; diff --git a/server/modules/routing/readwritesplit/rwsplit_mysql.c b/server/modules/routing/readwritesplit/rwsplit_mysql.c index 6dcdd50d5..6f1b31410 100644 --- a/server/modules/routing/readwritesplit/rwsplit_mysql.c +++ b/server/modules/routing/readwritesplit/rwsplit_mysql.c @@ -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 */ diff --git a/server/modules/routing/readwritesplit/rwsplit_route_stmt.c b/server/modules/routing/readwritesplit/rwsplit_route_stmt.c index ae6e42e1f..298358b4f 100644 --- a/server/modules/routing/readwritesplit/rwsplit_route_stmt.c +++ b/server/modules/routing/readwritesplit/rwsplit_route_stmt.c @@ -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; } diff --git a/server/modules/routing/readwritesplit/rwsplit_select_backends.c b/server/modules/routing/readwritesplit/rwsplit_select_backends.c index fba1d7271..35875be65 100644 --- a/server/modules/routing/readwritesplit/rwsplit_select_backends.c +++ b/server/modules/routing/readwritesplit/rwsplit_select_backends.c @@ -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 && diff --git a/server/modules/routing/schemarouter/schemarouter.c b/server/modules/routing/schemarouter/schemarouter.c index 1a7f12bda..6d802fe5a 100644 --- a/server/modules/routing/schemarouter/schemarouter.c +++ b/server/modules/routing/schemarouter/schemarouter.c @@ -84,8 +84,6 @@ static void handleError(ROUTER* instance, DCB* backend_dcb, error_action_t action, bool* succp); - -static int router_get_servercount(ROUTER_INSTANCE* router); static backend_ref_t* get_bref_from_dcb(ROUTER_CLIENT_SES* rses, DCB* dcb); static route_target_t get_shard_route_target(qc_query_type_t qtype, @@ -307,7 +305,7 @@ char* get_lenenc_str(void* data) showdb_response_t parse_showdb_response(ROUTER_CLIENT_SES* rses, backend_ref_t* bref, GWBUF** buffer) { unsigned char* ptr; - char* target = bref->bref_backend->backend_server->unique_name; + char* target = bref->bref_backend->server->unique_name; GWBUF* buf; bool duplicate_found = false; showdb_response_t rval = SHOWDB_PARTIAL_RESPONSE; @@ -394,12 +392,12 @@ showdb_response_t parse_showdb_response(ROUTER_CLIENT_SES* rses, backend_ref_t* { atomic_add(&bref->n_mapping_eof, 1); MXS_INFO("schemarouter: SHOW DATABASES fully received from %s.", - bref->bref_backend->backend_server->unique_name); + bref->bref_backend->server->unique_name); } else { MXS_INFO("schemarouter: SHOW DATABASES partially received from %s.", - bref->bref_backend->backend_server->unique_name); + bref->bref_backend->server->unique_name); } gwbuf_free(buf); @@ -454,13 +452,13 @@ int gen_databaselist(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* session) { if (BREF_IS_IN_USE(&session->rses_backend_ref[i]) && !BREF_IS_CLOSED(&session->rses_backend_ref[i]) & - SERVER_IS_RUNNING(session->rses_backend_ref[i].bref_backend->backend_server)) + SERVER_IS_RUNNING(session->rses_backend_ref[i].bref_backend->server)) { clone = gwbuf_clone(buffer); dcb = session->rses_backend_ref[i].bref_dcb; rval |= !dcb->func.write(dcb, clone); MXS_DEBUG("schemarouter: Wrote SHOW DATABASES to %s for session %p: returned %d", - session->rses_backend_ref[i].bref_backend->backend_server->unique_name, + session->rses_backend_ref[i].bref_backend->server->unique_name, session->rses_client_dcb->session, rval); } @@ -561,7 +559,7 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, for (i = 0; i < client->rses_nbackends; i++) { - char *srvnm = client->rses_backend_ref[i].bref_backend->backend_server->unique_name; + char *srvnm = client->rses_backend_ref[i].bref_backend->server->unique_name; if (strcmp(srvnm, buffer->hint->data) == 0) { rval = srvnm; @@ -597,21 +595,16 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, */ bool check_shard_status(ROUTER_INSTANCE* router, char* shard) { - int i; - bool rval = false; - - for (i = 0; router->servers[i]; i++) + for (SERVER_REF *ref = router->service->dbref; ref; ref = ref->next) { - if (strcmp(router->servers[i]->backend_server->unique_name, shard) == 0) + if (strcmp(ref->server->unique_name, shard) == 0 && + SERVER_IS_RUNNING(ref->server)) { - if (SERVER_IS_RUNNING(router->servers[i]->backend_server)) - { - rval = true; - } - break; + return true; } } - return rval; + + return false; } /** @@ -695,22 +688,21 @@ ROUTER_OBJECT* GetModuleObject() static ROUTER* createInstance(SERVICE *service, char **options) { ROUTER_INSTANCE* router; - SERVER_REF* server; CONFIG_PARAMETER* conf; - int nservers; - int i; CONFIG_PARAMETER* param; if ((router = MXS_CALLOC(1, sizeof(ROUTER_INSTANCE))) == NULL) { return NULL; } + if ((router->ignored_dbs = hashtable_alloc(SCHEMAROUTER_HASHSIZE, hashkeyfun, hashcmpfun)) == NULL) { MXS_ERROR("Memory allocation failed when allocating schemarouter database ignore list."); MXS_FREE(router); return NULL; } + hashtable_memory_fns(router->ignored_dbs, hashtable_item_strdup, NULL, hashtable_item_free, NULL); if ((router->shard_maps = hashtable_alloc(SCHEMAROUTER_USERHASH_SIZE, hashkeyfun, hashcmpfun)) == NULL) @@ -740,10 +732,6 @@ static ROUTER* createInstance(SERVICE *service, char **options) router->stats.ses_shortest = (double)((unsigned long)(~0)); spinlock_init(&router->lock); - /** Calculate number of servers */ - server = service->dbref; - nservers = 0; - conf = service->svc_config_param; if ((config_get_param(conf, "auth_all_servers")) == NULL) { @@ -801,17 +789,20 @@ static ROUTER* createInstance(SERVICE *service, char **options) bool failure = false; - for (i = 0; options && options[i]; i++) + for (int i = 0; options && options[i]; i++) { - char* value; - if ((value = strchr(options[i], '=')) == NULL) + char* value = strchr(options[i], '='); + + if (value == NULL) { MXS_ERROR("Unknown router options for Schemarouter: %s", options[i]); failure = true; break; } + *value = '\0'; value++; + if (strcmp(options[i], "max_sescmd_history") == 0) { router->schemarouter_config.max_sescmd_hist = atoi(value); @@ -849,94 +840,9 @@ static ROUTER* createInstance(SERVICE *service, char **options) if (failure) { MXS_FREE(router); - return NULL; + router = NULL; } - while (server != NULL) - { - nservers++; - server = server->next; - } - router->servers = (BACKEND **)MXS_CALLOC(nservers + 1, sizeof(BACKEND *)); - - if (router->servers == NULL) - { - MXS_FREE(router); - return NULL; - } - /** - * Create an array of the backend servers in the router structure to - * maintain a count of the number of connections to each - * backend server. - */ - server = service->dbref; - nservers = 0; - - while (server != NULL) - { - if ((router->servers[nservers] = MXS_MALLOC(sizeof(BACKEND))) == NULL) - { - goto clean_up; - } - router->servers[nservers]->backend_server = server->server; - router->servers[nservers]->backend_conn_count = 0; - router->servers[nservers]->weight = 1; - router->servers[nservers]->be_valid = false; - router->servers[nservers]->stats.queries = 0; - if (server->server->monuser == NULL && service->credentials.name != NULL) - { - router->servers[nservers]->backend_server->monuser = - MXS_STRDUP_A(service->credentials.name); - } - if (server->server->monpw == NULL && service->credentials.authdata != NULL) - { - router->servers[nservers]->backend_server->monpw = - MXS_STRDUP_A(service->credentials.authdata); - } -#if defined(SS_DEBUG) - router->servers[nservers]->be_chk_top = CHK_NUM_BACKEND; - router->servers[nservers]->be_chk_tail = CHK_NUM_BACKEND; -#endif - nservers += 1; - server = server->next; - } - router->servers[nservers] = NULL; - - /** - * Process the options - */ - router->bitmask = 0; - router->bitvalue = 0; - - /** - * Read config version number from service to inform what configuration - * is used if any. - */ - router->schemarouter_version = service->svc_config_version; - - /** - * 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); - goto retblock; - -clean_up: - /** clean up */ - for (i = 0; i < nservers; i++) - { - MXS_FREE(router->servers[i]); - } - MXS_FREE(router->servers); - MXS_FREE(router); - router = NULL; - /** Fallthrough */ -retblock: return (ROUTER *)router; } @@ -976,15 +882,12 @@ static void* newSession(ROUTER* router_inst, SESSION* session) ROUTER_INSTANCE* router = (ROUTER_INSTANCE *)router_inst; bool succp; int router_nservers = 0; /*< # of servers in total */ - int i; - char db[MYSQL_DATABASE_MAXLEN + 1]; + char db[MYSQL_DATABASE_MAXLEN + 1] = ""; MySQLProtocol* protocol = session->client_dcb->protocol; MYSQL_session* data = session->client_dcb->data; bool using_db = false; bool have_db = false; - *db = 0; - spinlock_acquire(&session->ses_lock); /* To enable connecting directly to a sharded database we first need @@ -1011,8 +914,7 @@ static void* newSession(ROUTER* router_inst, SESSION* session) 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; @@ -1080,47 +982,57 @@ static void* newSession(ROUTER* router_inst, SESSION* session) * responding server. */ - router_nservers = router_get_servercount(router); + router_nservers = router->service->n_dbref; /** * Create backend reference objects for this session. */ - backend_ref = (backend_ref_t *)MXS_CALLOC(1, router_nservers * sizeof(backend_ref_t)); + backend_ref = (backend_ref_t *)MXS_CALLOC(router_nservers, sizeof(backend_ref_t)); if (backend_ref == NULL) { - /** 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. */ - for (i = 0; i < router_nservers; i++) + + int i = 0; + + for (SERVER_REF *ref = router->service->dbref; ref; ref = ref->next) { + if (ref->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; + 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].n_mapping_eof = 0; - backend_ref[i].map_queue = NULL; - backend_ref[i].bref_backend = router->servers[i]; - /** store pointers to sescmd list to both cursors */ - backend_ref[i].bref_sescmd_cur.scmd_cur_rses = client_rses; - 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; + backend_ref[i].bref_state = 0; + backend_ref[i].n_mapping_eof = 0; + backend_ref[i].map_queue = NULL; + backend_ref[i].bref_backend = ref; + /** 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++; + } + } + + if (i < router_nservers) + { + router_nservers = i; } spinlock_init(&client_rses->rses_lock); client_rses->rses_backend_ref = backend_ref; + client_rses->rses_nbackends = router_nservers; /** * Find a backend servers to connect to. @@ -1130,43 +1042,23 @@ static void* newSession(ROUTER* router_inst, SESSION* session) { MXS_FREE(client_rses->rses_backend_ref); MXS_FREE(client_rses); - client_rses = NULL; - goto return_rses; + return NULL; } /** * Connect to all backend servers */ - succp = connect_backend_servers(backend_ref, - router_nservers, - session, - router); + succp = connect_backend_servers(backend_ref, router_nservers, session, router); rses_end_locked_router_action(client_rses); - /** - * Master and at least slaves must be found - */ - if (!succp) + if (!succp || !(succp = rses_begin_locked_router_action(client_rses))) { MXS_FREE(client_rses->rses_backend_ref); MXS_FREE(client_rses); - client_rses = NULL; - goto return_rses; - } - /** Copy backend pointers to router session. */ - client_rses->rses_backend_ref = backend_ref; - client_rses->rses_nbackends = router_nservers; /*< # of backend servers */ - - if (!(succp = rses_begin_locked_router_action(client_rses))) - { - MXS_FREE(client_rses->rses_backend_ref); - MXS_FREE(client_rses); - - client_rses = NULL; - goto return_rses; + return NULL; } - if (db[0] != 0x0) + if (db[0]) { /* Store the database the client is connecting to */ snprintf(client_rses->connect_db, MYSQL_DATABASE_MAXLEN + 1, "%s", db); @@ -1176,26 +1068,6 @@ static void* newSession(ROUTER* router_inst, SESSION* session) atomic_add(&router->stats.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; } @@ -1271,7 +1143,7 @@ static void closeSession(ROUTER* instance, void* router_session) */ dcb_close(dcb); /** decrease server current connection counters */ - atomic_add(&bref->bref_backend->backend_conn_count, -1); + atomic_add(&bref->bref_backend->connections, -1); } } @@ -1316,51 +1188,18 @@ 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; - backend_ref_t* bref; + ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_client_session; - router_cli_ses = (ROUTER_CLIENT_SES *)router_client_session; - router = (ROUTER_INSTANCE *)router_instance; - - for (i = 0; i < router_cli_ses->rses_nbackends; i++) + for (int i = 0; i < router_cli_ses->rses_nbackends; i++) { - bref = &router_cli_ses->rses_backend_ref[i]; - while (bref->bref_pending_cmd && - (bref->bref_pending_cmd = gwbuf_consume( - bref->bref_pending_cmd, gwbuf_length(bref->bref_pending_cmd)))) - { - ; - } + gwbuf_free(router_cli_ses->rses_backend_ref[i].bref_pending_cmd); } - 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); /** * 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; @@ -1416,15 +1255,15 @@ static bool get_shard_dcb(DCB** p_dcb, for (i = 0; i < rses->rses_nbackends; i++) { - BACKEND* b = backend_ref[i].bref_backend; + SERVER_REF* b = backend_ref[i].bref_backend; /** * To become chosen: * backend must be in use, name must match, and * the backend state must be RUNNING */ if (BREF_IS_IN_USE((&backend_ref[i])) && - (strncasecmp(name, b->backend_server->unique_name, PATH_MAX) == 0) && - SERVER_IS_RUNNING(b->backend_server)) + (strncasecmp(name, b->server->unique_name, PATH_MAX) == 0) && + SERVER_IS_RUNNING(b->server)) { *p_dcb = backend_ref[i].bref_dcb; succp = true; @@ -2226,14 +2065,13 @@ static int routeQuery(ROUTER* instance, if (TARGET_IS_ANY(route_target)) { - int z; - - for (z = 0; inst->servers[z]; z++) + for (int i = 0; i < router_cli_ses->rses_nbackends; i++) { - if (SERVER_IS_RUNNING(inst->servers[z]->backend_server)) + SERVER *server = router_cli_ses->rses_backend_ref[i].bref_backend->server; + if (SERVER_IS_RUNNING(server)) { route_target = TARGET_NAMED_SERVER; - targetserver = MXS_STRDUP_A(inst->servers[z]->backend_server->unique_name); + targetserver = MXS_STRDUP_A(server->unique_name); break; } } @@ -2241,9 +2079,7 @@ static int routeQuery(ROUTER* instance, if (TARGET_IS_ANY(route_target)) { /**No valid backends alive*/ - MXS_INFO("schemarouter: No backends are running"); - MXS_ERROR("Schemarouter: Failed to route query, " - "no backends are available."); + MXS_ERROR("Schemarouter: Failed to route query, no backends are available."); rses_end_locked_router_action(router_cli_ses); ret = 0; goto retblock; @@ -2281,8 +2117,8 @@ static int routeQuery(ROUTER* instance, scur = &bref->bref_sescmd_cur; MXS_INFO("Route query to \t%s:%d <", - bref->bref_backend->backend_server->name, - bref->bref_backend->backend_server->port); + bref->bref_backend->server->name, + bref->bref_backend->server->port); /** * Store current stmt if execution of previous session command * haven't completed yet. Note that according to MySQL protocol @@ -2311,7 +2147,6 @@ static int routeQuery(ROUTER* instance, bref = get_bref_from_dcb(router_cli_ses, target_dcb); bref_set_state(bref, BREF_QUERY_ACTIVE); bref_set_state(bref, BREF_WAITING_RESULT); - atomic_add(&bref->bref_backend->stats.queries, 1); } else { @@ -2401,17 +2236,6 @@ static void diagnostic(ROUTER *instance, DCB *dcb) 100.0 * ((double)router->stats.n_sescmd / (double)router->stats.n_queries) : 0.0; - dcb_printf(dcb, "\33[1;4m%-16s%-16s%-16s\33[0m\n", "Server", "Queries", "State"); - for (i = 0; router->servers[i]; i++) - { - dcb_printf(dcb, "%-16s%-16d%-16s\n", - router->servers[i]->backend_server->unique_name, - router->servers[i]->stats.queries, - SERVER_IS_RUNNING(router->servers[i]->backend_server) ? - "\33[30;42mRUNNING\33[0m" : - "\33[30;41mDOWN\33[0m"); - } - /** Session command statistics */ dcb_printf(dcb, "\n\33[1;4mSession Commands\33[0m\n"); dcb_printf(dcb, "Total number of queries: %d\n", @@ -2515,7 +2339,7 @@ static void clientReply(ROUTER* instance, MXS_DEBUG("schemarouter: Reply from [%s] session [%p]" " mapping [%s] queries queued [%s]", - bref->bref_backend->backend_server->unique_name, + bref->bref_backend->server->unique_name, router_cli_ses->rses_client_dcb->session, router_cli_ses->init & INIT_MAPPING ? "true" : "false", router_cli_ses->queue == NULL ? "none" : @@ -2642,8 +2466,8 @@ static void clientReply(ROUTER* instance, MXS_ERROR("Failed to execute %s in %s:%d. %s %s", cmdstr, - bref->bref_backend->backend_server->name, - bref->bref_backend->backend_server->port, + bref->bref_backend->server->name, + bref->bref_backend->server->port, err, replystr); @@ -2710,8 +2534,8 @@ static void clientReply(ROUTER* instance, MXS_INFO("Backend %s:%d processed reply and starts to execute " "active cursor.", - bref->bref_backend->backend_server->name, - bref->bref_backend->backend_server->port); + bref->bref_backend->server->name, + bref->bref_backend->server->port); execute_sescmd_in_backend(bref); } @@ -2758,44 +2582,41 @@ static void clientReply(ROUTER* instance, /** Compare number of connections from this router in backend servers */ int bref_cmp_router_conn(const void* bref1, const void* bref2) { - BACKEND* b1 = ((backend_ref_t *)bref1)->bref_backend; - BACKEND* b2 = ((backend_ref_t *)bref2)->bref_backend; + SERVER_REF* b1 = ((backend_ref_t *)bref1)->bref_backend; + SERVER_REF* b2 = ((backend_ref_t *)bref2)->bref_backend; - return ((1000 * b1->backend_conn_count) / b1->weight) - - ((1000 * b2->backend_conn_count) / b2->weight); + return ((1000 * b1->connections) / b1->weight) + - ((1000 * b2->connections) / b2->weight); } /** Compare number of global connections in backend servers */ int bref_cmp_global_conn(const void* bref1, const void* bref2) { - BACKEND* b1 = ((backend_ref_t *)bref1)->bref_backend; - BACKEND* b2 = ((backend_ref_t *)bref2)->bref_backend; + SERVER_REF* b1 = ((backend_ref_t *)bref1)->bref_backend; + SERVER_REF* b2 = ((backend_ref_t *)bref2)->bref_backend; - return ((1000 * b1->backend_server->stats.n_current) / b1->weight) - - ((1000 * b2->backend_server->stats.n_current) / b2->weight); + return ((1000 * b1->server->stats.n_current) / b1->weight) + - ((1000 * b2->server->stats.n_current) / b2->weight); } /** Compare replication lag between backend servers */ int bref_cmp_behind_master(const void* bref1, const void* bref2) { - BACKEND* b1 = ((backend_ref_t *)bref1)->bref_backend; - BACKEND* b2 = ((backend_ref_t *)bref2)->bref_backend; + SERVER_REF* b1 = ((backend_ref_t *)bref1)->bref_backend; + SERVER_REF* b2 = ((backend_ref_t *)bref2)->bref_backend; - return ((b1->backend_server->rlag < b2->backend_server->rlag) ? -1 : - ((b1->backend_server->rlag > b2->backend_server->rlag) ? 1 : 0)); + return b1->server->rlag - b2->server->rlag; } /** Compare number of current operations in backend servers */ int bref_cmp_current_load(const void* bref1, const void* bref2) { - SERVER* s1 = ((backend_ref_t *)bref1)->bref_backend->backend_server; - SERVER* s2 = ((backend_ref_t *)bref2)->bref_backend->backend_server; - BACKEND* b1 = ((backend_ref_t *)bref1)->bref_backend; - BACKEND* b2 = ((backend_ref_t *)bref2)->bref_backend; + SERVER_REF* b1 = ((backend_ref_t *)bref1)->bref_backend; + SERVER_REF* b2 = ((backend_ref_t *)bref2)->bref_backend; - return ((1000 * s1->stats.n_current_ops) - b1->weight) - - ((1000 * s2->stats.n_current_ops) - b2->weight); + return ((1000 * b1->server->stats.n_current_ops) - b1->weight) + - ((1000 * b2->server->stats.n_current_ops) - b2->weight); } static void bref_clear_state(backend_ref_t* bref, bref_state_t state) @@ -2824,14 +2645,14 @@ static void bref_clear_state(backend_ref_t* bref, bref_state_t state) else { /** Decrease global operation count */ - prev2 = atomic_add(&bref->bref_backend->backend_server->stats.n_current_ops, -1); + prev2 = atomic_add(&bref->bref_backend->server->stats.n_current_ops, -1); ss_dassert(prev2 > 0); if (prev2 <= 0) { MXS_ERROR("[%s] Error: negative current operation count in backend %s:%u", __FUNCTION__, - bref->bref_backend->backend_server->name, - bref->bref_backend->backend_server->port); + bref->bref_backend->server->name, + bref->bref_backend->server->port); } } } @@ -2861,18 +2682,18 @@ static void bref_set_state(backend_ref_t* bref, bref_state_t state) MXS_ERROR("[%s] Error: negative number of connections waiting " "for results in backend %s:%u", __FUNCTION__, - bref->bref_backend->backend_server->name, - bref->bref_backend->backend_server->port); + bref->bref_backend->server->name, + bref->bref_backend->server->port); } /** Increase global operation count */ - prev2 = atomic_add(&bref->bref_backend->backend_server->stats.n_current_ops, 1); + prev2 = atomic_add(&bref->bref_backend->server->stats.n_current_ops, 1); ss_dassert(prev2 >= 0); if (prev2 < 0) { MXS_ERROR("[%s] Error: negative current operation count in backend %s:%u", __FUNCTION__, - bref->bref_backend->backend_server->name, - bref->bref_backend->backend_server->port); + bref->bref_backend->server->name, + bref->bref_backend->server->port); } } } @@ -2942,14 +2763,14 @@ static bool connect_backend_servers(backend_ref_t* backend_ref, for (i = 0; i < router_nservers; i++) { - BACKEND* b = backend_ref[i].bref_backend; + SERVER_REF* b = backend_ref[i].bref_backend; MXS_INFO("MaxScale connections : %d (%d) in \t%s:%d %s", - b->backend_conn_count, - b->backend_server->stats.n_current, - b->backend_server->name, - b->backend_server->port, - STRSRVSTATUS(b->backend_server)); + b->connections, + b->server->stats.n_current, + b->server->name, + b->server->port, + STRSRVSTATUS(b->server)); } } /*< log only */ /** @@ -2958,9 +2779,9 @@ static bool connect_backend_servers(backend_ref_t* backend_ref, */ for (i = 0; i < router_nservers; i++) { - BACKEND* b = backend_ref[i].bref_backend; + SERVER_REF* b = backend_ref[i].bref_backend; - if (SERVER_IS_RUNNING(b->backend_server)) + if (SERVER_IS_RUNNING(b->server)) { servers_found += 1; @@ -2972,9 +2793,9 @@ static bool connect_backend_servers(backend_ref_t* backend_ref, /** New server connection */ else { - backend_ref[i].bref_dcb = dcb_connect(b->backend_server, + backend_ref[i].bref_dcb = dcb_connect(b->server, session, - b->backend_server->protocol); + b->server->protocol); if (backend_ref[i].bref_dcb != NULL) { @@ -3001,7 +2822,7 @@ static bool connect_backend_servers(backend_ref_t* backend_ref, * But decreased in the calling function * of dcb_close. */ - atomic_add(&b->backend_conn_count, 1); + atomic_add(&b->connections, 1); dcb_add_callback(backend_ref[i].bref_dcb, DCB_REASON_NOT_RESPONDING, @@ -3013,8 +2834,8 @@ static bool connect_backend_servers(backend_ref_t* backend_ref, succp = false; MXS_ERROR("Unable to establish " "connection with slave %s:%d", - b->backend_server->name, - b->backend_server->port); + b->server->name, + b->server->port); /* handle connect error */ break; } @@ -3048,14 +2869,14 @@ static bool connect_backend_servers(backend_ref_t* backend_ref, { for (i = 0; i < router_nservers; i++) { - BACKEND* b = backend_ref[i].bref_backend; + SERVER_REF* b = backend_ref[i].bref_backend; if (BREF_IS_IN_USE((&backend_ref[i]))) { MXS_INFO("Connected %s in \t%s:%d", - STRSRVSTATUS(b->backend_server), - b->backend_server->name, - b->backend_server->port); + STRSRVSTATUS(b->server), + b->server->name, + b->server->port); } } /* for */ } @@ -3438,28 +3259,7 @@ static bool execute_sescmd_in_backend(backend_ref_t* backend_ref) /** Cursor is left active when function returns. */ sescmd_cursor_set_active(scur, true); } -#if defined(SS_DEBUG) - if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO)) - { - tracelog_routed_query(scur->scmd_cur_rses, - "execute_sescmd_in_backend", - backend_ref, - sescmd_cursor_clone_querybuf(scur)); - } - { - GWBUF* tmpbuf = sescmd_cursor_clone_querybuf(scur); - uint8_t* ptr = GWBUF_DATA(tmpbuf); - unsigned char cmd = MYSQL_GET_COMMAND(ptr); - - MXS_DEBUG("%lu [execute_sescmd_in_backend] Just before write, fd " - "%d : cmd %s.", - pthread_self(), - dcb->fd, - STRPACKETTYPE(cmd)); - gwbuf_free(tmpbuf); - } -#endif /*< SS_DEBUG */ switch (scur->scmd_cur_cmd->my_sescmd_packet_type) { case MYSQL_COM_CHANGE_USER: @@ -3568,75 +3368,6 @@ static rses_property_t* mysql_sescmd_get_property(mysql_sescmd_t* scmd) return scmd->my_sescmd_prop; } -static void tracelog_routed_query(ROUTER_CLIENT_SES* rses, - char* funcname, - backend_ref_t* bref, - GWBUF* buf) -{ - uint8_t* packet = GWBUF_DATA(buf); - unsigned char packet_type = packet[4]; - size_t len; - size_t buflen = GWBUF_LENGTH(buf); - char* querystr; - char* startpos = (char *)&packet[5]; - - CHK_BACKEND_REF(bref); - ss_debug(BACKEND *b = bref->bref_backend); - CHK_BACKEND(b); - ss_debug(DCB *dcb = bref->bref_dcb); - CHK_DCB(dcb); - - ss_debug(backend_type_t be_type = BACKEND_TYPE(b)); - - if (GWBUF_IS_TYPE_MYSQL(buf)) - { - len = packet[0]; - len += 256 * packet[1]; - len += 256 * 256 * packet[2]; - - if (packet_type == '\x03') - { - querystr = (char *)MXS_MALLOC(len); - MXS_ABORT_IF_NULL(querystr); - memcpy(querystr, startpos, len - 1); - querystr[len - 1] = '\0'; - MXS_DEBUG("%lu [%s] %d bytes long buf, \"%s\" -> %s:%d %s dcb %p", - pthread_self(), - funcname, - (int)buflen, - querystr, - b->backend_server->name, - b->backend_server->port, - STRBETYPE(be_type), - dcb); - MXS_FREE(querystr); - } - else if (packet_type == '\x22' || - packet_type == 0x22 || - packet_type == '\x26' || - packet_type == 0x26 || - true) - { - querystr = (char *)MXS_MALLOC(len); - MXS_ABORT_IF_NULL(querystr); - memcpy(querystr, startpos, len - 1); - querystr[len - 1] = '\0'; - MXS_DEBUG("%lu [%s] %d bytes long buf, \"%s\" -> %s:%d %s dcb %p", - pthread_self(), - funcname, - (int)buflen, - querystr, - b->backend_server->name, - b->backend_server->port, - STRBETYPE(be_type), - dcb); - MXS_FREE(querystr); - } - } - gwbuf_free(buf); -} - - /** * Return RCAP_TYPE_STMT_INPUT. */ @@ -3700,17 +3431,16 @@ static bool route_session_write(ROUTER_CLIENT_SES* router_cli_ses, if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO)) { MXS_INFO("Route query to %s\t%s:%d%s", - (SERVER_IS_MASTER(backend_ref[i].bref_backend->backend_server) ? + (SERVER_IS_MASTER(backend_ref[i].bref_backend->server) ? "master" : "slave"), - backend_ref[i].bref_backend->backend_server->name, - backend_ref[i].bref_backend->backend_server->port, + backend_ref[i].bref_backend->server->name, + backend_ref[i].bref_backend->server->port, (i + 1 == router_cli_ses->rses_nbackends ? " <" : "")); } if (BREF_IS_IN_USE((&backend_ref[i]))) { rc = dcb->func.write(dcb, gwbuf_clone(querybuf)); - atomic_add(&backend_ref[i].bref_backend->stats.queries, 1); if (rc != 1) { succp = false; @@ -3808,10 +3538,10 @@ static bool route_session_write(ROUTER_CLIENT_SES* router_cli_ses, if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO)) { MXS_INFO("Route query to %s\t%s:%d%s", - (SERVER_IS_MASTER(backend_ref[i].bref_backend->backend_server) ? + (SERVER_IS_MASTER(backend_ref[i].bref_backend->server) ? "master" : "slave"), - backend_ref[i].bref_backend->backend_server->name, - backend_ref[i].bref_backend->backend_server->port, + backend_ref[i].bref_backend->server->name, + backend_ref[i].bref_backend->server->port, (i + 1 == router_cli_ses->rses_nbackends ? " <" : "")); } @@ -3833,8 +3563,8 @@ static bool route_session_write(ROUTER_CLIENT_SES* router_cli_ses, succp = true; MXS_INFO("Backend %s:%d already executing sescmd.", - backend_ref[i].bref_backend->backend_server->name, - backend_ref[i].bref_backend->backend_server->port); + backend_ref[i].bref_backend->server->name, + backend_ref[i].bref_backend->server->port); } else { @@ -3844,12 +3574,8 @@ static bool route_session_write(ROUTER_CLIENT_SES* router_cli_ses, { MXS_ERROR("Failed to execute session " "command in %s:%d", - backend_ref[i].bref_backend->backend_server->name, - backend_ref[i].bref_backend->backend_server->port); - } - else - { - atomic_add(&backend_ref[i].bref_backend->stats.queries, 1); + backend_ref[i].bref_backend->server->name, + backend_ref[i].bref_backend->server->port); } } } @@ -4031,7 +3757,6 @@ static bool handle_error_new_connection(ROUTER_INSTANCE* inst, GWBUF* errmsg) { SESSION* ses; - int router_nservers, i; unsigned char cmd = *((unsigned char*)errmsg->start + 4); backend_ref_t* bref; @@ -4088,13 +3813,12 @@ static bool handle_error_new_connection(ROUTER_INSTANCE* inst, &router_handle_state_switch, (void *)bref); - router_nservers = router_get_servercount(inst); /** * Try to get replacement slave or at least the minimum * number of slave connections for router session. */ succp = connect_backend_servers(rses->rses_backend_ref, - router_nservers, + rses->rses_nbackends, ses, inst); @@ -4109,24 +3833,6 @@ return_succp: return succp; } -/** - * Count the number of servers. - * @param inst Router instance - * @return Number of servers - */ -static int router_get_servercount(ROUTER_INSTANCE* inst) -{ - int router_nservers = 0; - BACKEND** b = inst->servers; - /** count servers */ - while (*(b++) != NULL) - { - router_nservers++; - } - - return router_nservers; -} - /** * Finds out if there is a backend reference pointing at the DCB given as * parameter. @@ -4191,7 +3897,7 @@ static int router_handle_state_switch(DCB* dcb, bref = (backend_ref_t *) data; CHK_BACKEND_REF(bref); - srv = bref->bref_backend->backend_server; + srv = bref->bref_backend->server; if (SERVER_IS_RUNNING(srv)) { @@ -4201,7 +3907,7 @@ static int router_handle_state_switch(DCB* dcb, switch (reason) { case DCB_REASON_NOT_RESPONDING: - atomic_add(&bref->bref_backend->backend_conn_count, -1); + atomic_add(&bref->bref_backend->connections, -1); MXS_INFO("schemarouter: server %s not responding", srv->unique_name); dcb->func.hangup(dcb); break; @@ -4479,7 +4185,7 @@ int inspect_backend_mapping_states(ROUTER_CLIENT_SES *router_cli_ses, { router_cli_ses->rses_backend_ref[i].bref_mapped = true; MXS_DEBUG("schemarouter: Received SHOW DATABASES reply from %s for session %p", - router_cli_ses->rses_backend_ref[i].bref_backend->backend_server->unique_name, + router_cli_ses->rses_backend_ref[i].bref_backend->server->unique_name, router_cli_ses->rses_client_dcb->session); } else if (rc == SHOWDB_PARTIAL_RESPONSE) @@ -4487,7 +4193,7 @@ int inspect_backend_mapping_states(ROUTER_CLIENT_SES *router_cli_ses, bref->map_queue = writebuf; writebuf = NULL; MXS_DEBUG("schemarouter: Received partial SHOW DATABASES reply from %s for session %p", - router_cli_ses->rses_backend_ref[i].bref_backend->backend_server->unique_name, + router_cli_ses->rses_backend_ref[i].bref_backend->server->unique_name, router_cli_ses->rses_client_dcb->session); } else @@ -4540,7 +4246,7 @@ int inspect_backend_mapping_states(ROUTER_CLIENT_SES *router_cli_ses, { mapped = false; MXS_DEBUG("schemarouter: Still waiting for reply to SHOW DATABASES from %s for session %p", - bkrf[i].bref_backend->backend_server->unique_name, + bkrf[i].bref_backend->server->unique_name, router_cli_ses->rses_client_dcb->session); } } diff --git a/server/modules/routing/schemarouter/schemarouter.h b/server/modules/routing/schemarouter/schemarouter.h index 88dd01eda..c3cab3c28 100644 --- a/server/modules/routing/schemarouter/schemarouter.h +++ b/server/modules/routing/schemarouter/schemarouter.h @@ -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 */