From 5242cd5ebfa8622b3ae7905fb3a3a54e0d914a9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Tue, 2 Apr 2019 10:18:33 +0300 Subject: [PATCH] Readwritesplit: Graceful maintenance mode By allowing transactions to the master to end even if the server is in maintenance mode makes it possible to terminate connections at a known point. This helps prevent interrupted transactions which can help reduce errors that are visible to the clients. --- .../routing/readwritesplit/readwritesplit.hh | 19 +++++++++++++++++++ .../readwritesplit/rwsplit_route_stmt.cc | 2 +- .../readwritesplit/rwsplit_select_backends.cc | 2 +- .../routing/readwritesplit/rwsplitsession.cc | 15 ++++++++++++--- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.hh b/server/modules/routing/readwritesplit/readwritesplit.hh index 54023cc39..b2941da65 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.hh +++ b/server/modules/routing/readwritesplit/readwritesplit.hh @@ -386,6 +386,25 @@ static inline const char* failure_mode_to_str(enum failure_mode type) void closed_session_reply(GWBUF* querybuf); bool send_readonly_error(DCB* dcb); +/** + * See if the current master is still a valid TARGET_MASTER candidate + * + * The master is valid if it's in `Master, Running` state or if it is still in but in maintenance mode while a + * transaction is open. If a transaction is open to a master in maintenance mode, the connection is closed on + * the next COMMIT or ROLLBACK. + * + * @see RWSplitSession::close_stale_connections() + */ +inline bool can_continue_using_master(const mxs::RWBackend* current_master) +{ + constexpr uint64_t bits = SERVER_MASTER | SERVER_RUNNING | SERVER_MAINT; + auto server = current_master->server(); + + return server->is_master() || (current_master->in_use() + && (server->status & bits) == bits + && session_trx_is_active(current_master->dcb()->session)); +} + mxs::RWBackend* get_root_master(const mxs::PRWBackends& backends, mxs::RWBackend* current_master, const BackendSelectFunction& func); diff --git a/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc b/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc index 812140faa..4f8aae865 100644 --- a/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc +++ b/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc @@ -638,7 +638,7 @@ RWBackend* RWSplitSession::get_master_backend() { if (master->in_use() || (m_config.master_reconnection && master->can_connect())) { - if (master->is_master()) + if (can_continue_using_master(master)) { rval = master; } diff --git a/server/modules/routing/readwritesplit/rwsplit_select_backends.cc b/server/modules/routing/readwritesplit/rwsplit_select_backends.cc index 082e2736a..579b65c38 100644 --- a/server/modules/routing/readwritesplit/rwsplit_select_backends.cc +++ b/server/modules/routing/readwritesplit/rwsplit_select_backends.cc @@ -395,7 +395,7 @@ static void log_server_connections(select_criteria_t criteria, const PRWBackends RWBackend* get_root_master(const PRWBackends& backends, RWBackend* current_master, const BackendSelectFunction& func) { - if (current_master && current_master->in_use() && current_master->is_master()) + if (current_master && current_master->in_use() && can_continue_using_master(current_master)) { return current_master; } diff --git a/server/modules/routing/readwritesplit/rwsplitsession.cc b/server/modules/routing/readwritesplit/rwsplitsession.cc index 2cdd7a3b4..e7fde5a22 100644 --- a/server/modules/routing/readwritesplit/rwsplitsession.cc +++ b/server/modules/routing/readwritesplit/rwsplitsession.cc @@ -541,7 +541,7 @@ void RWSplitSession::close_stale_connections() { auto current_rank = get_current_rank(); - for (auto& backend : m_backends) + for (auto& backend : m_raw_backends) { if (backend->in_use()) { @@ -549,8 +549,17 @@ void RWSplitSession::close_stale_connections() if (!server->is_usable()) { - MXS_INFO("Discarding connection to '%s': Server is in maintenance", backend->name()); - backend->close(); + if (backend == m_current_master + && can_continue_using_master(m_current_master) + && !session_trx_is_ending(m_client->session)) + { + MXS_INFO("Keeping connection to '%s' open until transaction ends", backend->name()); + } + else + { + MXS_INFO("Discarding connection to '%s': Server is in maintenance", backend->name()); + backend->close(); + } } else if (server->rank() != current_rank) {