diff --git a/include/maxscale/protocol/mysql.h b/include/maxscale/protocol/mysql.h index c2af6903a..bd4edf090 100644 --- a/include/maxscale/protocol/mysql.h +++ b/include/maxscale/protocol/mysql.h @@ -526,6 +526,15 @@ bool mxs_mysql_is_ok_packet(GWBUF* buffer); */ bool mxs_mysql_is_err_packet(GWBUF* buffer); +/** + * Extract the error code from an ERR packet + * + * @param buffer Buffer containing the ERR packet + * + * @return The error code or 0 if the buffer is not an ERR packet + */ +uint16_t mxs_mysql_get_mysql_errno(GWBUF* buffer); + /** * @brief Check if a buffer contains a result set * diff --git a/server/modules/protocol/MySQL/mysql_common.cc b/server/modules/protocol/MySQL/mysql_common.cc index cbbfc561b..cdb8fd1e1 100644 --- a/server/modules/protocol/MySQL/mysql_common.cc +++ b/server/modules/protocol/MySQL/mysql_common.cc @@ -1171,6 +1171,21 @@ bool mxs_mysql_is_err_packet(GWBUF* buffer) return cmd == MYSQL_REPLY_ERR; } +uint16_t mxs_mysql_get_mysql_errno(GWBUF* buffer) +{ + uint16_t rval = 0; + + if (mxs_mysql_is_err_packet(buffer)) + { + uint8_t buf[2]; + // First two bytes after the 0xff byte are the error code + gwbuf_copy_data(buffer, MYSQL_HEADER_LEN + 1, 2, buf); + rval = gw_mysql_get_byte2(buf); + } + + return rval; +} + bool mxs_mysql_is_result_set(GWBUF* buffer) { bool rval = false; diff --git a/server/modules/routing/readwritesplit/rwsplitsession.cc b/server/modules/routing/readwritesplit/rwsplitsession.cc index 76615472e..ae86915b9 100644 --- a/server/modules/routing/readwritesplit/rwsplitsession.cc +++ b/server/modules/routing/readwritesplit/rwsplitsession.cc @@ -555,6 +555,12 @@ void RWSplitSession::manage_transactions(SRWBackend& backend, GWBUF* writebuf) } } +static bool server_is_shutting_down(GWBUF* writebuf) +{ + uint64_t err = mxs_mysql_get_mysql_errno(writebuf); + return err == ER_SERVER_SHUTDOWN || err == ER_NORMAL_SHUTDOWN || err == ER_SHUTDOWN_COMPLETE; +} + void RWSplitSession::clientReply(GWBUF* writebuf, DCB* backend_dcb) { DCB* client_dcb = backend_dcb->session->client_dcb; @@ -578,6 +584,14 @@ void RWSplitSession::clientReply(GWBUF* writebuf, DCB* backend_dcb) } return; } + else if (backend->get_reply_state() == REPLY_STATE_START && server_is_shutting_down(writebuf)) + { + // The server is shutting down, act as if the network connection failed. This allows + // the query to be retried on another server without the client noticing it. + poll_fake_hangup_event(backend_dcb); + gwbuf_free(writebuf); + return; + } if ((writebuf = handle_causal_read_reply(writebuf, backend)) == NULL) {