diff --git a/Documentation/Changelog.md b/Documentation/Changelog.md index ba6372aaf..f2bc3fbac 100644 --- a/Documentation/Changelog.md +++ b/Documentation/Changelog.md @@ -14,6 +14,7 @@ as JSON objects (beta level functionality). For more details, please refer to: +* [MariaDB MaxScale 2.0.3 Release Notes](Release-Notes/MaxScale-2.0.3-Release-Notes.md) * [MariaDB MaxScale 2.0.2 Release Notes](Release-Notes/MaxScale-2.0.2-Release-Notes.md) * [MariaDB MaxScale 2.0.1 Release Notes](Release-Notes/MaxScale-2.0.1-Release-Notes.md) * [MariaDB MaxScale 2.0.0 Release Notes](Release-Notes/MaxScale-2.0.0-Release-Notes.md) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 545f4c597..6734790cc 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -622,6 +622,10 @@ serverB | 15 | 27% serverC | 10 | 18% serverD | 20 | 36% +_**Note**: If the value of the weighting parameter of an individual server is +zero or the relative weight rounds down to zero, no queries will be routed to +that server as long as a server with a positive weight is available._ + Here is an excerpt from an example configuration with the `serv_weight` parameter used as the weighting parameter. diff --git a/Documentation/Release-Notes/MaxScale-2.0.3-Release-Notes.md b/Documentation/Release-Notes/MaxScale-2.0.3-Release-Notes.md index aaa94a3a7..c3d52ad0a 100644 --- a/Documentation/Release-Notes/MaxScale-2.0.3-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-2.0.3-Release-Notes.md @@ -6,8 +6,9 @@ This document describes the changes in release 2.0.3, when compared to release [2.0.2](MaxScale-2.0.2-Release-Notes.md). If you are upgrading from release 1.4.4, please also read the release -notes of release [2.0.0](./MaxScale-2.0.0-Release-Notes.md) and -release [2.0.1](./MaxScale-2.0.1-Release-Notes.md). +notes of release [2.0.0](./MaxScale-2.0.0-Release-Notes.md), +release [2.0.1](./MaxScale-2.0.1-Release-Notes.md) and +release [2.0.2](./MaxScale-2.0.2-Release-Notes.md). For any problems you encounter, please submit a bug report at [Jira](https://jira.mariadb.org). @@ -22,9 +23,18 @@ support systemd. ## Bug fixes [Here](https://jira.mariadb.org/issues/?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%202.0.3) -is a list of bugs fixed since the release of MaxScale 2.0.1. +is a list of bugs fixed since the release of MaxScale 2.0.2. +* [MXS-1048](https://jira.mariadb.org/browse/MXS-1048): ShemaRouter can't handle backquoted database names +* [MXS-1047](https://jira.mariadb.org/browse/MXS-1047): Batch inserts through Maxscale with C/J stall +* [MXS-1045](https://jira.mariadb.org/browse/MXS-1045): Defunct processes after maxscale have executed script during failover +* [MXS-1044](https://jira.mariadb.org/browse/MXS-1044): Init-Script for SLES 11 displays error messages when called +* [MXS-1043](https://jira.mariadb.org/browse/MXS-1043): Reading last insert id from @@identity variable does not work with maxscale +* [MXS-1033](https://jira.mariadb.org/browse/MXS-1033): maxscale crushes on maxadmin request +* [MXS-1026](https://jira.mariadb.org/browse/MXS-1026): Crash with NullAuth authenticator * [MXS-1009](https://jira.mariadb.org/browse/MXS-1009): maxinfo sigsegv in spinlock_release +* [MXS-964](https://jira.mariadb.org/browse/MXS-964): Fatal: MaxScale 2.0.1 received fatal signal 6 +* [MXS-956](https://jira.mariadb.org/browse/MXS-956): Maxscale crash: Removing DCB 0x7fbf94016760 but was in state DCB_STATE_DISCONNECTED which is not legal for a call to dcb_close ## Known Issues and Limitations @@ -45,4 +55,3 @@ from the version of MaxScale. For instance, the tag of version `X.Y.Z` of MaxSca is `maxscale-X.Y.Z`. The source code is available [here](https://github.com/mariadb-corporation/MaxScale). - diff --git a/README.md b/README.md index 40fadbd18..65a6eb736 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ issues and communicate with the MaxScale community. - Email: maxscale@googlegroups.com - Forum: http://groups.google.com/forum/#!forum/maxscale +We're also on the #maria and #maxscale channels on FreeNode. + Please report all feature requests, improvements and bugs in the [MariaDB Jira](https://jira.mariadb.org/projects/MXS/issues). # Documentation diff --git a/VERSION.cmake b/VERSION.cmake index cb7413725..1679f4800 100644 --- a/VERSION.cmake +++ b/VERSION.cmake @@ -5,7 +5,7 @@ set(MAXSCALE_VERSION_MAJOR "2" CACHE STRING "Major version") set(MAXSCALE_VERSION_MINOR "0" CACHE STRING "Minor version") -set(MAXSCALE_VERSION_PATCH "2" CACHE STRING "Patch version") +set(MAXSCALE_VERSION_PATCH "3" CACHE STRING "Patch version") # This should only be incremented if a package is rebuilt set(MAXSCALE_BUILD_NUMBER 1 CACHE STRING "Release number") diff --git a/cmake/init_scripts.cmake b/cmake/init_scripts.cmake index d85b86163..23ab7b8e5 100644 --- a/cmake/init_scripts.cmake +++ b/cmake/init_scripts.cmake @@ -1,24 +1,29 @@ # Check which init.d script to install find_file(RPM_FNC functions PATHS /etc/rc.d/init.d) find_file(DEB_FNC init-functions PATHS /lib/lsb) +find_file(SLES_FNC SuSE-release PATHS /etc) -if(${RPM_FNC} MATCHES "NOTFOUND" AND ${DEB_FNC} MATCHES "NOTFOUND") - message(FATAL_ERROR "Cannot find required init-functions in /lib/lsb/ or /etc/rc.d/init.d/, please confirm that your system files are OK.") -elseif(${RPM_FNC} MATCHES "NOTFOUND") - set(HAVE_LSB_FUNCTIONS TRUE CACHE BOOL "If init.d script uses /lib/lsb/init-functions instead of /etc/rc.d/init.d/functions.") +if(EXISTS ${RPM_FNC}) + set(USE_RPM TRUE CACHE BOOL "If init.d script uses /lib/lsb/init-functions instead of /etc/rc.d/init.d/functions.") +elseif(EXISTS ${SLES_FNC}) + set(USE_SLES TRUE CACHE BOOL "SLES11 has reduced /lib/lsb/init-functions and needs a special init-script") +elseif(EXISTS ${DEB_FNC}) + set(USE_DEB TRUE CACHE BOOL "If init.d script uses /lib/lsb/init-functions instead of /etc/rc.d/init.d/functions.") else() - set(HAVE_LSB_FUNCTIONS FALSE CACHE BOOL "If init.d script uses /lib/lsb/init-functions instead of /etc/rc.d/init.d/functions.") + message(FATAL_ERROR "Cannot find required init-functions in /lib/lsb/ or /etc/rc.d/init.d/, please confirm that your system files are OK.") +endif() + +if(USE_DEB) + configure_file(${CMAKE_SOURCE_DIR}/etc/ubuntu/init.d/maxscale.in ${CMAKE_BINARY_DIR}/maxscale @ONLY) +elseif(USE_RPM) + configure_file(${CMAKE_SOURCE_DIR}/etc/init.d/maxscale.in ${CMAKE_BINARY_DIR}/maxscale @ONLY) +elseif(USE_SLES) + configure_file(${CMAKE_SOURCE_DIR}/etc/sles11/init.d/maxscale.in ${CMAKE_BINARY_DIR}/maxscale @ONLY) endif() configure_file(${CMAKE_SOURCE_DIR}/etc/maxscale.conf.in ${CMAKE_BINARY_DIR}/maxscale.conf @ONLY) configure_file(${CMAKE_SOURCE_DIR}/etc/maxscale.service.in ${CMAKE_BINARY_DIR}/maxscale.service @ONLY) -if(HAVE_LSB_FUNCTIONS) - configure_file(${CMAKE_SOURCE_DIR}/etc/ubuntu/init.d/maxscale.in ${CMAKE_BINARY_DIR}/maxscale @ONLY) -else() - configure_file(${CMAKE_SOURCE_DIR}/etc/init.d/maxscale.in ${CMAKE_BINARY_DIR}/maxscale @ONLY) -endif() - if(PACKAGE) message(STATUS "maxscale.conf will unpack to: /etc/ld.so.conf.d") message(STATUS "startup scripts will unpack to to: /etc/init.d") diff --git a/etc/sles11/init.d/maxscale.in b/etc/sles11/init.d/maxscale.in new file mode 100755 index 000000000..33e5f3515 --- /dev/null +++ b/etc/sles11/init.d/maxscale.in @@ -0,0 +1,164 @@ +#!/bin/bash +# +# maxscale: The MariaDB Corporation MaxScale database proxy +# +# description: MaxScale provides database specific proxy functionality +# +# processname: maxscale +# +### BEGIN INIT INFO +# Provides: maxscale +# Required-Start: $syslog $local_fs +# Required-Stop: $syslog $local_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: The maxscale database proxy +# Description: MaxScale is a database proxy server that can be used to front end +# database clusters offering different routing, filtering and protocol choices +### END INIT INFO + +############################################# +# MaxScale HOME, PIDFILE, LIB +############################################# + +export MAXSCALE_PIDFILE=@MAXSCALE_VARDIR@/run/maxscale/maxscale.pid +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@/maxscale + +############################### +# LSB Exit codes (non-Status) +############################### +_RETVAL_GENERIC=1 +_RETVAL_NOT_INSTALLED=5 +_RETVAL_NOT_RUNNING=7 + +############################### +# LSB Status action Exit codes +############################### +_RETVAL_STATUS_OK=0 +_RETVAL_STATUS_NOT_RUNNING=3 + +# Sanity checks. +[ -x @CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale ] || exit $_RETVAL_NOT_INSTALLED + +# Source additional command line arguments. +[ -f /etc/default/maxscale ] && . /etc/default/maxscale + +################################# +# stop/start/status related vars +################################# +NAME=maxscale +DAEMON=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale +DAEMON_OPTS="--user=maxscale $MAXSCALE_OPTIONS" + +# Source function library. +. /lib/lsb/init-functions + +# we can rearrange this easily +processname=maxscale +servicename=maxscale + +RETVAL=0 + +start() { + + if [ ! -d @MAXSCALE_VARDIR@/log/maxscale ] + then + mkdir -p @MAXSCALE_VARDIR@/log/maxscale + fi + + if [ ! -d @MAXSCALE_VARDIR@/cache/maxscale ] + then + mkdir -p @MAXSCALE_VARDIR@/cache/maxscale + fi + + if [ ! -d @MAXSCALE_VARDIR@/lib/maxscale ] + then + mkdir -p @MAXSCALE_VARDIR@/lib/maxscale + fi + + if [ ! -d @MAXSCALE_VARDIR@/run/maxscale ] + then + mkdir -p @MAXSCALE_VARDIR@/run/maxscale + fi + + chown -R maxscale:maxscale @MAXSCALE_VARDIR@/log/maxscale + chown -R maxscale:maxscale @MAXSCALE_VARDIR@/lib/maxscale + chown -R maxscale:maxscale @MAXSCALE_VARDIR@/cache/maxscale + chown -R maxscale:maxscale @MAXSCALE_VARDIR@/run/maxscale + chmod 0755 @MAXSCALE_VARDIR@/log/maxscale + chmod 0755 @MAXSCALE_VARDIR@/lib/maxscale + chmod 0755 @MAXSCALE_VARDIR@/cache/maxscale + chmod 0755 @MAXSCALE_VARDIR@/run/maxscale + + ulimit -HSn 65535 + echo -n "Starting MaxScale" + start_daemon -p "$MAXSCALE_PIDFILE" $DAEMON $DAEMON_OPTS 2> /dev/null > /dev/null + + sleep 2 + + checkproc $DAEMON + rc_status -v +} + +stop() { + echo -n "Stopping MaxScale" + + maxscale_wait_stop + rc_status -v +} + +reload() { + echo -n "Reloading MaxScale" + + kill -HUP $(cat "$MAXSCALE_PIDFILE") + rc_status -v +} + +maxscale_wait_stop() { + PIDTMP=$(pidofproc -p "$MAXSCALE_PIDFILE" "$DAEMON") + kill -TERM "${PIDTMP:-}" 2> /dev/null; + if [ -n "${PIDTMP:-}" ] && kill -0 "${PIDTMP:-}" 2> /dev/null; then + local i=0 + while kill -0 "${PIDTMP:-}" 2> /dev/null; do + if [ $i = '60' ]; then + break + STATUS=2 + fi + [ "$VERBOSE" != no ] && echo -n "." + sleep 1 + i=$(($i+1)) + done + return $STATUS + else + return $STATUS + fi +} + +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + # return 0 on success + # return 3 on any error + echo -n "Checking MaxScale" + checkproc $DAEMON + rc_status -v + ;; + restart) + stop + start + ;; + reload) + reload + RETVAL=$? + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|reload}" + ;; +esac +exit $RETVAL diff --git a/etc/upstart/maxscale.conf.in b/etc/upstart/maxscale.conf.in index bbbc89b5f..c8fc44212 100644 --- a/etc/upstart/maxscale.conf.in +++ b/etc/upstart/maxscale.conf.in @@ -12,7 +12,7 @@ respawn # respawn limit 2 5 # Unlimited open files -limit nofile unlimited unlimited +limit nofile 65535 65535 # Make sure /var/run/maxscale exists pre-start exec /usr/bin/install -d -o maxscale -g maxscale @MAXSCALE_VARDIR@/run/maxscale diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 6415c40a2..23d6fb489 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -1305,6 +1305,7 @@ static void handleError(ROUTER *instance, void *router_session, if (!rses_begin_locked_router_action(rses)) { /** Session is already closed */ + problem_dcb->dcb_errhandle_called = true; *succp = false; return; } @@ -1327,15 +1328,17 @@ static void handleError(ROUTER *instance, void *router_session, } session = problem_dcb->session; - bool close_dcb = true; backend_ref_t *bref = get_bref_from_dcb(rses, problem_dcb); - if (session == NULL || rses == NULL) + if (session == NULL) { + MXS_ERROR("Session of DCB %p is NULL, won't close the DCB.", problem_dcb); + ss_dassert(false); *succp = false; } else if (DCB_ROLE_CLIENT_HANDLER == problem_dcb->dcb_role) { + dcb_close(problem_dcb); *succp = false; } else @@ -1383,6 +1386,9 @@ static void handleError(ROUTER *instance, void *router_session, if (bref != NULL) { CHK_BACKEND_REF(bref); + RW_CHK_DCB(bref, problem_dcb); + dcb_close(problem_dcb); + RW_CLOSE_BREF(bref); close_failed_bref(bref, true); } else @@ -1399,21 +1405,13 @@ static void handleError(ROUTER *instance, void *router_session, *succp = handle_error_new_connection(inst, &rses, problem_dcb, errmsgbuf); } - RW_CHK_DCB(bref, problem_dcb); - if (bref) { /** This is a valid DCB for a backend ref */ - - if (!BREF_IS_IN_USE(bref) || bref->bref_dcb != problem_dcb) + if (BREF_IS_IN_USE(bref) && bref->bref_dcb == problem_dcb) { - /** The backend is closed or the reference was replaced */ - dcb_close(problem_dcb); - RW_CLOSE_BREF(bref); - } - else - { - MXS_ERROR("Backend '%s' is still in use and points to the problem DCB. Not closing.", + ss_dassert(false); + MXS_ERROR("Backend '%s' is still in use and points to the problem DCB.", bref->ref->server->unique_name); } } @@ -1425,29 +1423,13 @@ static void handleError(ROUTER *instance, void *router_session, MXS_ERROR("DCB connected to '%s' is not in use by the router " "session, not closing it. DCB is in state '%s'", remote, STRDCBSTATE(problem_dcb->state)); - MXS_ERROR("Backends currently in use:"); - - for (int i = 0; i < rses->rses_nbackends; i++) - { - dcb_state_t state = DCB_STATE_UNDEFINED; - if (BREF_IS_IN_USE(&rses->rses_backend_ref[i])) - { - state = rses->rses_backend_ref[i].bref_dcb->state; - } - - MXS_ERROR("%p: %s - %p", &rses->rses_backend_ref[i], STRDCBSTATE(state), - rses->rses_backend_ref[i].bref_dcb); - } } - - close_dcb = false; break; } case ERRACT_REPLY_CLIENT: { handle_error_reply_client(session, rses, problem_dcb, errmsgbuf); - close_dcb = false; *succp = false; /*< no new backend servers were made available */ break; } @@ -1459,12 +1441,6 @@ static void handleError(ROUTER *instance, void *router_session, } } - if (close_dcb) - { - RW_CHK_DCB(bref, problem_dcb); - dcb_close(problem_dcb); - RW_CLOSE_BREF(bref); - } rses_end_locked_router_action(rses); } @@ -1594,6 +1570,8 @@ static bool handle_error_new_connection(ROUTER_INSTANCE *inst, /** * If bref == NULL it has been replaced already with another one. + * + * NOTE: This can never happen. */ if ((bref = get_bref_from_dcb(myrses, backend_dcb)) == NULL) { @@ -1626,25 +1604,10 @@ static bool handle_error_new_connection(ROUTER_INSTANCE *inst, } } + RW_CHK_DCB(bref, backend_dcb); + dcb_close(backend_dcb); + RW_CLOSE_BREF(bref); close_failed_bref(bref, false); - - /** - * Error handler is already called for this DCB because - * it's not polling anymore. It can be assumed that - * it succeed because rses isn't closed. - */ - if (backend_dcb->state != DCB_STATE_POLLING) - { - return true; - } - /** - * Remove callback because this DCB won't be used - * unless it is reconnected later, and then the callback - * is set again. - */ - dcb_remove_callback(backend_dcb, DCB_REASON_NOT_RESPONDING, - &router_handle_state_switch, (void *)bref); - max_nslaves = rses_get_max_slavecount(myrses, myrses->rses_nbackends); max_slave_rlag = rses_get_max_replication_lag(myrses); /** diff --git a/server/modules/routing/readwritesplit/rwsplit_internal.h b/server/modules/routing/readwritesplit/rwsplit_internal.h index f92e72c79..ac3155a41 100644 --- a/server/modules/routing/readwritesplit/rwsplit_internal.h +++ b/server/modules/routing/readwritesplit/rwsplit_internal.h @@ -31,15 +31,15 @@ MXS_BEGIN_DECLS */ #include -#define RW_CHK_DCB(bref, dcb) \ +#define RW_CHK_DCB(b, d) \ do{ \ - if(dcb->state == DCB_STATE_DISCONNECTED){ \ + if(d->state == DCB_STATE_DISCONNECTED){ \ MXS_NOTICE("DCB was closed on line %d and another attempt to close it is made on line %d." , \ - (bref) ? (bref)->closed_at : -1, __LINE__); \ + (b) ? (b)->closed_at : -1, __LINE__); \ } \ }while (false) -#define RW_CLOSE_BREF(b) do{ if (bref){ bref->closed_at = __LINE__; } } while (false) +#define RW_CLOSE_BREF(b) do{ if (b){ (b)->closed_at = __LINE__; } } while (false) /* * The following are implemented in rwsplit_mysql.c diff --git a/server/modules/routing/readwritesplit/rwsplit_route_stmt.c b/server/modules/routing/readwritesplit/rwsplit_route_stmt.c index 7d4d1de17..88b671344 100644 --- a/server/modules/routing/readwritesplit/rwsplit_route_stmt.c +++ b/server/modules/routing/readwritesplit/rwsplit_route_stmt.c @@ -1175,6 +1175,14 @@ bool handle_master_is_target(ROUTER_INSTANCE *inst, ROUTER_CLIENT_SES *rses, if (rses->rses_config.rw_master_failure_mode == RW_ERROR_ON_WRITE) { succp = send_readonly_error(rses->client_dcb); + + if (rses->rses_master_ref && BREF_IS_IN_USE(rses->rses_master_ref)) + { + close_failed_bref(rses->rses_master_ref, true); + RW_CHK_DCB(rses->rses_master_ref, rses->rses_master_ref->bref_dcb); + dcb_close(rses->rses_master_ref->bref_dcb); + RW_CLOSE_BREF(rses->rses_master_ref); + } } else { diff --git a/server/modules/routing/schemarouter/schemarouter.c b/server/modules/routing/schemarouter/schemarouter.c index 8aa38ec42..d01bb673e 100644 --- a/server/modules/routing/schemarouter/schemarouter.c +++ b/server/modules/routing/schemarouter/schemarouter.c @@ -528,8 +528,9 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, query = modutil_get_SQL(buffer); if ((tmp = strcasestr(query, "from"))) { - char *saved, *tok = strtok_r(tmp, " ;", &saved); - tok = strtok_r(NULL, " ;", &saved); + const char *delim = "` \n\t;"; + char *saved, *tok = strtok_r(tmp, delim, &saved); + tok = strtok_r(NULL, delim, &saved); ss_dassert(tok != NULL); tmp = (char*) hashtable_fetch(ht, tok); diff --git a/server/modules/routing/schemarouter/sharding_common.c b/server/modules/routing/schemarouter/sharding_common.c index 43104a4b1..6ef54130c 100644 --- a/server/modules/routing/schemarouter/sharding_common.c +++ b/server/modules/routing/schemarouter/sharding_common.c @@ -34,16 +34,20 @@ bool extract_database(GWBUF* buf, char* str) /** Copy database name from MySQL packet to session */ if (qc_get_operation(buf) == QUERY_OP_CHANGE_DB) { + const char *delim = "` \n\t;"; + query = modutil_get_SQL(buf); - tok = strtok_r(query, " ;", &saved); + tok = strtok_r(query, delim, &saved); + if (tok == NULL || strcasecmp(tok, "use") != 0) { - MXS_ERROR("extract_database: Malformed change database packet."); + MXS_ERROR("extract_database: Malformed chage database packet."); succp = false; goto retblock; } - tok = strtok_r(NULL, " ;", &saved); + tok = strtok_r(NULL, delim, &saved); + if (tok == NULL) { MXS_ERROR("extract_database: Malformed change database packet."); @@ -51,16 +55,7 @@ bool extract_database(GWBUF* buf, char* str) goto retblock; } - size_t len = strlen(tok); - if (len > MYSQL_DATABASE_MAXLEN) - { - MXS_ERROR("extract_database: Malformed change database packet, " - "too long database name."); - succp = false; - goto retblock; - } - - strcpy(str, tok); + strncpy(str, tok, MYSQL_DATABASE_MAXLEN); } else {