diff --git a/include/maxscale/clock.h b/include/maxscale/clock.h index eed126c78..da72319f6 100644 --- a/include/maxscale/clock.h +++ b/include/maxscale/clock.h @@ -30,11 +30,17 @@ int64_t mxs_clock(); /** * Convert heartbeats to seconds */ -#define MXS_CLOCK_TO_SEC(a) ((int64_t)a / 10) +static inline int64_t MXS_CLOCK_TO_SEC(int64_t a) +{ + return a / 10; +} /** * Convert seconds to heartbeats */ -#define MXS_SEC_TO_CLOCK(a) ((int64_t)a * 10) +static inline int64_t MXS_SEC_TO_CLOCK(int64_t a) +{ + return a * 10; +} MXS_END_DECLS diff --git a/include/maxscale/protocol.hh b/include/maxscale/protocol.hh index 22561713d..1ec46d0a8 100644 --- a/include/maxscale/protocol.hh +++ b/include/maxscale/protocol.hh @@ -179,6 +179,18 @@ struct MXS_PROTOCOL * @return A buffer containing the error message */ GWBUF* (* reject)(const char* host); + + /** + * Check if the DCB can be closed in a controlled manner + * + * The DCB will be unconditionally closed if this entry point is not implemented or a hard-coded timeout + * is exceeded. + * + * @param dcb DCB to check + * + * @return True if the DCB can be closed + */ + bool (* can_close)(DCB*); }; /** @@ -186,7 +198,7 @@ struct MXS_PROTOCOL * the MXS_PROTOCOL structure is changed. See the rules defined in modinfo.h * that define how these numbers should change. */ -#define MXS_PROTOCOL_VERSION {2, 1, 0} +#define MXS_PROTOCOL_VERSION {2, 2, 0} /** * Specifies capabilities specific for protocol. diff --git a/server/core/routingworker.cc b/server/core/routingworker.cc index 5a12df695..5b9d72440 100644 --- a/server/core/routingworker.cc +++ b/server/core/routingworker.cc @@ -161,6 +161,15 @@ void modules_thread_finish() } } } + +bool can_close_dcb(DCB* dcb) +{ + auto idle = MXS_CLOCK_TO_SEC(mxs_clock() - dcb->last_read); + + return idle > 5 // Timed out + || !dcb->func.can_close // Not implemented + || dcb->func.can_close(dcb); // Protocol says it's OK to close +} } namespace maxscale @@ -550,6 +559,8 @@ void RoutingWorker::register_zombie(DCB* pDcb) void RoutingWorker::delete_zombies() { + Zombies not_ready; + // An algorithm cannot be used, as the final closing of a DCB may cause // other DCBs to be registered in the zombie queue. @@ -557,8 +568,18 @@ void RoutingWorker::delete_zombies() { DCB* pDcb = m_zombies.back(); m_zombies.pop_back(); - dcb_final_close(pDcb); + + if (can_close_dcb(pDcb)) + { + dcb_final_close(pDcb); + } + else + { + not_ready.push_back(pDcb); + } } + + m_zombies.insert(m_zombies.end(), not_ready.begin(), not_ready.end()); } bool RoutingWorker::pre_run() diff --git a/server/modules/protocol/MySQL/mariadbbackend/mysql_backend.cc b/server/modules/protocol/MySQL/mariadbbackend/mysql_backend.cc index a26069aa2..af303d157 100644 --- a/server/modules/protocol/MySQL/mariadbbackend/mysql_backend.cc +++ b/server/modules/protocol/MySQL/mariadbbackend/mysql_backend.cc @@ -63,6 +63,7 @@ static bool get_ip_string_and_port(struct sockaddr_storage* sa, int iplen, in_port_t* port_out); static bool gw_connection_established(DCB* dcb); +static bool gw_auth_is_complete(DCB* dcb); json_t* gw_json_diagnostics(DCB* dcb); extern "C" @@ -92,6 +93,8 @@ MXS_MODULE* MXS_CREATE_MODULE() NULL, /* Connection limit reached */ gw_connection_established, gw_json_diagnostics, + NULL, + gw_auth_is_complete, }; static MXS_MODULE info = @@ -2018,6 +2021,24 @@ static bool gw_connection_established(DCB* dcb) && !proto->stored_query; } +static bool gw_auth_is_complete(DCB* dcb) +{ + MySQLProtocol* proto = (MySQLProtocol*)dcb->protocol; + + switch (proto->protocol_auth_state) + { + case MXS_AUTH_STATE_FAILED: + case MXS_AUTH_STATE_HANDSHAKE_FAILED: + case MXS_AUTH_STATE_COMPLETE: + MXS_DEBUG("(%lu) Auth is complete for DCB %lu", dcb->session->ses_id, dcb->m_uid); + return true; + + default: + MXS_DEBUG("(%lu) Auth not yet complete for DCB %lu", dcb->session->ses_id, dcb->m_uid); + return false; + } +} + json_t* gw_json_diagnostics(DCB* dcb) { MySQLProtocol* proto = static_cast(dcb->protocol);