Clean up unexpected error handling in readwritesplit

By using the Error class, the code can be cleaned up and simplified.
This commit is contained in:
Markus Mäkelä 2019-06-14 10:06:38 +03:00
parent 5edfba6623
commit 7dde0edb54
No known key found for this signature in database
GPG Key ID: 72D48FCE664F7B19
3 changed files with 39 additions and 24 deletions

View File

@ -19,6 +19,7 @@
#include <maxscale/backend.hh>
#include <maxscale/modutil.hh>
#include <maxscale/response_stat.hh>
#include <maxscale/protocol/mysql.hh>
namespace maxscale
{
@ -87,6 +88,21 @@ public:
return rv;
}
bool is_unexpected_error() const
{
switch (m_code)
{
case ER_CONNECTION_KILLED:
case ER_SERVER_SHUTDOWN:
case ER_NORMAL_SHUTDOWN:
case ER_SHUTDOWN_COMPLETE:
return true;
default:
return false;
}
}
uint32_t code() const
{
return m_code;
@ -121,7 +137,7 @@ public:
}
private:
uint16_t m_code { 0 };
uint16_t m_code {0};
std::string m_sql_state;
std::string m_message;
};

View File

@ -410,7 +410,7 @@ void RWBackend::process_reply(GWBUF* buffer)
process_packets(buffer);
}
if (get_reply_state() == REPLY_STATE_DONE)
if (get_reply_state() == REPLY_STATE_DONE && is_waiting_result())
{
ack_write();
}

View File

@ -580,29 +580,15 @@ void RWSplitSession::clientReply(GWBUF* writebuf, DCB* backend_dcb)
DCB* client_dcb = backend_dcb->session->client_dcb;
RWBackend* backend = get_backend_from_dcb(backend_dcb);
if (backend->get_reply_state() == REPLY_STATE_DONE)
if (backend->get_reply_state() == REPLY_STATE_DONE
&& !connection_was_killed(writebuf)
&& !server_is_shutting_down(writebuf))
{
if (connection_was_killed(writebuf))
{
// The connection was killed, we can safely ignore it. When the TCP connection is
// closed, the router's error handling will sort it out.
gwbuf_free(writebuf);
}
else
{
/** If we receive an unexpected response from the server, the internal
* logic cannot handle this situation. Routing the reply straight to
* the client should be the safest thing to do at this point. */
log_unexpected_response(backend, writebuf, m_current_query.get());
MXS_SESSION_ROUTE_REPLY(backend_dcb->session, writebuf);
}
return;
}
else if (backend->get_reply_state() == REPLY_STATE_START && server_is_shutting_down(writebuf))
{
// The server is shutting down, ignore this error and wait for the TCP connection to die.
// This allows the query to be retried on another server without the client noticing it.
gwbuf_free(writebuf);
/** If we receive an unexpected response from the server, the internal
* logic cannot handle this situation. Routing the reply straight to
* the client should be the safest thing to do at this point. */
log_unexpected_response(backend, writebuf, m_current_query.get());
MXS_SESSION_ROUTE_REPLY(backend_dcb->session, writebuf);
return;
}
@ -615,6 +601,19 @@ void RWSplitSession::clientReply(GWBUF* writebuf, DCB* backend_dcb)
const RWBackend::Error& error = backend->error();
if (error.is_unexpected_error())
{
// The server sent an error that we didn't expect: treat it as if the connection was closed. The
// client shouldn't see this error as we can replace the closed connection.
// TODO: Erase trailing packets
if (mxs_mysql_is_err_packet(writebuf))
{
gwbuf_free(writebuf);
return;
}
}
if (error && m_config.transaction_replay && error.is_rollback())
{
MXS_INFO("A retryable error: %s", error.message().c_str());