From 970d70700d758e96aa32e1a571ba52a831743ab7 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 7 Dec 2016 08:51:51 +0200 Subject: [PATCH 01/13] Close master references when they lose the master state If the `error_on_write` mode is used when a master loses the master state, the backend would not get closed. This would allow masters that appear back to be used which is not intended. --- .../routing/readwritesplit/readwritesplit.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 7dcf5c867..89629a7b2 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -69,15 +69,15 @@ MODULE_INFO info = * @endverbatim */ -#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) static char *version_str = "V1.1.0"; @@ -2385,6 +2385,14 @@ static bool route_single_stmt(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 { From 0fab454e66ddc41a527778d38077a107baec8008 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 7 Dec 2016 08:53:43 +0200 Subject: [PATCH 02/13] Close the DCB and the related backend at the same time Closing the DCB and the backend reference that uses it at the same time makes the error handling code clearer and removes some of the assumptions that the code made. It will cause the DCB to be closed in multiple places but the logic of why a DCB is being closed is more visible from the code. This change should remove all cases where a DCB is closed without a tightly coupled backend reference. --- .../routing/readwritesplit/readwritesplit.c | 68 +++++-------------- 1 file changed, 16 insertions(+), 52 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 89629a7b2..0320b60dd 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -4473,15 +4473,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 @@ -4529,6 +4531,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 @@ -4545,21 +4550,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->bref_backend->backend_server->unique_name); } } @@ -4571,29 +4568,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; } @@ -4605,12 +4586,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); } @@ -4686,9 +4661,12 @@ 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) { + ss_dassert(false); succp = true; goto return_succp; } @@ -4705,25 +4683,11 @@ static bool handle_error_new_connection(ROUTER_INSTANCE *inst, client_dcb->func.write(client_dcb, gwbuf_clone(errmsg)); } + 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) - { - succp = true; - goto return_succp; - } - /** - * 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); router_nservers = router_get_servercount(inst); max_nslaves = rses_get_max_slavecount(myrses, router_nservers); max_slave_rlag = rses_get_max_replication_lag(myrses); From d4d40c0b9bd7115571eb4e5326f075dc4b7d8264 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 9 Dec 2016 11:13:27 +0200 Subject: [PATCH 03/13] MXS-1047: Backport to 2.0 Backported commits 0e50ecb525003b87e8199708008e4a18606c5e54 and 570e12942ba3d2363ec7098dcf1d35c6b68667a1 to 2.0. --- server/modules/protocol/mysql_backend.c | 64 +++++++++++-------- .../routing/readwritesplit/readwritesplit.c | 20 ++---- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index aac99511d..23bf2a2b4 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -79,7 +79,7 @@ static int backend_write_delayqueue(DCB *dcb); static void backend_set_delayqueue(DCB *dcb, GWBUF *queue); static int gw_change_user(DCB *backend_dcb, SERVER *server, SESSION *in_session, GWBUF *queue); static char *gw_backend_default_auth(); -static GWBUF* process_response_data(DCB* dcb, GWBUF* readbuf, int nbytes_to_process); +static GWBUF* process_response_data(DCB* dcb, GWBUF** readbuf, int nbytes_to_process); extern char* create_auth_failed_msg(GWBUF* readbuf, char* hostaddr, uint8_t* sha1); static bool sescmd_response_complete(DCB* dcb); static int gw_read_reply_or_error(DCB *dcb, MYSQL_session local_session); @@ -1091,24 +1091,31 @@ gw_read_and_write(DCB *dcb, MYSQL_session local_session) } } + do + { + GWBUF *stmt = NULL; /** * If protocol has session command set, concatenate whole * response into one buffer. */ - if (protocol_get_srv_command((MySQLProtocol *) dcb->protocol, false) != MYSQL_COM_UNDEFINED) + if (protocol_get_srv_command((MySQLProtocol *)dcb->protocol, false) != MYSQL_COM_UNDEFINED) { - read_buffer = process_response_data(dcb, read_buffer, gwbuf_length(read_buffer)); + stmt = process_response_data(dcb, &read_buffer, gwbuf_length(read_buffer)); /** * Received incomplete response to session command. * Store it to readqueue and return. */ if (!sescmd_response_complete(dcb)) { + stmt = gwbuf_append(stmt, read_buffer); + spinlock_acquire(&dcb->authlock); + dcb->dcb_readqueue = gwbuf_append(stmt, dcb->dcb_readqueue); + spinlock_release(&dcb->authlock); return_code = 0; goto return_rc; } - if (!read_buffer) + if (!stmt) { MXS_NOTICE("%lu [gw_read_backend_event] " "Read buffer unexpectedly null, even though response " @@ -1119,6 +1126,12 @@ gw_read_and_write(DCB *dcb, MYSQL_session local_session) goto return_rc; } } + else + { + stmt = read_buffer; + read_buffer = NULL; + } + /** * Check that session is operable, and that client DCB is * still listening the socket for replies. @@ -1127,7 +1140,7 @@ gw_read_and_write(DCB *dcb, MYSQL_session local_session) dcb->session->client_dcb != NULL && dcb->session->client_dcb->state == DCB_STATE_POLLING && (session->router_session || - session->service->router->getCapabilities() & (int)RCAP_TYPE_NO_RSESSION)) + session->service->router->getCapabilities() & (int)RCAP_TYPE_NO_RSESSION)) { MySQLProtocol *client_protocol = (MySQLProtocol *)dcb->session->client_dcb->protocol; if (client_protocol != NULL) @@ -1136,30 +1149,29 @@ gw_read_and_write(DCB *dcb, MYSQL_session local_session) if (client_protocol->protocol_auth_state == MYSQL_IDLE) { - gwbuf_set_type(read_buffer, GWBUF_TYPE_MYSQL); + gwbuf_set_type(stmt, GWBUF_TYPE_MYSQL); - session->service->router->clientReply( - session->service->router_instance, - session->router_session, - read_buffer, - dcb); + session->service->router->clientReply(session->service->router_instance, + session->router_session, + stmt, dcb); return_code = 1; } } else if (dcb->session->client_dcb->dcb_role == DCB_ROLE_INTERNAL) { - gwbuf_set_type(read_buffer, GWBUF_TYPE_MYSQL); - session->service->router->clientReply( - session->service->router_instance, - session->router_session, - read_buffer, dcb); + gwbuf_set_type(stmt, GWBUF_TYPE_MYSQL); + session->service->router->clientReply(session->service->router_instance, + session->router_session, + stmt, dcb); return_code = 1; } } else /*< session is closing; replying to client isn't possible */ { - gwbuf_free(read_buffer); + gwbuf_free(stmt); } + } + while (read_buffer); return_rc: return return_code; @@ -1928,7 +1940,7 @@ retblock: * @return GWBUF which includes complete MySQL packet */ static GWBUF* process_response_data(DCB* dcb, - GWBUF* readbuf, + GWBUF** readbuf, int nbytes_to_process) { int npackets_left = 0; /*< response's packet count */ @@ -1946,7 +1958,7 @@ static GWBUF* process_response_data(DCB* dcb, } /** All buffers processed here are sescmd responses */ - gwbuf_set_type(readbuf, GWBUF_TYPE_SESCMD_RESPONSE); + gwbuf_set_type(*readbuf, GWBUF_TYPE_SESCMD_RESPONSE); /** * Now it is known how many packets there should be and how much @@ -1980,7 +1992,7 @@ static GWBUF* process_response_data(DCB* dcb, * packet content. Fails if read buffer doesn't include * enough data to read the packet length. */ - init_response_status(readbuf, srvcmd, &npackets_left, &nbytes_left); + init_response_status(*readbuf, srvcmd, &npackets_left, &nbytes_left); } initial_packets = npackets_left; @@ -1996,7 +2008,7 @@ static GWBUF* process_response_data(DCB* dcb, if (nbytes_to_process >= 5) { /** discard source buffer */ - readbuf = gwbuf_consume(readbuf, GWBUF_LENGTH(readbuf)); + *readbuf = gwbuf_consume(*readbuf, GWBUF_LENGTH(*readbuf)); nbytes_left -= nbytes_to_process; } nbytes_to_process = 0; @@ -2008,8 +2020,8 @@ static GWBUF* process_response_data(DCB* dcb, nbytes_to_process = 0; ss_dassert(npackets_left > 0); npackets_left -= 1; - outbuf = gwbuf_append(outbuf, readbuf); - readbuf = NULL; + outbuf = gwbuf_append(outbuf, *readbuf); + *readbuf = NULL; } /** * Buffer contains more data than we need. Split the complete packet and @@ -2020,7 +2032,7 @@ static GWBUF* process_response_data(DCB* dcb, ss_dassert(nbytes_left < nbytes_to_process); ss_dassert(nbytes_left > 0); ss_dassert(npackets_left > 0); - outbuf = gwbuf_append(outbuf, gwbuf_split(&readbuf, nbytes_left)); + outbuf = gwbuf_append(outbuf, gwbuf_split(readbuf, nbytes_left)); nbytes_to_process -= nbytes_left; npackets_left -= 1; nbytes_left = 0; @@ -2056,7 +2068,7 @@ static GWBUF* process_response_data(DCB* dcb, * three bytes left. If there is less than three * bytes in the buffer or it is NULL, we need to wait for more data from the backend server.*/ - if (readbuf == NULL || gwbuf_length(readbuf) < 3) + if (*readbuf == NULL || gwbuf_length(*readbuf) < 3) { MXS_DEBUG("%lu [%s] Read %d packets. Waiting for %d more " "packets for a total of %d packets.", @@ -2073,7 +2085,7 @@ static GWBUF* process_response_data(DCB* dcb, return NULL; } uint8_t packet_len[3]; - gwbuf_copy_data(readbuf, 0, 3, packet_len); + gwbuf_copy_data(*readbuf, 0, 3, packet_len); nbytes_left = gw_mysql_get_byte3(packet_len) + MYSQL_HEADER_LEN; /** Store new status to protocol structure */ protocol_set_response_status(p, npackets_left, nbytes_left); diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 0320b60dd..353c40b4e 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -2421,23 +2421,11 @@ static bool route_single_stmt(ROUTER_INSTANCE *inst, ROUTER_CLIENT_SES *rses, bref->bref_backend->backend_server->port); /** * Store current stmt if execution of previous session command - * haven't completed yet. - * - * !!! Note that according to MySQL protocol - * there can only be one such non-sescmd stmt at the time. - * It is possible that bref->bref_pending_cmd includes a pending - * command if rwsplit is parent or child for another router, - * which runs all the same commands. - * - * If the assertion below traps, pending queries are treated - * somehow wrong, or client is sending more queries before - * previous is received. + * hasn't completed yet. */ - if (sescmd_cursor_is_active(scur)) + if (sescmd_cursor_is_active(scur) && bref != rses->rses_master_ref) { - ss_dassert(bref->bref_pending_cmd == NULL); - bref->bref_pending_cmd = gwbuf_clone(querybuf); - + bref->bref_pending_cmd = gwbuf_append(bref->bref_pending_cmd, gwbuf_clone(querybuf)); rses_end_locked_router_action(rses); goto retblock; } @@ -4247,7 +4235,7 @@ static bool route_session_write(ROUTER_CLIENT_SES *router_cli_ses, * Otherwise, cursor will execute pending commands * when it completes with previous commands. */ - if (sescmd_cursor_is_active(scur)) + if (sescmd_cursor_is_active(scur) && &backend_ref[i] != router_cli_ses->rses_master_ref) { nsucc += 1; MXS_INFO("Backend %s:%d already executing sescmd.", From 15a8675fca53da3417b8c0155e43d91e1173f208 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 11 Dec 2016 21:41:38 +0200 Subject: [PATCH 04/13] Mark error handler as called for closed sessions When a DCB error occurs, the handleError entry point of the routers is called. The caller of this entry point expects that the error handler marks the DCB as handled. The aforementioned behavior is wrong as the error handler should not keep track of whether the handler was already called. --- server/modules/routing/readwritesplit/readwritesplit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 353c40b4e..dc67558a0 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -4439,6 +4439,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; } From ea8199054251b877f6663ac93d079433fdf89c83 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 12 Dec 2016 10:30:57 +0200 Subject: [PATCH 05/13] Update Change Log --- Documentation/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/Changelog.md b/Documentation/Changelog.md index e7818829e..5cd7b8fa2 100644 --- a/Documentation/Changelog.md +++ b/Documentation/Changelog.md @@ -11,6 +11,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) From 3872cebd0934b2fd4777161a818a6b9e954908fe Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 13 Dec 2016 13:51:46 +0200 Subject: [PATCH 06/13] MXS-1048: Handle backquoted database names The schemarouter didn't expect backquoted database names for SHOW TABLE and USE statements. --- .../routing/schemarouter/schemarouter.c | 5 +- .../routing/schemarouter/sharding_common.c | 48 ++++++++++--------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/server/modules/routing/schemarouter/schemarouter.c b/server/modules/routing/schemarouter/schemarouter.c index 29f17d5c3..4126958f7 100644 --- a/server/modules/routing/schemarouter/schemarouter.c +++ b/server/modules/routing/schemarouter/schemarouter.c @@ -531,8 +531,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 0496e4e2b..456e2ca53 100644 --- a/server/modules/routing/schemarouter/sharding_common.c +++ b/server/modules/routing/schemarouter/sharding_common.c @@ -22,8 +22,8 @@ */ bool extract_database(GWBUF* buf, char* str) { - uint8_t* packet; - char *saved,*tok,*query = NULL; + uint8_t* packet; + char *saved, *tok, *query = NULL; bool succp = true; unsigned int plen; @@ -31,33 +31,37 @@ bool extract_database(GWBUF* buf, char* str) plen = gw_mysql_get_byte3(packet) - 1; /** Copy database name from MySQL packet to session */ - if(qc_get_operation(buf) == QUERY_OP_CHANGE_DB) + if (qc_get_operation(buf) == QUERY_OP_CHANGE_DB) { - query = modutil_get_SQL(buf); - tok = strtok_r(query," ;",&saved); - if(tok == NULL || strcasecmp(tok,"use") != 0) - { - MXS_ERROR("extract_database: Malformed chage database packet."); - succp = false; - goto retblock; - } + const char *delim = "` \n\t;"; - tok = strtok_r(NULL," ;",&saved); - if(tok == NULL) - { - MXS_ERROR("extract_database: Malformed chage database packet."); - succp = false; - goto retblock; - } + query = modutil_get_SQL(buf); + tok = strtok_r(query, delim, &saved); - strncpy(str,tok,MYSQL_DATABASE_MAXLEN); + if (tok == NULL || strcasecmp(tok, "use") != 0) + { + MXS_ERROR("extract_database: Malformed chage database packet."); + succp = false; + goto retblock; + } + + tok = strtok_r(NULL, delim, &saved); + + if (tok == NULL) + { + MXS_ERROR("extract_database: Malformed change database packet."); + succp = false; + goto retblock; + } + + strncpy(str, tok, MYSQL_DATABASE_MAXLEN); } else { - memcpy(str,packet + 5,plen); - memset(str + plen,0,1); + memcpy(str, packet + 5, plen); + memset(str + plen, 0, 1); } - retblock: +retblock: free(query); return succp; } From 5c5b2594b5868329ad6a56b3269683d3e9193bcc Mon Sep 17 00:00:00 2001 From: Bernd Wolber Date: Fri, 2 Dec 2016 11:20:10 +0100 Subject: [PATCH 07/13] MXS-1044: Added init-script for SLES11 --- CMakeLists.txt | 2 + cmake/macros.cmake | 8 +- etc/sles11/init.d/maxscale.in | 164 ++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 1 deletion(-) create mode 100755 etc/sles11/init.d/maxscale.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 8945e1952..1ebae7025 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -278,6 +278,8 @@ if(WITH_SCRIPTS) configure_file(${CMAKE_SOURCE_DIR}/etc/maxscale.service.in ${CMAKE_BINARY_DIR}/maxscale.service @ONLY) if(DEB_BASED) configure_file(${CMAKE_SOURCE_DIR}/etc/ubuntu/init.d/maxscale.in ${CMAKE_BINARY_DIR}/maxscale @ONLY) + elseif(SLES_BASED) + configure_file(${CMAKE_SOURCE_DIR}/etc/sles/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() diff --git a/cmake/macros.cmake b/cmake/macros.cmake index 4c1a6efbe..d2d2d99ec 100644 --- a/cmake/macros.cmake +++ b/cmake/macros.cmake @@ -65,13 +65,19 @@ macro(check_dirs) if(${DEB_FNC} MATCHES "DEB_FNC-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.") else() - set(DEB_BASED TRUE CACHE BOOL "If init.d script uses /lib/lsb/init-functions instead of /etc/rc.d/init.d/functions.") + find_file(SLES_FNC SuSE-release PATHS /etc) + if(${SLES_FNC} MATCHES "SLES_FNC-NOTFOUND") + set(DEB_BASED TRUE CACHE BOOL "If init.d script uses /lib/lsb/init-functions instead of /etc/rc.d/init.d/functions.") + else() + set(SLES_BASED TRUE CACHE BOOL "SLES11 has reduced /lib/lsb/init-functions and needs a special init-script") + endif() endif() else() set(DEB_BASED FALSE CACHE BOOL "If init.d script uses /lib/lsb/init-functions instead of /etc/rc.d/init.d/functions.") endif() unset(DEB_FNC) unset(RPM_FNC) + unset(SLES_FNC) endif() #Check RabbitMQ headers and libraries 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 From 251e9a6c0f30802e276b62acb66ab10368586fbb Mon Sep 17 00:00:00 2001 From: Bernd Wolber Date: Fri, 2 Dec 2016 11:23:33 +0100 Subject: [PATCH 08/13] fixed wrong path --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ebae7025..b3da92208 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -279,7 +279,7 @@ if(WITH_SCRIPTS) if(DEB_BASED) configure_file(${CMAKE_SOURCE_DIR}/etc/ubuntu/init.d/maxscale.in ${CMAKE_BINARY_DIR}/maxscale @ONLY) elseif(SLES_BASED) - configure_file(${CMAKE_SOURCE_DIR}/etc/sles/init.d/maxscale.in ${CMAKE_BINARY_DIR}/maxscale @ONLY) + configure_file(${CMAKE_SOURCE_DIR}/etc/sles11/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() From fe2a607d53db0cb1bc6d6cf9e292c8ac70cc2f3b Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Wed, 14 Dec 2016 10:20:55 +0200 Subject: [PATCH 09/13] Add 2.0.3 release notes --- .../MaxScale-2.0.3-Release-Notes.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) 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). - From 15040e7a76de9e4641045b5d6023c851dd920f7b Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Wed, 14 Dec 2016 12:38:27 +0200 Subject: [PATCH 10/13] Update version number --- VERSION.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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") From 9e27e6c002b297d6ddbdbdb34c3de849069a9d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Fri, 16 Dec 2016 15:51:17 +0200 Subject: [PATCH 11/13] Don't ask for unlimited files There's a bug reported on Launchpad (https://bugs.launchpad.net/ubuntu/+source/upstart/+bug/1420640) that describes a similar situation. The solution is to request a lower limit of open files for the process. --- etc/upstart/maxscale.conf.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 7a08ea99af0e59e4bee1d73dc99e70db4cc6a98a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 4 Nov 2016 15:19:22 +0200 Subject: [PATCH 12/13] Change README to Markdown and fix Travis for 2.0 Converting the README into Markdown format makes it a lot easier to comprehent. Also cleaned up the formatting. The 2.0 branch had a broken Travis configuration. Fixed it and changed links to point to 2.0 branch. --- .travis.yml | 12 ++---------- .travis/build_maxscale.sh | 7 +++---- CMakeLists.txt | 2 +- README => README.md | 32 +++++++++++++++++++------------- 4 files changed, 25 insertions(+), 28 deletions(-) rename README => README.md (56%) diff --git a/.travis.yml b/.travis.yml index 33d030bc7..052d7da42 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,18 +6,8 @@ os: linux env: global: - - MARIADB_URL=https://downloads.mariadb.org/interstitial/mariadb-5.5.48/bintar-linux-glibc_214-x86_64/mariadb-5.5.48-linux-glibc_214-x86_64.tar.gz/from/http%3A//mirror.netinch.com/pub/mariadb/ - - MARIADB_TAR=mariadb-5.5.48-linux-glibc_214-x86_64.tar.gz - - MARIADB_DIR=mariadb-5.5.48-linux-x86_64 - secure: "kfzqiIq1XhZ89XYsnqFhPKr5UWB+W4fYAYpOYOLgWMmqfjwqQTm1nN/A6TuFmdbTrzB6hLawsxIUrPS+QKs4TI8tTQMRZ8IZV4TIUQVa7SNQljwrKvnSu0fSoqpPrvXxjEjbTlvpo7X5EKCyCB0Xz6NaYVJIvE9bYnwCEAJw30k=" - -# prepare the environment -before_script: - # get mariadb packages from mariadb.org - - chmod +x .travis/download_mariadb.sh - - .travis/download_mariadb.sh - # actual compilation commands script: - chmod +x .travis/build_maxscale.sh @@ -31,6 +21,8 @@ addons: - libpcre3-dev - doxygen - pandoc + - uuid + - uuid-dev coverity_scan: project: name: "mariadb-corporation/MaxScale" diff --git a/.travis/build_maxscale.sh b/.travis/build_maxscale.sh index 919455cb5..916b8d37c 100644 --- a/.travis/build_maxscale.sh +++ b/.travis/build_maxscale.sh @@ -8,16 +8,15 @@ echo TRAVIS_BUILD_DIR: ${TRAVIS_BUILD_DIR} -echo MARIADB_DIR: ${MARIADB_DIR} mkdir build cd build -cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DMYSQL_EMBEDDED_INCLUDE_DIR=${TRAVIS_BUILD_DIR}/${MARIADB_DIR}/include/ -DMYSQL_EMBEDDED_LIBRARIES=${TRAVIS_BUILD_DIR}/${MARIADB_DIR}/lib/libmysqld.a -DERRMSG=${TRAVIS_BUILD_DIR}/${MARIADB_DIR}/share/english/errmsg.sys +cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=Y -DBUILD_AVRO=N -make VERBOSE=1 +make +make test sudo make install -sudo make testcore sudo ./postinst maxscale --version diff --git a/CMakeLists.txt b/CMakeLists.txt index b3da92208..f29db5dd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -266,7 +266,7 @@ if(WITH_MAXSCALE_CNF) endif() install(FILES server/maxscale_binlogserver_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION ${MAXSCALE_SHAREDIR}) -install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES ${CMAKE_SOURCE_DIR}/README.md DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_SOURCE_DIR}/LICENSE.TXT DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES etc/lsyncd_example.conf DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES Documentation/maxscale.1 DESTINATION ${CMAKE_INSTALL_DATADIR}/man/man1) diff --git a/README b/README.md similarity index 56% rename from README rename to README.md index fb0611c14..5f65df0d5 100644 --- a/README +++ b/README.md @@ -1,5 +1,7 @@ # MaxScale by MariaDB Corporation +[![Build Status](https://travis-ci.org/mariadb-corporation/MaxScale.svg?branch=2.0)](https://travis-ci.org/mariadb-corporation/MaxScale) + The MariaDB Corporation MaxScale is an intelligent proxy that allows forwarding of database statements to one or more database servers using complex rules, a semantic understanding of the database statements and the @@ -26,23 +28,27 @@ as external shared objects and are referred to as routing modules. An Google Group exists for MaxScale that can be used to discuss ideas, issues and communicate with the MaxScale community. - Email: maxscale@googlegroups.com - Forum: http://groups.google.com/forum/#!forum/maxscale +- Email: maxscale@googlegroups.com +- Forum: http://groups.google.com/forum/#!forum/maxscale -Bugs can be reported in the MariaDB Corporation bugs database: -https://jira.mariadb.org/projects/MXS/issues +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 For information about installing and using MaxScale, please refer to the -documentation. It is in Markdown format. +documentation. The official documentation can be found on the +[MariaDB Knowledge Base](https://mariadb.com/kb/en/mariadb-enterprise/maxscale/). -You can point your browser to the MaxScale project at GitHub. Look -inside the "Documentation" directory, where you will find a file named -Documentation-Contents.md. Click on that, and GitHub will show the -documentation in its intended display format. The contents page lists -the available documents and has links to them. +- [MariaDB MaxScale 2.0 Documentation](https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-20-contents/) +- [MariaDB MaxScale 1.4 Documentation](https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-14/maxscale-maxscale-contents/) -If you do not want to rely on the internet, then clone the project -from GitHub and point your browser to the Documentation-Contents.md -file in your local file system and proceed as above. +The module and configuration documentation can be found in the _Documentation_ +directory of the source tree. + +# Contributing Code + +Read the [Contributing](https://github.com/mariadb-corporation/MaxScale/wiki/Contributing) +page on the wiki for more information on how to do pull request and where to do +them. From 68965639d24c5b5b205c98c1f9d8c26e2797a807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Tue, 20 Dec 2016 02:32:48 +0200 Subject: [PATCH 13/13] Document zero weightby values The behavior when a server had a zero value for the weighting parameter was not documented. --- Documentation/Getting-Started/Configuration-Guide.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index c28eb1d08..bd593ea5a 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -512,6 +512,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.