diff --git a/include/maxscale/queryclassifier.hh b/include/maxscale/queryclassifier.hh index fe35e6154..1c3e654eb 100644 --- a/include/maxscale/queryclassifier.hh +++ b/include/maxscale/queryclassifier.hh @@ -208,6 +208,16 @@ public: m_load_data_state = state; } + /** + * Check if current transaction is still a read-only transaction + * + * @return True if no statements have been executed that modify data + */ + bool is_trx_still_read_only() const + { + return m_trx_is_read_only; + } + /** * @brief Store and process a prepared statement * @@ -313,6 +323,15 @@ private: */ uint32_t ps_id_internal_get(GWBUF* pBuffer); + /** + * Check if the query type is that of a read-only query + * + * @param qtype Query type mask + * + * @return True if the query type is that of a read-only query + */ + bool query_type_is_read_only(uint32_t qtype) const; + uint32_t get_route_target(uint8_t command, uint32_t qtype, HINT* pHints); MXS_SESSION* session() const @@ -361,6 +380,7 @@ private: SPSManager m_sPs_manager; HandleMap m_ps_handles; /** External ID to internal ID */ RouteInfo m_route_info; + bool m_trx_is_read_only; }; } diff --git a/server/core/queryclassifier.cc b/server/core/queryclassifier.cc index 44dfd5581..e6d035157 100644 --- a/server/core/queryclassifier.cc +++ b/server/core/queryclassifier.cc @@ -346,6 +346,7 @@ QueryClassifier::QueryClassifier(Handler* pHandler, , m_large_query(false) , m_multi_statements_allowed(are_multi_statements_allowed(pSession)) , m_sPs_manager(new PSManager) + , m_trx_is_read_only(true) { } @@ -369,6 +370,34 @@ void QueryClassifier::ps_erase(GWBUF* buffer) return m_sPs_manager->erase(buffer); } +bool QueryClassifier::query_type_is_read_only(uint32_t qtype) const +{ + bool rval = false; + + if (!qc_query_is_type(qtype, QUERY_TYPE_MASTER_READ) && + !qc_query_is_type(qtype, QUERY_TYPE_WRITE) && + (qc_query_is_type(qtype, QUERY_TYPE_READ) || + qc_query_is_type(qtype, QUERY_TYPE_SHOW_TABLES) || + qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ) || + qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) || + qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ))) + { + if (qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ)) + { + if (m_use_sql_variables_in == TYPE_ALL) + { + rval = true; + } + } + else + { + rval = true; + } + } + + return rval; +} + uint32_t QueryClassifier::get_route_target(uint8_t command, uint32_t qtype, HINT* pHints) { bool trx_active = session_trx_is_active(m_pSession); @@ -430,35 +459,9 @@ uint32_t QueryClassifier::get_route_target(uint8_t command, uint32_t qtype, HINT /** * Hints may affect on routing of the following queries */ - else if (!trx_active && !load_active && - !qc_query_is_type(qtype, QUERY_TYPE_MASTER_READ) && - !qc_query_is_type(qtype, QUERY_TYPE_WRITE) && - (qc_query_is_type(qtype, QUERY_TYPE_READ) || - qc_query_is_type(qtype, QUERY_TYPE_SHOW_TABLES) || - qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ) || - qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) || - qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ))) + else if (!trx_active && !load_active && query_type_is_read_only(qtype)) { - if (qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ)) - { - if (m_use_sql_variables_in == TYPE_ALL) - { - target = TARGET_SLAVE; - } - } - else if (qc_query_is_type(qtype, QUERY_TYPE_READ) || // Normal read - qc_query_is_type(qtype, QUERY_TYPE_SHOW_TABLES) || // SHOW TABLES - qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) || // System variable - qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ)) // Global system variable - { - target = TARGET_SLAVE; - } - - /** If nothing matches then choose the master */ - if ((target & (TARGET_ALL | TARGET_SLAVE | TARGET_MASTER)) == 0) - { - target = TARGET_MASTER; - } + target = TARGET_SLAVE; } else if (session_trx_is_read_only(m_pSession)) { @@ -929,6 +932,19 @@ QueryClassifier::update_route_info(QueryClassifier::current_target_t current_tar route_target = get_route_target(command, type_mask, pBuffer->hint); } + + if (session_trx_is_ending(m_pSession) || + qc_query_is_type(type_mask, QUERY_TYPE_BEGIN_TRX)) + { + // Transaction is ending or starting + m_trx_is_read_only = true; + } + else if (session_trx_is_active(m_pSession) && + !query_type_is_read_only(type_mask)) + { + // Transaction is no longer read-only + m_trx_is_read_only = false; + } } else if (load_data_state() == QueryClassifier::LOAD_DATA_ACTIVE) {