From ea0c4bd61633d82e36c97a0faebdb082d8fcd5de Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 14 Dec 2015 13:45:18 +0200 Subject: [PATCH 01/27] Fixed systemctl commands being called even if it wasn't available On systems where the systemd directories exist but not the systemctl command, installation of MaxScale would cause systemctl to be called. This would print an error when the package is being installed. --- etc/postinst.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/etc/postinst.in b/etc/postinst.in index f37f59f7d..2e25a9730 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -44,7 +44,8 @@ else echo "Could not find ldconfig file: @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.conf" >& 2 fi -if [ -f @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.service ] +# Only copy the scripts if systemd folder and systemctl executable are found +if [ -f @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.service ] && [ -x "$(which systemctl)" ] then if [ -d "/lib/systemd/system" ] then From 0ea22ddaead8f0e3e55589b6f7d589e4e22b1921 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 14 Dec 2015 16:13:29 +0200 Subject: [PATCH 02/27] Change links to Release Notes and Upgrade document. --- Documentation/Documentation-Contents.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index 52d3135a3..fe592c3f3 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -6,7 +6,7 @@ ## About MaxScale - [About MaxScale](About/About-MaxScale.md) - - [MaxScale 1.2.0 Release Notes](Release-Notes/MaxScale-1.2.0-Release-Notes.md) + - [MaxScale 1.3.0 Release Notes](Release-Notes/MaxScale-1.3.0-Release-Notes.md) - [Changelog](Changelog.md) - [Limitations](About/Limitations.md) - [COPYRIGHT](About/COPYRIGHT.md) @@ -20,6 +20,7 @@ ## Upgrading MaxScale +- [Upgrading MaxScale from 1.2 to 1.3](Upgrading/Upgrading-To-MaxScale-1.3.md) - [Upgrading MaxScale from 1.1.1 to 1.2](Upgrading/Upgrading-To-MaxScale-1.2.md) - [Upgrading MaxScale from 1.0.5 to 1.1.0](Upgrading/Upgrading-To-MaxScale-1.1.0.md) @@ -31,7 +32,7 @@ - [How Errors are Handled in MaxScale](Reference/How-errors-are-handled-in-MaxScale.md) - [Debug and Diagnostic Support](Reference/Debug-And-Diagnostic-Support.md) - [Routing Hints](Reference/Hint-Syntax.md) - + ## Tutorials The main tutorial for MaxScale consist of setting up MaxScale for the environment you are using with either a connection-based or a read/write-based configuration. From 3d20beef8c5ac6e17d31321350f6c57652611df0 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 7 Dec 2015 18:27:30 +0200 Subject: [PATCH 03/27] Fixed possible memory leaks in dbfwfilter.c Made some variables stack allocated so there is no change of memory leaking. There was no real reason to allocate memory from the heap for the variables in question since they did not need to persist outside the scope of the function. --- server/modules/filter/dbfwfilter.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/server/modules/filter/dbfwfilter.c b/server/modules/filter/dbfwfilter.c index 2232acd5c..de5280b37 100644 --- a/server/modules/filter/dbfwfilter.c +++ b/server/modules/filter/dbfwfilter.c @@ -989,7 +989,7 @@ bool parse_rule_definition(FW_INSTANCE* instance, RULE* ruledef, char* rule, cha { bool escaped = false; regex_t *re; - char* start, *str; + char* start; tok = strtok_r(NULL, " ", saveptr); char delim = '\''; int n_char = 0; @@ -1042,20 +1042,13 @@ bool parse_rule_definition(FW_INSTANCE* instance, RULE* ruledef, char* rule, cha goto retblock; } - str = calloc(((tok - start) + 1), sizeof(char)); - if (str == NULL) - { - MXS_ERROR("Fatal Error: malloc returned NULL."); - rval = false; - goto retblock; - } + char str[(tok - start) + 1]; re = (regex_t*) malloc(sizeof(regex_t)); if (re == NULL) { MXS_ERROR("Fatal Error: malloc returned NULL."); rval = false; - free(str); goto retblock; } @@ -1073,7 +1066,6 @@ bool parse_rule_definition(FW_INSTANCE* instance, RULE* ruledef, char* rule, cha ruledef->type = RT_REGEX; ruledef->data = (void*) re; } - free(str); } else if (strcmp(tok, "limit_queries") == 0) @@ -1267,11 +1259,8 @@ createInstance(char **options, FILTER_PARAMETER **params) { if (strcmp(params[i]->name, "rules") == 0) { - if (filename) - { - free(filename); - } - filename = strdup(params[i]->value); + filename = params[i]->value; + break; } } @@ -1282,6 +1271,7 @@ createInstance(char **options, FILTER_PARAMETER **params) if (strcmp(options[i], "ignorecase") == 0) { my_instance->regflags |= REG_ICASE; + break; } } } @@ -1300,7 +1290,6 @@ createInstance(char **options, FILTER_PARAMETER **params) MXS_ERROR("Error while opening rule file for firewall filter."); hashtable_free(my_instance->htable); free(my_instance); - free(filename); return NULL; } @@ -1350,16 +1339,15 @@ createInstance(char **options, FILTER_PARAMETER **params) if (file_empty) { MXS_ERROR("dbfwfilter: File is empty: %s", filename); - free(filename); err = true; goto retblock; } fclose(file); - free(filename); /**Apply the rules to users*/ ptr = my_instance->userstrings; + my_instance->userstrings = NULL; if (ptr == NULL) { @@ -2182,6 +2170,10 @@ bool parse_at_times(const char** tok, char** saveptr, RULE* ruledef) { ruledef->active = tr; } + else + { + free(tr); + } return success; } From 78b5777d6ea955ac35ce75337622267c3f0b1790 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 15 Dec 2015 11:19:09 +0200 Subject: [PATCH 04/27] Fixes to Coverity defects Few fixes to possible use of freed memory and resource leaks. --- server/core/config.c | 1 + server/core/dbusers.c | 23 +++++++++++-------- server/core/dcb.c | 6 ++--- server/core/gateway.c | 2 +- server/modules/filter/mqfilter.c | 16 ++++++++++--- server/modules/protocol/httpd.c | 4 ++++ .../modules/routing/maxinfo/maxinfo_parse.c | 4 ++++ .../routing/schemarouter/schemarouter.c | 4 ++-- .../routing/schemarouter/shardrouter.c | 2 +- 9 files changed, 42 insertions(+), 20 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 2c51349f9..75c65007c 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -2528,6 +2528,7 @@ config_get_ifaddr(unsigned char *output) { memcpy(output, ifr.ifr_hwaddr.sa_data, 6); } + close(sock); return success; } diff --git a/server/core/dbusers.c b/server/core/dbusers.c index a76f3f498..f1a67ff4c 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -2343,29 +2343,30 @@ static void * dbusers_keyread(int fd) { MYSQL_USER_HOST *dbkey; - int tmp; if ((dbkey = (MYSQL_USER_HOST *) malloc(sizeof(MYSQL_USER_HOST))) == NULL) { return NULL; } - if (read(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + + int user_size; + if (read(fd, &user_size, sizeof(user_size)) != sizeof(user_size)) { free(dbkey); return NULL; } - if ((dbkey->user = (char *) malloc(tmp + 1)) == NULL) + if ((dbkey->user = (char *) malloc(user_size + 1)) == NULL) { free(dbkey); return NULL; } - if (read(fd, dbkey->user, tmp) != tmp) + if (read(fd, dbkey->user, user_size) != user_size) { free(dbkey->user); free(dbkey); return NULL; } - dbkey->user[tmp] = 0; // NULL Terminate + dbkey->user[user_size] = 0; // NULL Terminate if (read(fd, &dbkey->ipv4, sizeof(dbkey->ipv4)) != sizeof(dbkey->ipv4)) { free(dbkey->user); @@ -2378,28 +2379,30 @@ dbusers_keyread(int fd) free(dbkey); return NULL; } - if (read(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + + int res_size; + if (read(fd, &res_size, sizeof(res_size)) != sizeof(res_size)) { free(dbkey->user); free(dbkey); return NULL; } - if (tmp != -1) + else if (res_size != -1) { - if ((dbkey->resource = (char *) malloc(tmp + 1)) == NULL) + if ((dbkey->resource = (char *) malloc(res_size + 1)) == NULL) { free(dbkey->user); free(dbkey); return NULL; } - if (read(fd, dbkey->resource, tmp) != tmp) + if (read(fd, dbkey->resource, res_size) != res_size) { free(dbkey->resource); free(dbkey->user); free(dbkey); return NULL; } - dbkey->resource[tmp] = 0; // NULL Terminate + dbkey->resource[res_size] = 0; // NULL Terminate } else // NULL is valid, so represent with a length of -1 { diff --git a/server/core/dcb.c b/server/core/dcb.c index a4d63d7e9..322a27163 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -643,6 +643,9 @@ dcb_process_victim_queue(DCB *listofdcb) } else { +#if defined(FAKE_CODE) + conn_open[dcb->fd] = false; +#endif /* FAKE_CODE */ dcb->fd = DCBFD_CLOSED; MXS_DEBUG("%lu [dcb_process_victim_queue] Closed socket " @@ -650,9 +653,6 @@ dcb_process_victim_queue(DCB *listofdcb) pthread_self(), dcb->fd, dcb); -#if defined(FAKE_CODE) - conn_open[dcb->fd] = false; -#endif /* FAKE_CODE */ } } diff --git a/server/core/gateway.c b/server/core/gateway.c index d90b7d48f..5aadda73f 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1024,7 +1024,7 @@ int main(int argc, char **argv) int n_services; int eno = 0; /*< local variable for errno */ int opt; - int daemon_pipe[2]; + int daemon_pipe[2] = {-1, -1}; bool parent_process; int child_status; void** threads = NULL; /*< thread list */ diff --git a/server/modules/filter/mqfilter.c b/server/modules/filter/mqfilter.c index 0ca8c01ef..d46d340c3 100644 --- a/server/modules/filter/mqfilter.c +++ b/server/modules/filter/mqfilter.c @@ -502,7 +502,7 @@ createInstance(char **options, FILTER_PARAMETER **params) MQ_INSTANCE *my_instance; int paramcount = 0, parammax = 64, i = 0, x = 0, arrsize = 0; FILTER_PARAMETER** paramlist; - char** arr; + char** arr = NULL; char taskname[512]; if ((my_instance = calloc(1, sizeof(MQ_INSTANCE)))) @@ -514,6 +514,8 @@ createInstance(char **options, FILTER_PARAMETER **params) if ((my_instance->conn = amqp_new_connection()) == NULL) { + free(paramlist); + free(my_instance); return NULL; } my_instance->channel = 1; @@ -610,6 +612,10 @@ createInstance(char **options, FILTER_PARAMETER **params) if (arrsize > 0) { + for (int x = 0; x < arrsize; x++) + { + free(arr[x]); + } free(arr); } arrsize = 0; @@ -777,7 +783,11 @@ createInstance(char **options, FILTER_PARAMETER **params) snprintf(taskname, 511, "mqtask%d", atomic_add(&hktask_id, 1)); hktask_add(taskname, sendMessage, (void*) my_instance, 5); - + for (int x = 0; x < arrsize; x++) + { + free(arr[x]); + } + free(arr); } return(FILTER *) my_instance; } @@ -834,7 +844,7 @@ void sendMessage(void* data) { MQ_INSTANCE *instance = (MQ_INSTANCE*) data; mqmessage *tmp; - int err_num; + int err_num = AMQP_STATUS_OK; spinlock_acquire(&instance->rconn_lock); if (instance->conn_stat != AMQP_STATUS_OK) diff --git a/server/modules/protocol/httpd.c b/server/modules/protocol/httpd.c index 042a20366..28ebdae10 100644 --- a/server/modules/protocol/httpd.c +++ b/server/modules/protocol/httpd.c @@ -360,6 +360,10 @@ int n_connect = 0; } n_connect++; } + else + { + close(so); + } } } diff --git a/server/modules/routing/maxinfo/maxinfo_parse.c b/server/modules/routing/maxinfo/maxinfo_parse.c index e63d6b2cf..86dacdfa7 100644 --- a/server/modules/routing/maxinfo/maxinfo_parse.c +++ b/server/modules/routing/maxinfo/maxinfo_parse.c @@ -156,6 +156,7 @@ MAXINFO_TREE *col, *table; { /** Unknown token after RESTART MONITOR|SERVICE */ *parse_error = PARSE_SYNTAX_ERROR; + free(text); free_tree(tree); return NULL; } @@ -376,7 +377,10 @@ int i; } if (s1 == s2) + { + *text = NULL; return NULL; + } *text = strndup(s1, s2 - s1); for (i = 0; keywords[i].text; i++) diff --git a/server/modules/routing/schemarouter/schemarouter.c b/server/modules/routing/schemarouter/schemarouter.c index 4e5daaf4a..f5364155c 100644 --- a/server/modules/routing/schemarouter/schemarouter.c +++ b/server/modules/routing/schemarouter/schemarouter.c @@ -1254,7 +1254,7 @@ static void* newSession( if(db[0] != 0x0) { /* Store the database the client is connecting to */ - strncpy(client_rses->connect_db,db,MYSQL_DATABASE_MAXLEN+1); + snprintf(client_rses->connect_db, MYSQL_DATABASE_MAXLEN + 1, "%s", db); } @@ -3797,7 +3797,7 @@ static bool route_session_write( unsigned char packet_type, skygw_query_type_t qtype) { - bool succp; + bool succp = false; rses_property_t* prop; backend_ref_t* backend_ref; int i; diff --git a/server/modules/routing/schemarouter/shardrouter.c b/server/modules/routing/schemarouter/shardrouter.c index b2d65d7d7..8b49c88d6 100644 --- a/server/modules/routing/schemarouter/shardrouter.c +++ b/server/modules/routing/schemarouter/shardrouter.c @@ -1497,7 +1497,7 @@ gen_show_dbs_response(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client) rval = gwbuf_append(rval, last_packet); rval = gwbuf_make_contiguous(rval); - + hashtable_iterator_free(iter); return rval; } From 8d969aadd1e09a3975148b0cae5da8186b0d0b71 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 14 Dec 2015 11:56:34 +0200 Subject: [PATCH 05/27] Changed default number of threads and added `auto` value. Changed default number of threads to 1 instead of autoconfigured value and added a new `auto` variable which enables autoconfiguration of thread count. The number of threads used when autoconfiguratio fails was changed from 4 to 1. The default value of using N threads where N is the number of CPU cores was not optimal as the possibility of rescheduling was higher the more utility threads there were. Due to this, N-1 is deemed to be the better autoconfigured value for thread count. --- .../Getting-Started/Configuration-Guide.md | 21 ++++++------- server/core/config.c | 30 +++++++++++++++---- server/core/gw_utils.c | 4 +-- server/include/maxconfig.h | 1 + server/maxscale_template.cnf | 3 +- 5 files changed, 39 insertions(+), 20 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index d08c5aaaa..fbecf2252 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -69,19 +69,20 @@ The global settings, in a section named `[MaxScale]`, allow various parameters t #### `threads` This parameter controls the number of worker threads that are handling the -events coming from the kernel. MaxScale will auto-detect the number of -processors of the system unless number of threads is manually configured. -It is recommended that you let MaxScale detect how many cores the system -has and leave this parameter undefined. The number of used cores will be -logged into the message logs and if you are not satisfied with the -auto-detected value, you can manually configure it. Increasing the amount -of worker threads beyond the number of processor cores does not improve -the performance, rather is likely to degrade it, and can consume resources -needlessly. +events coming from the kernel. The default is 1 thread. It is recommended that +you start with one thread and increase the number if you require greater +performance. Increasing the amount of worker threads beyond the number of +processor cores does not improve the performance, rather is likely to degrade +it, and can consume resources needlessly. + +You can enable automatic configuration of this value by setting the value to +`auto`. This way MaxScale will detect the number of available processors and +set the amount of threads to be equal to that number. This should only be used +for systems dedicated for running MaxScale. ``` # Valid options are: -# threads= +# threads=[ | auto ] [MaxScale] threads=1 diff --git a/server/core/config.c b/server/core/config.c index 75c65007c..703ebf972 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1578,15 +1578,33 @@ handle_global_item(const char *name, const char *value) int i; if (strcmp(name, "threads") == 0) { - int thrcount = atoi(value); - if (thrcount > 0) + if (strcmp(name, "auto") == 0) { - gateway.n_threads = thrcount; + if ((gateway.n_threads = get_processor_count()) > 1) + { + gateway.n_threads--; + } } else { - MXS_WARNING("Invalid value for 'threads': %s.", value); - return 0; + int thrcount = atoi(value); + if (thrcount > 0) + { + gateway.n_threads = thrcount; + + int processor_count = get_processor_count(); + if (thrcount > processor_count) + { + MXS_WARNING("Number of threads set to %d which is greater than" + " the number of processors available: %d", + thrcount, processor_count); + } + } + else + { + MXS_WARNING("Invalid value for 'threads': %s.", value); + return 0; + } } } else if (strcmp(name, "non_blocking_polls") == 0) @@ -1706,7 +1724,7 @@ global_defaults() { uint8_t mac_addr[6]=""; struct utsname uname_data; - gateway.n_threads = get_processor_count(); + gateway.n_threads = DEFAULT_NTHREADS; gateway.n_nbpoll = DEFAULT_NBPOLLS; gateway.pollsleep = DEFAULT_POLLSLEEP; gateway.auth_conn_timeout = DEFAULT_AUTH_CONNECT_TIMEOUT; diff --git a/server/core/gw_utils.c b/server/core/gw_utils.c index 175586f57..57c562b9b 100644 --- a/server/core/gw_utils.c +++ b/server/core/gw_utils.c @@ -237,8 +237,8 @@ long get_processor_count() #ifdef _SC_NPROCESSORS_ONLN if ((processors = sysconf(_SC_NPROCESSORS_ONLN)) <= 0) { - MXS_WARNING("Unable to establish the number of available cores. Defaulting to 4."); - processors = 4; + MXS_WARNING("Unable to establish the number of available cores. Defaulting to 1."); + processors = 1; } #else #error _SC_NPROCESSORS_ONLN not available. diff --git a/server/include/maxconfig.h b/server/include/maxconfig.h index 89ee2abeb..439157dba 100644 --- a/server/include/maxconfig.h +++ b/server/include/maxconfig.h @@ -41,6 +41,7 @@ #define DEFAULT_POLLSLEEP 1000 /**< Default poll wait time (milliseconds) */ #define _SYSNAME_STR_LENGTH 256 /**< sysname len */ #define _RELEASE_STR_LENGTH 256 /**< release len */ +#define DEFAULT_NTHREADS 1 /**< Default number of polling threads */ /** * Maximum length for configuration parameter value. */ diff --git a/server/maxscale_template.cnf b/server/maxscale_template.cnf index 26e35d8a4..e9b5f94aa 100644 --- a/server/maxscale_template.cnf +++ b/server/maxscale_template.cnf @@ -3,12 +3,11 @@ # Global parameters # -# Number of threads is autodetected, uncomment for manual configuration # Complete list of configuration options: # https://github.com/mariadb-corporation/MaxScale/blob/master/Documentation/Getting-Started/Configuration-Guide.md [maxscale] -#threads=8 +threads=1 # Server definitions # From 4b7cec504ad239d375f4f3e84fc375b0ccf7c63c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 17 Dec 2015 10:23:47 +0200 Subject: [PATCH 06/27] Changed maxscale.service restart values The maxscale service will be restarted only if the process is down because a signal was caught or the process times out. If the process would be restarted when it exits with a non-zero value, this would lead to infinite loops when there is a configuration error. --- etc/maxscale.service.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/maxscale.service.in b/etc/maxscale.service.in index f21de6e7b..0df1c7aba 100644 --- a/etc/maxscale.service.in +++ b/etc/maxscale.service.in @@ -4,7 +4,7 @@ After=network.target [Service] Type=forking -Restart=on-failure +Restart=on-abnormal PIDFile=@MAXSCALE_VARDIR@/run/maxscale/maxscale.pid ExecStartPre=/usr/bin/install -d @MAXSCALE_VARDIR@/run/maxscale -o maxscale -g maxscale ExecStart=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale --user=maxscale From 2b6b8f87dbbcf11f606c6d31f24fc338ef5961fa Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 23 Dec 2015 12:32:45 +0200 Subject: [PATCH 07/27] Fix to MXS-183: Fixed wrong pointer being used for logging error message The wrong pointer was used when logging an error message about a bad parameter. --- server/core/config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 703ebf972..a951e2a47 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1940,7 +1940,7 @@ process_config_update(CONFIG_CONTEXT *context) "count or\n\t%% for specifying the " "maximum percentage of available the " "slaves that will be connected.", - ((SERVICE*)obj->element)->name, + service->name, param->name, param->value); } @@ -1978,7 +1978,7 @@ process_config_update(CONFIG_CONTEXT *context) "for parameter \'%s.%s = %s\'\n\tExpected " "type is for maximum " "slave replication lag.", - ((SERVICE*)obj->element)->name, + service->name, param->name, param->value); } From 0f51f9c714fe269c31d913951c33c3067f3cb93f Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 17 Dec 2015 15:45:16 +0100 Subject: [PATCH 08/27] Changed behaviour for a slave requesting master_log_pos beyond binlog file size Slave request for a log_pos behind binlog file size may result in a disconnection or replication error: if binlog file is latest one slave get disconnected otherwise an error message is returned and replication stops --- server/modules/include/blr.h | 1 + server/modules/routing/binlog/blr_file.c | 24 +++++++++++++++++++++-- server/modules/routing/binlog/blr_slave.c | 21 ++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index 23add0bba..d8f27e675 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -185,6 +185,7 @@ #define SLAVE_POS_READ_ERR 0xff #define SLAVE_POS_READ_UNSAFE 0xfe #define SLAVE_POS_BAD_FD 0xfd +#define SLAVE_POS_BEYOND_EOF 0xfc /** * Some useful macros for examining the MySQL Response packets diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index 0053cd806..535b3f02c 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -432,8 +432,28 @@ struct stat statb; if (pos > filelen) { - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Requested position %lu is beyond end of the binlog file '%s', size %lu", - pos, file->binlogname, filelen); + spinlock_acquire(&router->binlog_lock); + spinlock_acquire(&file->lock); + + if (strcmp(router->binlog_name, file->binlogname) != 0) + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Requested position %lu is beyond " + "'closed' binlog file '%s', size %lu. Generating Error '1236'", + pos, file->binlogname, filelen); + } else { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Requested position %lu is beyond " + "end of the latest binlog file '%s', size %lu. Disconnecting", + pos, file->binlogname, filelen); + + /* Slave will be disconnected by the calling routine */ + hdr->ok = SLAVE_POS_BEYOND_EOF; + } + + } + + spinlock_release(&file->lock); + spinlock_release(&router->binlog_lock); + return NULL; } diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index 8f27f6425..dbfd061e7 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -2034,6 +2034,27 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; read_errmsg); } + if (hdr.ok == SLAVE_POS_BEYOND_EOF) { + MXS_ERROR("%s Slave %s:%i, server-id %d, binlog '%s', %s", + router->service->name, + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, + read_errmsg); + + /* + * Close the slave session and socket + * The slave will try to reconnect + */ + dcb_close(slave->dcb); + +#ifndef BLFILE_IN_SLAVE + blr_close_binlog(router, file); +#endif + return 0; + } + if (hdr.ok == SLAVE_POS_READ_ERR) { MXS_ERROR("%s Slave %s:%i, server-id %d, binlog '%s', %s", router->service->name, From 71212a824b41342557b71f18f5e683f1cae5585d Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 17 Dec 2015 16:25:04 +0100 Subject: [PATCH 09/27] Removed extra brace Removed extra brace --- server/modules/routing/binlog/blr_file.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index 535b3f02c..4cb7eab18 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -445,9 +445,8 @@ struct stat statb; "end of the latest binlog file '%s', size %lu. Disconnecting", pos, file->binlogname, filelen); - /* Slave will be disconnected by the calling routine */ - hdr->ok = SLAVE_POS_BEYOND_EOF; - } + /* Slave will be disconnected by the calling routine */ + hdr->ok = SLAVE_POS_BEYOND_EOF; } From 04f807290a605462d4335ec5f75c85c3303289bb Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Fri, 4 Dec 2015 11:43:00 +0200 Subject: [PATCH 10/27] Remove file from slave The binlog file is now always opened when it is needed and closed when we are finished with it. That will remove any potential file concurrency issues between different threads dealing with the same slave. --- server/modules/include/blr.h | 2 + server/modules/routing/binlog/blr.c | 6 +++ server/modules/routing/binlog/blr_slave.c | 57 ++++++++++++++++++----- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index d8f27e675..58600d259 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -302,7 +302,9 @@ typedef struct router_slave { char binlogfile[BINLOG_FNAMELEN+1]; /*< Current binlog file for this slave */ char *uuid; /*< Slave UUID */ +#ifdef BLFILE_IN_SLAVE BLFILE *file; /*< Currently open binlog file */ +#endif int serverid; /*< Server-id of the slave */ char *hostname; /*< Hostname of the slave, if known */ char *user; /*< Username if given */ diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index 76443e13c..629eb99c9 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -756,7 +756,9 @@ ROUTER_SLAVE *slave; spinlock_init(&slave->catch_lock); slave->dcb = session->client; slave->router = inst; +#ifdef BLFILE_IN_SLAVE slave->file = NULL; +#endif strcpy(slave->binlogfile, "unassigned"); slave->connect_time = time(0); slave->lastEventTimestamp = 0; @@ -901,8 +903,12 @@ ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session; */ slave->state = BLRS_UNREGISTERED; +#if BLFILE_IN_SLAVE + // TODO: Is it really certain the file can be closed here? If other + // TODO: threads are using the slave instance, bag things will happen. [JWi]. if (slave->file) blr_close_binlog(router, slave->file); +#endif /* Unlock */ rses_end_locked_router_action(slave); diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index dbfd061e7..c799d6071 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -103,7 +103,7 @@ static int blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, G int blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large); uint8_t *blr_build_header(GWBUF *pkt, REP_HEADER *hdr); int blr_slave_callback(DCB *dcb, DCB_REASON reason, void *data); -static int blr_slave_fake_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); +static int blr_slave_fake_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, BLFILE** filep); static void blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); static int blr_slave_send_maxscale_version(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); static int blr_slave_send_server_id(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); @@ -1915,10 +1915,18 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; slave->cstate |= CS_BUSY; spinlock_release(&slave->catch_lock); - if (slave->file == NULL) + BLFILE *file; +#ifdef BLFILE_IN_SLAVE + file = slave->file; + slave->file = NULL; +#else + file = NULL; +#endif + + if (file == NULL) { rotating = router->rotating; - if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL) + if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL) { char err_msg[BINLOG_ERROR_MSG_LEN+1]; err_msg[BINLOG_ERROR_MSG_LEN] = '\0'; @@ -1951,8 +1959,12 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; } slave->stats.n_bursts++; +#ifdef BLSLAVE_IN_FILE + slave->file = file; +#endif + while (burst-- && burst_size > 0 && - (record = blr_read_binlog(router, slave->file, slave->binlog_pos, &hdr, read_errmsg)) != NULL) + (record = blr_read_binlog(router, file, slave->binlog_pos, &hdr, read_errmsg)) != NULL) { head = gwbuf_alloc(5); ptr = GWBUF_DATA(head); @@ -1967,13 +1979,17 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; if (hdr.event_type == ROTATE_EVENT) { unsigned long beat1 = hkheartbeat; - blr_close_binlog(router, slave->file); + blr_close_binlog(router, file); if (hkheartbeat - beat1 > 1) MXS_ERROR("blr_close_binlog took %lu maxscale beats", hkheartbeat - beat1); blr_slave_rotate(router, slave, GWBUF_DATA(record)); beat1 = hkheartbeat; +#ifdef BLFILE_IN_SLAVE if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL) +#else + if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL) +#endif { char err_msg[BINLOG_ERROR_MSG_LEN+1]; err_msg[BINLOG_ERROR_MSG_LEN] = '\0'; @@ -2003,6 +2019,9 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; dcb_close(slave->dcb); break; } +#ifdef BLFILE_IN_SLAVE + file = slave->file; +#endif if (hkheartbeat - beat1 > 1) MXS_ERROR("blr_open_binlog took %lu beats", hkheartbeat - beat1); @@ -2076,7 +2095,9 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; blr_send_custom_error(slave->dcb, slave->seqno++, 0, read_errmsg, "HY000", 1236); dcb_close(slave->dcb); - +#ifndef BLFILE_IN_SLAVE + blr_close_binlog(router, file); +#endif return 0; } @@ -2096,6 +2117,9 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; */ dcb_close(slave->dcb); +#ifndef BLFILE_IN_SLAVE + blr_close_binlog(router, file); +#endif return 0; } } @@ -2194,7 +2218,7 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; } else { - if (slave->binlog_pos >= blr_file_size(slave->file) + if (slave->binlog_pos >= blr_file_size(file) && router->rotating == 0 && strcmp(router->binlog_name, slave->binlogfile) != 0 && (blr_master_connected(router) @@ -2219,7 +2243,11 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; slave->binlogfile, (unsigned long)slave->binlog_pos, router->binlog_name, router->binlog_position); - if (blr_slave_fake_rotate(router, slave)) +#ifdef BLFILE_IN_SLAVE + if (blr_slave_fake_rotate(router, slave, &slave->file)) +#else + if (blr_slave_fake_rotate(router, slave, &file)) +#endif { spinlock_acquire(&slave->catch_lock); slave->cstate |= CS_EXPECTCB; @@ -2240,6 +2268,13 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; poll_fake_write_event(slave->dcb); } } + +#ifndef BLFILE_IN_SLAVE + if (file) + { + blr_close_binlog(router, file); + } +#endif return rval; } @@ -2371,7 +2406,7 @@ int len = EXTRACT24(ptr + 9); // Extract the event length * @return Non-zero if the rotate took place */ static int -blr_slave_fake_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) +blr_slave_fake_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, BLFILE** filep) { char *sptr; int filenum; @@ -2383,11 +2418,11 @@ uint32_t chksum; if ((sptr = strrchr(slave->binlogfile, '.')) == NULL) return 0; - blr_close_binlog(router, slave->file); + blr_close_binlog(router, *filep); filenum = atoi(sptr + 1); sprintf(slave->binlogfile, BINLOG_NAMEFMT, router->fileroot, filenum + 1); slave->binlog_pos = 4; - if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL) + if ((*filep = blr_open_binlog(router, slave->binlogfile)) == NULL) return 0; binlognamelen = strlen(slave->binlogfile); From 0cf564aa0d6a9265bb46ba9651b3cd4a818b6568 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 29 Dec 2015 09:57:27 +0200 Subject: [PATCH 11/27] Fix to MXS-508: Fixed username matching in regexfilter The username matching was working as intended but the session's active value was ignored when queries were being routed. This meant that both the username and the IP address of the user were ignored and query replacement was always done. --- server/modules/filter/regexfilter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/filter/regexfilter.c b/server/modules/filter/regexfilter.c index 05ffea41a..5660d6af9 100644 --- a/server/modules/filter/regexfilter.c +++ b/server/modules/filter/regexfilter.c @@ -391,7 +391,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) REGEX_SESSION *my_session = (REGEX_SESSION *) session; char *sql, *newsql; - if (modutil_is_SQL(queue)) + if (my_session->active && modutil_is_SQL(queue)) { if (queue->next != NULL) { From e305a88e0e91dc2b35ff7795d30ca75386ca1231 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 17 Dec 2015 17:55:24 +0200 Subject: [PATCH 12/27] Enabled CPACK_DEBIAN_PACKAGE_SHLIBDEPS again The removal of this from the DEB packages caused no dependencies to be generated. --- cmake/package_deb.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/package_deb.cmake b/cmake/package_deb.cmake index ea07b6451..b5b55c64f 100644 --- a/cmake/package_deb.cmake +++ b/cmake/package_deb.cmake @@ -3,3 +3,4 @@ set(CPACK_GENERATOR "${CPACK_GENERATOR};DEB") set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/postinst;{CMAKE_BINARY_DIR}/postrm") execute_process(COMMAND dpgk --print-architecture OUTPUT_VARIABLE DEB_ARCHITECTURE) set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${DEB_ARCHITECTURE}) +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) From 080a9ebc9db61085372a1177bcb27247651aaf06 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 17 Dec 2015 23:03:22 +0200 Subject: [PATCH 13/27] PCRE2 is now statically linked Since the PCRE2 library was always going to be a part of MaxScale, there was no real reason to have it as a shared library apart from smaller binaries. --- cmake/BuildPCRE2.cmake | 21 +++++++------------ .../test/canonical_tests/CMakeLists.txt | 2 +- server/core/CMakeLists.txt | 8 +++---- server/modules/filter/CMakeLists.txt | 2 +- server/modules/routing/binlog/CMakeLists.txt | 2 +- utils/CMakeLists.txt | 2 +- 6 files changed, 16 insertions(+), 21 deletions(-) diff --git a/cmake/BuildPCRE2.cmake b/cmake/BuildPCRE2.cmake index 823053e56..50ae5bb66 100644 --- a/cmake/BuildPCRE2.cmake +++ b/cmake/BuildPCRE2.cmake @@ -1,23 +1,18 @@ # Build the PCRE2 library from source # -# This will add a 'pcre2' target to CMake which will generate the libpcre2-8.so -# dynamic library and the pcre2.h header. If your target requires PCRE2 you +# This will add a 'pcre2' target to CMake which will generate the libpcre2-8.a +# static library and the pcre2.h header. If your target requires PCRE2 you # need to add a dependeny on the 'pcre2' target by adding add_dependencies( pcre2) -# to the CMakeLists.txt +# to the CMakeLists.txt. You don't need to link against the pcre2 library +# because the static symbols will be in MaxScale. include(ExternalProject) -set(PCRE2_ROOT_DIR ${CMAKE_SOURCE_DIR}/pcre2/) -set(PCRE2_BUILD_DIR ${CMAKE_BINARY_DIR}/pcre2/) -set(PCRE2_LIBRARIES ${CMAKE_BINARY_DIR}/pcre2/libpcre2-8.so - ${CMAKE_BINARY_DIR}/pcre2/libpcre2-8.so.1.0.0 - CACHE STRING "PCRE2 dynamic libraries" FORCE) - -ExternalProject_Add(pcre2 SOURCE_DIR ${PCRE2_ROOT_DIR} - CMAKE_ARGS -DBUILD_SHARED_LIBS=Y -DPCRE2_BUILD_PCRE2GREP=N -DPCRE2_BUILD_TESTS=N - BINARY_DIR ${PCRE2_BUILD_DIR} +ExternalProject_Add(pcre2 SOURCE_DIR ${CMAKE_SOURCE_DIR}/pcre2/ + CMAKE_ARGS -DBUILD_SHARED_LIBS=N -DPCRE2_BUILD_PCRE2GREP=N -DPCRE2_BUILD_TESTS=N + BINARY_DIR ${CMAKE_BINARY_DIR}/pcre2/ BUILD_COMMAND make INSTALL_COMMAND "") include_directories(${CMAKE_BINARY_DIR}/pcre2/) -install(PROGRAMS ${PCRE2_LIBRARIES} DESTINATION ${MAXSCALE_LIBDIR}) +set(PCRE2_LIBRARIES ${CMAKE_BINARY_DIR}/pcre2/libpcre2-8.a CACHE INTERNAL "") diff --git a/query_classifier/test/canonical_tests/CMakeLists.txt b/query_classifier/test/canonical_tests/CMakeLists.txt index 925a3dd4f..a81aa3e88 100644 --- a/query_classifier/test/canonical_tests/CMakeLists.txt +++ b/query_classifier/test/canonical_tests/CMakeLists.txt @@ -8,7 +8,7 @@ else() endif() endif() add_executable(canonizer canonizer.c ${CMAKE_SOURCE_DIR}/server/core/random_jkiss.c) -target_link_libraries(canonizer pthread query_classifier z dl ssl aio crypt crypto rt m ${EMBEDDED_LIB} fullcore stdc++) +target_link_libraries(canonizer ${PCRE2_LIBRARIES} utils pthread query_classifier z dl ssl aio crypt crypto rt m ${EMBEDDED_LIB} fullcore stdc++) add_test(NAME Internal-TestCanonicalQuery COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/canontest.sh ${CMAKE_CURRENT_BINARY_DIR}/test.log ${CMAKE_CURRENT_SOURCE_DIR}/input.sql diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 53004c811..9f29ba741 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -5,7 +5,7 @@ if(BUILD_TESTS OR BUILD_TOOLS) elseif(WITH_TCMALLOC) target_link_libraries(fullcore ${TCMALLOC_LIBRARIES}) endif() - target_link_libraries(fullcore ${CURL_LIBRARIES} utils log_manager pthread ${LZMA_LINK_FLAGS} ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ssl aio rt crypt dl crypto inih z m stdc++) + target_link_libraries(fullcore ${CURL_LIBRARIES} utils ${PCRE2_LIBRARIES} log_manager pthread ${LZMA_LINK_FLAGS} ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ssl aio rt crypt dl crypto inih z m stdc++) add_dependencies(fullcore pcre2) endif() @@ -22,15 +22,15 @@ elseif(WITH_TCMALLOC) target_link_libraries(maxscale ${TCMALLOC_LIBRARIES}) endif() -target_link_libraries(maxscale ${LZMA_LINK_FLAGS} ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ${CURL_LIBRARIES} log_manager utils ssl aio pthread crypt dl crypto inih z rt m stdc++) +target_link_libraries(maxscale ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ${CURL_LIBRARIES} log_manager utils ssl aio pthread crypt dl crypto inih z rt m stdc++) install(TARGETS maxscale DESTINATION ${MAXSCALE_BINDIR}) add_executable(maxkeys maxkeys.c spinlock.c secrets.c utils.c gwdirs.c random_jkiss.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc) -target_link_libraries(maxkeys utils pthread crypt crypto) +target_link_libraries(maxkeys utils pthread crypt crypto ${PCRE2_LIBRARIES}) install(TARGETS maxkeys DESTINATION ${MAXSCALE_BINDIR}) add_executable(maxpasswd maxpasswd.c spinlock.c secrets.c utils.c gwdirs.c random_jkiss.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc) -target_link_libraries(maxpasswd utils pthread crypt crypto) +target_link_libraries(maxpasswd utils pthread crypt crypto ${PCRE2_LIBRARIES}) install(TARGETS maxpasswd DESTINATION ${MAXSCALE_BINDIR}) if(BUILD_TESTS) diff --git a/server/modules/filter/CMakeLists.txt b/server/modules/filter/CMakeLists.txt index e59201f22..a30aecbff 100644 --- a/server/modules/filter/CMakeLists.txt +++ b/server/modules/filter/CMakeLists.txt @@ -11,7 +11,7 @@ if(BUILD_RABBITMQ) endif() add_library(regexfilter SHARED regexfilter.c) -target_link_libraries(regexfilter log_manager ${PCRE2_LIBRARIES}) +target_link_libraries(regexfilter log_manager) add_dependencies(regexfilter pcre2) set_target_properties(regexfilter PROPERTIES VERSION "1.1.0") install(TARGETS regexfilter DESTINATION ${MAXSCALE_LIBDIR}) diff --git a/server/modules/routing/binlog/CMakeLists.txt b/server/modules/routing/binlog/CMakeLists.txt index fad0ecb8e..2b282eadb 100644 --- a/server/modules/routing/binlog/CMakeLists.txt +++ b/server/modules/routing/binlog/CMakeLists.txt @@ -20,7 +20,7 @@ add_executable(maxbinlogcheck maxbinlogcheck.c blr_file.c blr_cache.c blr_master ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc ${CMAKE_SOURCE_DIR}/server/core/externcmd.c) -target_link_libraries(maxbinlogcheck utils ssl pthread ${LZMA_LINK_FLAGS} ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} aio rt crypt dl crypto inih z m stdc++ ${CURL_LIBRARIES}) +target_link_libraries(maxbinlogcheck utils ssl pthread ${LZMA_LINK_FLAGS} ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} aio rt crypt dl crypto inih z m stdc++ ${CURL_LIBRARIES} ${PCRE2_LIBRARIES}) install(TARGETS maxbinlogcheck DESTINATION bin) diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 2b467f34e..8bba56eca 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -1,3 +1,3 @@ add_library(utils skygw_utils.cc ../server/core/atomic.c) -target_link_libraries(utils stdc++ ${PCRE2_LIBRARIES}) +target_link_libraries(utils stdc++) add_dependencies(utils pcre2) From 3c69e641b9667f21ed58a2ef66db6b9ad033cc73 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 30 Dec 2015 05:12:43 +0200 Subject: [PATCH 14/27] Fixed false backend authentication failures It was possible that a backend server was doing authentication while the client closed the session. The more connections the router created the more likely it was. This caused unnecessary reloading of the database users and confusing error messages. With the implemented fix, there are additional checks for the session state before the users are reloaded or error messages are logged. --- server/modules/protocol/mysql_backend.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index 279a87981..94e59f25a 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -353,9 +353,12 @@ static int gw_read_backend_event(DCB *dcb) { } spinlock_release(&dcb->delayqlock); - /* try reload users' table for next connection */ - if (backend_protocol->protocol_auth_state == - MYSQL_AUTH_FAILED) + /* Only reload the users table if authentication failed and the + * client session is not stopping. It is possible that authentication + * fails because the client has closed the connection before all + * backends have done authentication. */ + if (backend_protocol->protocol_auth_state == MYSQL_AUTH_FAILED && + dcb->session->state != SESSION_STATE_STOPPING) { service_refresh_users(dcb->session->service); } @@ -694,12 +697,15 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue) switch (backend_protocol->protocol_auth_state) { case MYSQL_HANDSHAKE_FAILED: case MYSQL_AUTH_FAILED: - MXS_ERROR("Unable to write to backend '%s' due to " - "%s failure. Server in state %s.", - dcb->server->unique_name, - backend_protocol->protocol_auth_state == MYSQL_HANDSHAKE_FAILED ? - "handshake" : "authentication", - STRSRVSTATUS(dcb->server)); + if (dcb->session->state != SESSION_STATE_STOPPING) + { + MXS_ERROR("Unable to write to backend '%s' due to " + "%s failure. Server in state %s.", + dcb->server->unique_name, + backend_protocol->protocol_auth_state == MYSQL_HANDSHAKE_FAILED ? + "handshake" : "authentication", + STRSRVSTATUS(dcb->server)); + } /** Consume query buffer */ while ((queue = gwbuf_consume( queue, From e40944c9b940dc752e6b7c6f9b5e53eda8f6d026 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 30 Dec 2015 11:31:27 +0200 Subject: [PATCH 15/27] Added note about persistent connections Since the persistent connections do not track session state, a note should be added somewhere which states this. --- Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md index 8f645d2c2..a14fb1abd 100644 --- a/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md @@ -24,6 +24,12 @@ using the *Persistent Connection* feature as it may reduce the time it takes from establishing a connection from the client through MaxScale to the backend server. +**NOTE**: The persistent connections do not track session state. This means +that changing the default database or modifying the session state will cause +those changes to be active even for new connections. If you use queries with +implicit databases or use connections with different client settings, you +should take great care when using persistent connections. + Additional information is available in the following document: * [Administration Tutorial](../Tutorials/Administration-Tutorial.md) From cec2c3670d2aacfdb4e5b91bce0677a263416cf2 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 30 Dec 2015 11:35:30 +0200 Subject: [PATCH 16/27] Added a link to the persistent connection tutorial to the configuration guide Also bolded out the note about session state tracking. --- Documentation/Getting-Started/Configuration-Guide.md | 2 ++ Documentation/Tutorials/Administration-Tutorial.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index fbecf2252..b8d836477 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -589,6 +589,8 @@ indicating a number of seconds. A DCB placed in the persistent pool for a server only be reused if the elapsed time since it joined the pool is less than the given value. Otherwise, the DCB will be discarded and the connection closed. +For more information about persistent connections, please read the [Administration Tutorial](../Tutorials/Administration-Tutorial.md). + ### Listener The listener defines a port and protocol pair that is used to listen for connections to a service. A service may have multiple listeners associated with it, either to support multiple protocols or multiple ports. As with other elements of the configuration the section name is the listener name and it can be selected freely. A type parameter is used to identify the section as a listener definition. Address is optional and it allows the user to limit connections to certain interface only. Socket is also optional and used for Unix socket connections. diff --git a/Documentation/Tutorials/Administration-Tutorial.md b/Documentation/Tutorials/Administration-Tutorial.md index 896c004e9..bb05eb68d 100644 --- a/Documentation/Tutorials/Administration-Tutorial.md +++ b/Documentation/Tutorials/Administration-Tutorial.md @@ -125,7 +125,7 @@ than `persistmaxtime` seconds. It was also be discarded if it has been disconne by the back end server. Connections will be selected that match the user name and protocol for the new request. -Please note that because persistent connections have previously been in use, they +**Please note** that because persistent connections have previously been in use, they may give a different environment from a fresh connection. For example, if the previous use of the connection issued "use mydatabase" then this setting will be carried over into the reuse of the same connection. For many applications this will From 94edc847c2de95e14e7d5ceac63754669a32fb4e Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 30 Dec 2015 14:50:03 +0200 Subject: [PATCH 17/27] Updated readconnroute limitations The limitation with readconnroute and LONGBLOB is only when data is being sent from the client to MaxScale. The client can still query for LONGBLOB data without any side-effects. --- Documentation/About/Limitations.md | 2 +- Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/About/Limitations.md b/Documentation/About/Limitations.md index f3df71ff7..fb3a5bf29 100644 --- a/Documentation/About/Limitations.md +++ b/Documentation/About/Limitations.md @@ -18,7 +18,7 @@ The default master selection is based only on MIN(wsrep_local_index). This can b * If Master changes (ie. new Master promotion) during current connection the router cannot check the change -* LONGBLOB is not supported +* Sending of LONGBLOB data is not supported ## Limitations in the Read/Write Splitter diff --git a/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md index a14fb1abd..bbc115a68 100644 --- a/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md @@ -237,7 +237,7 @@ the most serious of this are listed below. * When users have different passwords based on the host from which they connect MaxScale is unable to determine which password it should use to connect to the backend database. This results in failed connections and unusable usernames in MaxScale. -* LONGBLOB are currently not supported. +* The readconnroute module does not support sending of LONGBLOB data. * Galera Cluster variables, such as @@wsrep_node_name, are not resolved by the embedded MariaDB parser. From 309f794992b91c10e5b3cea90726c0e1c9d2efed Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 30 Dec 2015 16:53:57 +0200 Subject: [PATCH 18/27] Added images to RabbitMQ and Tee tutorial Added an image which depicts the use-case for the tutorial. --- .../Tutorials/RabbitMQ-And-Tee-Archiving.md | 2 ++ .../Tutorials/images/rabbit-and-tee.png | Bin 0 -> 23826 bytes 2 files changed, 2 insertions(+) create mode 100644 Documentation/Tutorials/images/rabbit-and-tee.png diff --git a/Documentation/Tutorials/RabbitMQ-And-Tee-Archiving.md b/Documentation/Tutorials/RabbitMQ-And-Tee-Archiving.md index c4a478e37..29ccde3aa 100644 --- a/Documentation/Tutorials/RabbitMQ-And-Tee-Archiving.md +++ b/Documentation/Tutorials/RabbitMQ-And-Tee-Archiving.md @@ -8,6 +8,8 @@ archiving the data and one for actual use, a RabbitMQ server and a MaxScale serv For testing purposes some of these can locate on the same server but for actual use, an HA solution is recommended. +![Data archiving with Mqfilter and Tee filters](images/rabbit-and-tee.png) + The use case for this tutorial is a production system with one main server where all queries are routed and an archive server where only INSERT, UPDATE and DELETE statements are routed. The queries routed to the archive servers are also transformed into a canonical diff --git a/Documentation/Tutorials/images/rabbit-and-tee.png b/Documentation/Tutorials/images/rabbit-and-tee.png new file mode 100644 index 0000000000000000000000000000000000000000..3f5342aca160e998ed4fc7caf42bccea38a7a960 GIT binary patch literal 23826 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 a`p^9Ee_i0?(j`Yg?(lT=b6Mw<&;$Ub1?t-X literal 0 HcmV?d00001 From 901dfd23c0b78739d556e099c28c998e47abde87 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Wed, 30 Dec 2015 16:03:30 +0100 Subject: [PATCH 19/27] Changed burst_size to long instead of unsigned long Changed burst_size to long instead of unsigned long. This way check burst_size > 0 is now effective. Setting "burstsize" option in router_options may be required. i.e.: burstsize=10M --- server/modules/routing/binlog/blr_slave.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index c799d6071..8908672b7 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -1895,7 +1895,7 @@ GWBUF *head, *record; REP_HEADER hdr; int written, rval = 1, burst; int rotating = 0; -unsigned long burst_size; +long burst_size; uint8_t *ptr; char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; From 3abcb52837453eb0d91dd4b6a3bc5f56c5b11d53 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 30 Dec 2015 19:49:41 +0200 Subject: [PATCH 20/27] MXS-502: Server state changes are logged at notify level When MaxScale perceives a state change in one of the servers it will log an message into the log file stating the previous and the current state. This will make it easier to analyze failures in the cluster. --- server/modules/monitor/galeramon.c | 2 +- server/modules/monitor/mmmon.c | 2 +- server/modules/monitor/mysql_mon.c | 2 +- server/modules/monitor/ndbclustermon.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index 192f7e697..24596bc6b 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -611,7 +611,7 @@ monitorMain(void *arg) evtype = mon_get_event_type(ptr); if (isGaleraEvent(evtype)) { - MXS_INFO("Server changed state: %s[%s:%u]: %s", + MXS_NOTICE("Server changed state: %s[%s:%u]: %s", ptr->server->unique_name, ptr->server->name, ptr->server->port, mon_get_event_name(ptr)); diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index 913318c3d..e03fb396f 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -652,7 +652,7 @@ monitorMain(void *arg) evtype = mon_get_event_type(ptr); if (isMySQLEvent(evtype)) { - MXS_INFO("Server changed state: %s[%s:%u]: %s", + MXS_NOTICE("Server changed state: %s[%s:%u]: %s", ptr->server->unique_name, ptr->server->name, ptr->server->port, mon_get_event_name(ptr)); diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 886d6f534..aa5d5b19f 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -946,7 +946,7 @@ monitorMain(void *arg) evtype = mon_get_event_type(ptr); if (isMySQLEvent(evtype)) { - MXS_INFO("Server changed state: %s[%s:%u]: %s", + MXS_NOTICE("Server changed state: %s[%s:%u]: %s", ptr->server->unique_name, ptr->server->name, ptr->server->port, mon_get_event_name(ptr)); diff --git a/server/modules/monitor/ndbclustermon.c b/server/modules/monitor/ndbclustermon.c index 2d0332598..023bf2550 100644 --- a/server/modules/monitor/ndbclustermon.c +++ b/server/modules/monitor/ndbclustermon.c @@ -417,7 +417,7 @@ monitorMain(void *arg) evtype = mon_get_event_type(ptr); if (isNdbEvent(evtype)) { - MXS_INFO("Server changed state: %s[%s:%u]: %s", + MXS_NOTICE("Server changed state: %s[%s:%u]: %s", ptr->server->unique_name, ptr->server->name, ptr->server->port, mon_get_event_name(ptr)); From 05a7f5759b663444698a9dc1e402cccf74a06d93 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 4 Jan 2016 18:30:38 +0200 Subject: [PATCH 21/27] Fixed thread safety issues in schemarouter and shardrouter Fixed strtok being used in a multithreaded context and removed unused code. --- .../routing/schemarouter/schemarouter.c | 47 +------------------ .../routing/schemarouter/shardrouter.c | 42 +---------------- 2 files changed, 4 insertions(+), 85 deletions(-) diff --git a/server/modules/routing/schemarouter/schemarouter.c b/server/modules/routing/schemarouter/schemarouter.c index f5364155c..6173750f3 100644 --- a/server/modules/routing/schemarouter/schemarouter.c +++ b/server/modules/routing/schemarouter/schemarouter.c @@ -569,8 +569,8 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, query = modutil_get_SQL(buffer); if((tmp = strcasestr(query,"from"))) { - char* tok = strtok(tmp, " ;"); - tok = strtok(NULL," ;"); + char *saved, *tok = strtok_r(tmp, " ;", &saved); + tok = strtok_r(NULL, " ;", &saved); ss_dassert(tok != NULL); tmp = (char*) hashtable_fetch(ht, tok); @@ -652,49 +652,6 @@ bool check_shard_status(ROUTER_INSTANCE* router, char* shard) return rval; } -/** - * Turn a string into an array of strings. The last element in the list is a NULL - * pointer. - * @param str String to tokenize - * @return Pointer to an array of strings. - */ -char** tokenize_string(char* str) -{ - char *tok; - char **list = NULL; - int sz = 2, count = 0; - - tok = strtok(str,", "); - - if(tok == NULL) - return NULL; - - list = (char**)malloc(sizeof(char*)*(sz)); - - while(tok) - { - if(count + 1 >= sz) - { - char** tmp = realloc(list,sizeof(char*)*(sz*2)); - if(tmp == NULL) - { - char errbuf[STRERROR_BUFLEN]; - MXS_ERROR("realloc returned NULL: %s.", - strerror_r(errno, errbuf, sizeof(errbuf))); - free(list); - return NULL; - } - list = tmp; - sz *= 2; - } - list[count] = strdup(tok); - count++; - tok = strtok(NULL,", "); - } - list[count] = NULL; - return list; -} - /** * A fake DCB read function used to forward queued queries. * @param dcb Internal DCB used by the router session diff --git a/server/modules/routing/schemarouter/shardrouter.c b/server/modules/routing/schemarouter/shardrouter.c index 8b49c88d6..2ece1549b 100644 --- a/server/modules/routing/schemarouter/shardrouter.c +++ b/server/modules/routing/schemarouter/shardrouter.c @@ -482,8 +482,8 @@ get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF* query = modutil_get_SQL(buffer); if((tmp = strcasestr(query,"from"))) { - char* tok = strtok(tmp, " ;"); - tok = strtok(NULL," ;"); + char *saved, *tok = strtok_r(tmp, " ;", &saved); + tok = strtok_r(NULL, " ;", &saved); ss_dassert(tok != NULL); tmp = (char*) hashtable_fetch(ht, tok); if(tmp) @@ -542,44 +542,6 @@ get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF* return rval; } -char** -tokenize_string(char* str) -{ - char *tok; - char **list = NULL; - int sz = 2, count = 0; - - tok = strtok(str, ", "); - - if(tok == NULL) - return NULL; - - list = (char**) malloc(sizeof(char*)*(sz)); - - while(tok) - { - if(count + 1 >= sz) - { - char** tmp = realloc(list, sizeof(char*)*(sz * 2)); - if(tmp == NULL) - { - char errbuf[STRERROR_BUFLEN]; - MXS_ERROR("realloc returned NULL: %s.", - strerror_r(errno, errbuf, sizeof(errbuf))); - free(list); - return NULL; - } - list = tmp; - sz *= 2; - } - list[count] = strdup(tok); - count++; - tok = strtok(NULL, ", "); - } - list[count] = NULL; - return list; -} - /** * This is the function used to channel replies from a subservice up to the client. * The values passed are set in the newSession function. From 4ef89d213be3b32c84511cd952a0286869aef3ea Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 31 Dec 2015 10:33:53 +0200 Subject: [PATCH 22/27] MXS-463: Filepaths are now properly formatted for printing The various global directory setter functions now process the input they receive and remove redundant and trailing forward slashes from the directory paths. --- server/core/gwdirs.c | 113 ++++++++++++++++++++++++++++-------------- server/core/secrets.c | 5 +- server/core/utils.c | 41 +++++++++++++++ server/include/gw.h | 1 + 4 files changed, 123 insertions(+), 37 deletions(-) diff --git a/server/core/gwdirs.c b/server/core/gwdirs.c index 29699ed11..19b21ad3f 100644 --- a/server/core/gwdirs.c +++ b/server/core/gwdirs.c @@ -17,66 +17,101 @@ */ #include +#include +/** + * Set the configuration file directory + * @param str Path to directory + */ void set_configdir(char* str) { free(configdir); + clean_up_pathname(str); configdir = str; } +/** + * Set the log file directory + * @param str Path to directory + */ void set_logdir(char* str) { free(logdir); + clean_up_pathname(str); logdir = str; } +/** + * Set the language file directory + * @param str Path to directory + */ void set_langdir(char* str) { free(langdir); + clean_up_pathname(str); langdir = str; } +/** + * Set the PID file directory + * @param str Path to directory + */ void set_piddir(char* str) { free(piddir); + clean_up_pathname(str); piddir = str; } +/** + * Set the cache directory + * @param str Path to directory + */ +void set_cachedir(char* param) +{ + free(cachedir); + clean_up_pathname(param); + cachedir = param; +} + +/** + * Set the data directory + * @param str Path to directory + */ +void set_datadir(char* param) +{ + free(maxscaledatadir); + clean_up_pathname(param); + maxscaledatadir = param; +} + +/** + * Set the library directory. Modules will be loaded from here. + * @param str Path to directory + */ +void set_libdir(char* param) +{ + free(libdir); + clean_up_pathname(param); + libdir = param; +} + /** * Get the directory with all the modules. * @return The module directory */ char* get_libdir() { - return libdir ? libdir : (char*)default_libdir; + return libdir ? libdir : (char*) default_libdir; } -void set_libdir(char* param) -{ - if (libdir) - { - free(libdir); - } - - libdir = param; -} /** * Get the service cache directory * @return The path to the cache directory */ char* get_cachedir() { - return cachedir ? cachedir : (char*)default_cachedir; -} - -void set_cachedir(char* param) -{ - if (cachedir) - { - free(cachedir); - } - - cachedir = param; + return cachedir ? cachedir : (char*) default_cachedir; } /** @@ -85,35 +120,41 @@ void set_cachedir(char* param) */ char* get_datadir() { - return maxscaledatadir ? maxscaledatadir : (char*)default_datadir; -} - -void set_datadir(char* param) -{ - if (maxscaledatadir) - { - free(maxscaledatadir); - } - - maxscaledatadir = param; + return maxscaledatadir ? maxscaledatadir : (char*) default_datadir; } +/** + * Get the configuration file directory + * @return The path to the configuration file directory + */ char* get_configdir() { - return configdir ? configdir : (char*)default_configdir; + return configdir ? configdir : (char*) default_configdir; } +/** + * Get the PID file directory which contains maxscale.pid + * @return Path to the PID file directory + */ char* get_piddir() { - return piddir ? piddir : (char*)default_piddir; + return piddir ? piddir : (char*) default_piddir; } +/** + * Return the log file directory + * @return Path to the log file directory + */ char* get_logdir() { - return logdir ? logdir : (char*)default_logdir; + return logdir ? logdir : (char*) default_logdir; } +/** + * Path to the directory which contains the errmsg.sys language file + * @return Path to the language file directory + */ char* get_langdir() { - return langdir ? langdir : (char*)default_langdir; + return langdir ? langdir : (char*) default_langdir; } diff --git a/server/core/secrets.c b/server/core/secrets.c index acc8ab226..1cb9b3c4c 100644 --- a/server/core/secrets.c +++ b/server/core/secrets.c @@ -25,6 +25,8 @@ #include #include +#include "gw.h" + /** * Generate a random printable character * @@ -67,6 +69,7 @@ secrets_readKeys(const char* path) if (path != NULL) { snprintf(secret_file, PATH_MAX, "%s/.secrets", path); + clean_up_pathname(secret_file); } else { @@ -224,7 +227,7 @@ int secrets_writeKeys(const char *path) } snprintf(secret_file, PATH_MAX + 9, "%s/.secrets", path); - secret_file[PATH_MAX + 9] = '\0'; + clean_up_pathname(secret_file); /* Open for writing | Create | Truncate the file for writing */ if ((fd = open(secret_file, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR)) < 0) diff --git a/server/core/utils.c b/server/core/utils.c index b2441e847..f71b5a1e2 100644 --- a/server/core/utils.c +++ b/server/core/utils.c @@ -278,3 +278,44 @@ char *create_hex_sha1_sha1_passwd(char *passwd) return hexpasswd; } + +/** + * Remove duplicate and trailing forward slashes from a path. + * @param path Path to clean up + */ +void clean_up_pathname(char *path) +{ + char *data = path; + size_t len = strlen(path); + + if (len > PATH_MAX) + { + MXS_WARNING("Pathname too long: %s", path); + } + + while (*data != '\0') + { + if (*data == '/') + { + if (*(data + 1) == '/') + { + memmove(data, data + 1, len); + len--; + } + else if (*(data + 1) == '\0' && data != path) + { + *data = '\0'; + } + else + { + data++; + len--; + } + } + else + { + data++; + len--; + } + } +} diff --git a/server/include/gw.h b/server/include/gw.h index 3571968ea..b686709bc 100644 --- a/server/include/gw.h +++ b/server/include/gw.h @@ -89,4 +89,5 @@ int parse_bindconfig(char *, unsigned short, struct sockaddr_in *); int setipaddress(struct in_addr *, char *); char* get_libdir(); long get_processor_count(); +void clean_up_pathname(char *path); #endif From 145abf5a53125a84df14c436102b8a83de1f208c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 31 Dec 2015 15:27:18 +0200 Subject: [PATCH 23/27] MXS-452: Updated readwritesplit documentation The documentation now mentions that weightby works with readwritesplit and links to the configuration guide for more information. --- Documentation/Routers/ReadWriteSplit.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/Routers/ReadWriteSplit.md b/Documentation/Routers/ReadWriteSplit.md index 9392422ef..11f9e7880 100644 --- a/Documentation/Routers/ReadWriteSplit.md +++ b/Documentation/Routers/ReadWriteSplit.md @@ -133,6 +133,14 @@ disable_sescmd_history=true master_accept_reads=true ``` +### `weightby` + +This parameter defines the name of the value which is used to calculate the +weights of the servers. The value should be the name of a parameter in the +server definitions and it should exist in all the servers used by this router. +For more information, see the description of the `weightby` parameter in +the [Configuration Guide](../Getting-Started/Configuration-Guide.md). + ## Routing hints The readwritesplit router supports routing hints. For a detailed guide on hint syntax and functionality, please read [this](../Reference/Hint-Syntax.md) document. From bd95ea2d357cfacdfdd782d78d1918a8da301e72 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 31 Dec 2015 15:45:32 +0200 Subject: [PATCH 24/27] MXS-451: Made documentation clearer about slave_selection_criteria The documentation for readwritesplit now clearly states that only connections from MaxScale are taken into consideration when slaves are being selected. --- Documentation/Routers/ReadWriteSplit.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/Routers/ReadWriteSplit.md b/Documentation/Routers/ReadWriteSplit.md index 11f9e7880..11ace959a 100644 --- a/Documentation/Routers/ReadWriteSplit.md +++ b/Documentation/Routers/ReadWriteSplit.md @@ -99,11 +99,13 @@ router_options=slave_selection_criteria= Where `` is one of the following values. -* `LEAST_GLOBAL_CONNECTIONS`, the slave with least connections in total -* `LEAST_ROUTER_CONNECTIONS`, the slave with least connections from this router +* `LEAST_GLOBAL_CONNECTIONS`, the slave with least connections from MaxScale +* `LEAST_ROUTER_CONNECTIONS`, the slave with least connections from this service * `LEAST_BEHIND_MASTER`, the slave with smallest replication lag * `LEAST_CURRENT_OPERATIONS` (default), the slave with least active operations +The `LEAST_GLOBAL_CONNECTIONS` and `LEAST_ROUTER_CONNECTIONS` use the connections from MaxScale to the server, not the amount of connections reported by the server itself. + ### `max_sescmd_history` **`max_sescmd_history`** sets a limit on how many session commands each session can execute before the session command history is disabled. The default is an unlimited number of session commands. From e0e0d3a5e544c13fd2d8882e9b5cade6181f7730 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Tue, 5 Jan 2016 16:03:08 +0200 Subject: [PATCH 25/27] Add location of source and packages to readme. --- .../Release-Notes/MaxScale-1.3.0-Release-Notes.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md index bbc115a68..48f56ad81 100644 --- a/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md @@ -249,3 +249,13 @@ the most serious of this are listed below. RPM and Debian packages are provided for the Linux distributions supported by MariaDB Enterprise. + +Packages can be downloaded [here](https://mariadb.com/resources/downloads). + +## Source Code + +The source code of MaxScale is tagged at GitHub with a tag, which is identical +with the version of MaxScale. For instance, the tag of version 1.2.1 of MaxScale +is 1.2.1. Further, *master* always refers to the latest released non-beta version. + +The source code is available [here](https://github.com/mariadb-corporation/MaxScale). From 5b91f9806787aa5bcf8806d904772ddcc7e6d31e Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 7 Jan 2016 14:36:52 +0100 Subject: [PATCH 26/27] Removed the 16 chars limitation for binlog file name Removed the 16 chars limitation for binlog file name --- server/modules/include/blr.h | 2 +- server/modules/routing/binlog/blr_master.c | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index 58600d259..148e75006 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -45,7 +45,7 @@ #include #include -#define BINLOG_FNAMELEN 16 +#define BINLOG_FNAMELEN 255 #define BLR_PROTOCOL "MySQLBackend" #define BINLOG_MAGIC { 0xfe, 0x62, 0x69, 0x6e } #define BINLOG_NAMEFMT "%s.%06d" diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index fd13823b4..4a6cd2f39 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -731,7 +731,10 @@ blr_make_binlog_dump(ROUTER_INSTANCE *router) { GWBUF *buf; unsigned char *data; -int len = 0x1b; +int binlog_file_len = strlen(router->binlog_name); +/* COM_BINLOG_DUMP needs 11 bytes + binlogname (terminating NULL is not required) */ +int len = 11 + binlog_file_len; + if ((buf = gwbuf_alloc(len + 4)) == NULL) return NULL; @@ -745,8 +748,8 @@ int len = 0x1b; encode_value(&data[9], 0, 16); // Flags encode_value(&data[11], router->serverid, 32); // Server-id of MaxScale - strncpy((char *)&data[15], router->binlog_name, - BINLOG_FNAMELEN); // binlog filename + memcpy((char *)&data[15], router->binlog_name, + binlog_file_len); // binlog filename return buf; } From 36bd218afde3d50305f93fce9e611a05712d40c3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 7 Jan 2016 07:47:16 +0200 Subject: [PATCH 27/27] Cleaned up documentation Fixed spelling mistakes, cleaned up formatting and added missing links. --- Documentation/Documentation-Contents.md | 13 ++++++++++++- Documentation/Filters/Named-Server-Filter.md | 6 +++--- Documentation/Filters/Query-Log-All-Filter.md | 4 ++++ Documentation/Filters/Regex-Filter.md | 13 +++++++++---- Documentation/Filters/Tee-Filter.md | 6 +++--- Documentation/Filters/Top-N-Filter.md | 6 +++--- .../MaxScale-1.3.0-Release-Notes.md | 5 +++-- Documentation/Routers/Binlogrouter.md | 4 ++-- .../Tutorials/RabbitMQ-And-Tee-Archiving.md | 10 +++++----- ...Replication-Proxy-Binlog-Router-Tutorial.md | 18 +++++++++--------- 10 files changed, 53 insertions(+), 32 deletions(-) diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index fe592c3f3..5e4105f69 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -53,13 +53,24 @@ These tutorials are for specific use cases and module combinations. ## Routers +The routing module is the core of a MaxScale service. The router documentation +contains all module specific configuration options and detailed explanations +of their use. + - [Read Write Split](Routers/ReadWriteSplit.md) - [Read Connection Router](Routers/ReadConnRoute.md) - [Schemarouter](Routers/SchemaRouter.md) + - [Binlogrouter](Routers/Binlogrouter.md) + +There are also two diagnostic routing modules. The CLI is for MaxAdmin and +the Debug CLI client for Telnet. + + - [CLI](Routers/CLI.md) + - [Debug CLI](Routers/Debug-CLI.md) ## Filters -Here are detailed documents about the filters MaxScale offers. They contain configuration guides and example use cases. Before reading these,you should have read the filter tutorial so that you know how they work and how to configure them. +Here are detailed documents about the filters MaxScale offers. They contain configuration guides and example use cases. Before reading these, you should have read the filter tutorial so that you know how they work and how to configure them. - [Query Log All](Filters/Query-Log-All-Filter.md) - [Regex Filter](Filters/Regex-Filter.md) diff --git a/Documentation/Filters/Named-Server-Filter.md b/Documentation/Filters/Named-Server-Filter.md index 3e41092de..62aa77ef8 100644 --- a/Documentation/Filters/Named-Server-Filter.md +++ b/Documentation/Filters/Named-Server-Filter.md @@ -1,10 +1,10 @@ -Named Server Filter +# Named Server Filter -# Overview +## Overview The **namedserverfilter** is a filter module for MaxScale which is able to route queries to servers based on regular expression matches. -# Configuration +## Configuration The configuration block for the Named Server filter requires the minimal filter options in it’s section within the maxscale.cnf file, stored in /etc/maxscale.cnf. diff --git a/Documentation/Filters/Query-Log-All-Filter.md b/Documentation/Filters/Query-Log-All-Filter.md index c06b97f6b..a18466895 100644 --- a/Documentation/Filters/Query-Log-All-Filter.md +++ b/Documentation/Filters/Query-Log-All-Filter.md @@ -107,3 +107,7 @@ filters=ProductsSelectLogger ``` The result of then putting this filter into the service used by the application would be a log file of all select queries that mentioned the table but did not mention the PRODUCT_ID primary key in the predicates for the query. +Executing `SELECT * FROM PRODUCTS` would log the following into `/var/logs/qla/SelectProducts`: +``` +07:12:56.324 7/01/2016, SELECT * FROM PRODUCTS +``` diff --git a/Documentation/Filters/Regex-Filter.md b/Documentation/Filters/Regex-Filter.md index fdd2053b5..ff88d7d7a 100644 --- a/Documentation/Filters/Regex-Filter.md +++ b/Documentation/Filters/Regex-Filter.md @@ -1,6 +1,6 @@ -Regex Filter +# Regex Filter -# Overview +## Overview The regex filter is a filter module for MaxScale that is able to rewrite query content using regular expression matches and text substitution. It uses the PCRE2 syntax which differs from the POSIX regular expressions used in MaxScale versions prior to 1.3.0. @@ -8,7 +8,7 @@ For all details about the PCRE2 syntax, please read the [PCRE2 syntax documentat Please note that the PCRE2 library uses a different syntax to refer to capture groups in the replacement string. The main difference is the usage of the dollar character instead of the backslash character for references e.g. `$1` instead of `\1`. For more details about the replacement string differences, please read the [Creating a new string with substitutions](http://www.pcre.org/current/doc/html/pcre2api.html#SEC34) chapter in the PCRE2 manual. -# Configuration +## Configuration The configuration block for the Regex filter requires the minimal filter options in it’s section within the maxscale.cnf file, stored in /etc/maxscale.cnf. @@ -80,7 +80,12 @@ log_file=/tmp/regexfilter.log ### `log_trace` -The optional log_trace parameter toggles the logging of non-matching and matching queries with their replacements into the trace log file. This is the preferred method of diagnosing the matching of queries since the trace log can be disabled mid-session if such a need rises. +The optional log_trace parameter toggles the logging of non-matching and +matching queries with their replacements into the log file on the *info* level. +This is the preferred method of diagnosing the matching of queries since the +log level can be changed at runtime. For more details about logging levels and +session specific logging, please read the [Configuration Guide](../Getting-Started/Configuration-Guide.md#global-settings) +and the [MaxAdmin](../Reference/MaxAdmin.md#change-maxscale-logging-options) documentation on changing the logging levels. ``` log_trace=true diff --git a/Documentation/Filters/Tee-Filter.md b/Documentation/Filters/Tee-Filter.md index c90fd814f..f8cbc9ef8 100644 --- a/Documentation/Filters/Tee-Filter.md +++ b/Documentation/Filters/Tee-Filter.md @@ -1,10 +1,10 @@ -TEE Filter +# Tee Filter -# Overview +## Overview The tee filter is a filter module for MaxScale is a "plumbing" fitting in the MaxScale filter toolkit. It can be used in a filter pipeline of a service to make a copy of requests from the client and dispatch a copy of the request to another service within MaxScale. -# Configuration +## Configuration The configuration block for the TEE filter requires the minimal filter parameters in it’s section within the maxscale.cnf file, stored in /etc/maxscale.cnf, that defines the filter to load and the service to send the duplicates to. Currently the tee filter does not support multi-statements. diff --git a/Documentation/Filters/Top-N-Filter.md b/Documentation/Filters/Top-N-Filter.md index f20d9cc3c..c1db46055 100644 --- a/Documentation/Filters/Top-N-Filter.md +++ b/Documentation/Filters/Top-N-Filter.md @@ -1,10 +1,10 @@ -Top Filter +# Top Filter -# Overview +## Overview The top filter is a filter module for MaxScale that monitors every SQL statement that passes through the filter. It measures the duration of that statement, the time between the statement being sent and the first result being returned. The top N times are kept, along with the SQL text itself and a list sorted on the execution times of the query is written to a file upon closure of the client session. -# Configuration +## Configuration The configuration block for the TOP filter requires the minimal filter options in it’s section within the maxscale.cnf file, stored in /etc/maxscale.cnf. diff --git a/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md index 48f56ad81..f4e7426db 100644 --- a/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.3.0-Release-Notes.md @@ -31,7 +31,7 @@ implicit databases or use connections with different client settings, you should take great care when using persistent connections. Additional information is available in the following document: -* [Administration Tutorial](../Tutorials/Administration-Tutorial.md) +* [Administration Tutorial](../Tutorials/Administration-Tutorial.md#persistent-connections) ### Binlog Server @@ -59,7 +59,8 @@ definition. Additional information is available in the following documents: * [Binlogrouter Tutorial](../Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md) -* [Upgrading binlogrouter to 1.3.0](../Upgrading/Upgrading-Binlogrouter-to-1.3.0.md) +* [Upgrading Binlogrouter to 1.3](../Upgrading/Upgrading-BinlogRouter-To-Maxscale-1.3.md) +* [Binlogrouter Documentation](../Routers/Binlogrouter.md) ### Logging Changes diff --git a/Documentation/Routers/Binlogrouter.md b/Documentation/Routers/Binlogrouter.md index a1c2b1728..782707fa9 100644 --- a/Documentation/Routers/Binlogrouter.md +++ b/Documentation/Routers/Binlogrouter.md @@ -17,7 +17,7 @@ Binlogrouter is configured with a comma-separated list of key-value pairs. The f ### `binlogdir` This parameter allows the location that MaxScale uses to store binlog files to be set. If this parameter is not set to a directory name then MaxScale will store the binlog files in the directory /var/cache/maxscale/. -In the binlog dir there is also the 'cache' directory that contains data retrieved from the master dureing registration phase and the master.ini file wich contains the configuration of current configured master. +In the binlog dir there is also the 'cache' directory that contains data retrieved from the master during registration phase and the master.ini file which contains the configuration of current configured master. ### `uuid` @@ -87,7 +87,7 @@ The default value is off, set transaction_safety=on to enable the incomplete tra ### `send_slave_heartbeat` -This defines whether (on | off) MaxSclale sends to the slave the heartbeat packet when there are no real binlog events to send. The default value if 'off', no heartbeat event is sent to slave server. If value is 'on' the interval value (requested by the slave during registration) is reported in the diagnostic output and the packect is send after the time interval without any event to send. +This defines whether (on | off) MaxScale sends to the slave the heartbeat packet when there are no real binlog events to send. The default value if 'off', no heartbeat event is sent to slave server. If value is 'on' the interval value (requested by the slave during registration) is reported in the diagnostic output and the packet is send after the time interval without any event to send. A complete example of a service entry for a binlog router service would be as follows. ``` diff --git a/Documentation/Tutorials/RabbitMQ-And-Tee-Archiving.md b/Documentation/Tutorials/RabbitMQ-And-Tee-Archiving.md index 29ccde3aa..07b85db90 100644 --- a/Documentation/Tutorials/RabbitMQ-And-Tee-Archiving.md +++ b/Documentation/Tutorials/RabbitMQ-And-Tee-Archiving.md @@ -32,14 +32,14 @@ in the Creating Database Users section of the [MaxScale Tutorial](MaxScale-Tutor ## Setting up RabbitMQ server -To set up the RabbitMQ server, follow the instructions for your OS onthe [RabbitMQ website](https://www.rabbitmq.com/download.html). +To set up the RabbitMQ server, follow the instructions for your OS on the [RabbitMQ website](https://www.rabbitmq.com/download.html). Useful documentation about access rights can be found on the [Access Control](https://www.rabbitmq.com/access-control.html) page and for UNIX systems the [`rabbitmqctl` manpage](https://www.rabbitmq.com/man/rabbitmqctl.1.man.html) has all the needed commands to manage your installation of RabbitMQ. For this tutorial, we will use a RabbitMQ server installed on a CentOS 7 from the RPM packages. Since CentOS 7 doesn't have the RabbitMQ server in the default -repositores, we will need two extra repositories: The EPEL repository and the Erlang repository. +repositories, we will need two extra repositories: The EPEL repository and the Erlang repository. * [EPEL repositories](https://fedoraproject.org/wiki/EPEL) * [Erlang repositories](https://www.erlang-solutions.com/resources/download.html) @@ -134,7 +134,7 @@ filters=MQ Filter The `filters` parameters for the services refer to the filters we will be creating next. The Production service will use the Tee filter to duplicate INSERT, UPDATE and DELETE statements to the Archive service. The statements passed to the Archive service will -use the MQ Filter to send the canonic versions of the statements to the RabbitMQ broker. +use the MQ Filter to send the canonical versions of the statements to the RabbitMQ broker. The Production service will use the `production-1` server and the Archive service will use the `archive-1` server. Both services user the `maxuser` user with the `maxpwd` password. @@ -158,7 +158,7 @@ The `port` parameter controls which port the listener will listen on and where t connections should be made. The `service` parameter tells which listener belongs to which service. -After the serivces and their listeners are configured we will configure the two filters we'll use. We +After the services and their listeners are configured we will configure the two filters we'll use. We begin with the Tee filter. ``` @@ -337,7 +337,7 @@ Listing queues ... ``` If we create a connection on the Production service on port 4000 and execute -a set of data modifying statemets we should see an equal number of statements +a set of data modifying statements we should see an equal number of statements being sent to the RabbitMQ server: ``` diff --git a/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md b/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md index 674f2fa08..52838a891 100644 --- a/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md +++ b/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md @@ -6,7 +6,7 @@ In a traditional MySQL replication setup a single master server is created and a Introducing a proxy layer between the master server and the slave servers can improve the situation, by reducing the load on the master to simply serving the proxy layer rather than all of the slaves. The slaves only need to be aware of the proxy layer and not of the real master server. Removing need for the slaves to have knowledge of the master, greatly simplifies the process of replacing a failed master within a replication environment. ## MariaDB/MySQL as a Binlog Server -The most obvious solution to the requirement for a proxy layer within a replication environment is to use a MariaDB or MySQL database instance. The database server is designed to allow this, since a slave server is able to be configured such that it will produce binary logs for updates it has itself received via replication from the master server. This is done with the *log_slave_updates* configuration option of the server. In this case the server is known as an intermediate master, it is simultanously a slave to the real master and a master to the other slaves in the configuration. +The most obvious solution to the requirement for a proxy layer within a replication environment is to use a MariaDB or MySQL database instance. The database server is designed to allow this, since a slave server is able to be configured such that it will produce binary logs for updates it has itself received via replication from the master server. This is done with the *log_slave_updates* configuration option of the server. In this case the server is known as an intermediate master, it is simultaneously a slave to the real master and a master to the other slaves in the configuration. Using an intermediate master does not, however, solve all the problems and introduces some new ones, due to the way replication is implemented. A slave server reads the binary log data and creates a relay log from that binary log. This log provides a source of SQL statements, which are executed within the slave in order to make the same changes to the databases on the slaves as were made on the master. If the *log_slave_updates* option has been enabled, new binary log entries are created for the statements executed from the relay log. @@ -60,7 +60,7 @@ The final configuration requirement is the router specific options. The binlog r ### binlogdir This parameter allows the location that MaxScale uses to store binlog files to be set. If this parameter is not set to a directory name then MaxScale will store the binlog files in the directory */var/cache/maxscale/*. -In the binlog dir there is also the 'cache' directory that contains data retrieved from the master during registration phase and the *master.ini* file wich contains the configuration of current configured master. +In the binlog dir there is also the 'cache' directory that contains data retrieved from the master during registration phase and the *master.ini* file which contains the configuration of current configured master. ### uuid @@ -110,7 +110,7 @@ This defines the value of the heartbeat interval in seconds for the connection t ### send_slave_heartbeat -This defines whether (on | off) MaxScale sends to the slave the heartbeat packet when there are no real binlog events to send. The default value if 'off', no heartbeat event is sent to slave server. If value is 'on' the interval value (requested by the slave during registration) is reported in the diagnostic output and the packect is send after the time interval without any event to send. +This defines whether (on | off) MaxScale sends to the slave the heartbeat packet when there are no real binlog events to send. The default value if 'off', no heartbeat event is sent to slave server. If value is 'on' the interval value (requested by the slave during registration) is reported in the diagnostic output and the packet is send after the time interval without any event to send. ### burstsize @@ -262,7 +262,7 @@ Enabling replication from a master server requires: It's possible to specify the desired *MASTER_LOG_FILE* but position must be 4 -The initfile option is nolonger available, filestem option also not available as the stem is automatically set by parsing *MASTER_LOG_FILE*. +The initfile option is no longer available, filestem option also not available as the stem is automatically set by parsing *MASTER_LOG_FILE*. ### Stop/start the replication @@ -287,11 +287,11 @@ Next step is the master configuration MariaDB> CHANGE MASTER TO ... -A succesful configuration change results in *master.ini* being updated. +A successful configuration change results in *master.ini* being updated. Any error is reported in the MySQL and in log files -The upported options are: +The supported options are: MASTER_HOST MASTER_PORT @@ -311,7 +311,7 @@ Examples: MariaDB> CHANGE MASTER TO MASTER_LOG_FILE=‘mysql-bin.000003',MASTER_LOG_POS=8888 This could be applied to current master_host/port or a new one. -If there is a master server maintenance and a slave is beeing promoted as master it should be checked that binlog file and position are valid: in case of any error replication stops and errors are reported via *SHOW SLAVE STATUS* and in error logs. +If there is a master server maintenance and a slave is being promoted as master it should be checked that binlog file and position are valid: in case of any error replication stops and errors are reported via *SHOW SLAVE STATUS* and in error logs. 2) Current binlog file is ‘mysql-bin.000099', position 1234 @@ -319,7 +319,7 @@ If there is a master server maintenance and a slave is beeing promoted as master This could be applied with current master_host/port or a new one If transaction safety option is on and the current binlog file contains an incomplete transaction it will be truncated to the position where transaction started. -In such situation a proper message is reported in MySQL connection and with next START SLAVE binlog file truncation will occurr and MaxScale will request events from the master using the next binlog file at position 4. +In such situation a proper message is reported in MySQL connection and with next START SLAVE binlog file truncation will occur and MaxScale will request events from the master using the next binlog file at position 4. The above scenario might refer to a master crash/failure: the new server that has just been promoted as master doesn't have last transaction events but it should have the new binlog file (the next in sequence). @@ -332,7 +332,7 @@ Check for any error in log files and with MariaDB> SHOW SLAVE STATUS; -In some situations replication state could be *STOPPED* and proper messages are displyed in error logs and in *SHOW SLAVE STATUS*, +In some situations replication state could be *STOPPED* and proper messages are displayed in error logs and in *SHOW SLAVE STATUS*, In order to resolve any mistake done with *CHANGE MASTER TO MASTER_LOG_FILE / MASTER_LOG_POS*, another administrative command would be helpful.