MXS-1625 Add QueryClassifier class
This is the first step in providing a QueryClassifier class that is capable of performing context dependent query classification, where the context is essentially the session state.
This commit is contained in:
51
include/maxscale/queryclassifier.hh
Normal file
51
include/maxscale/queryclassifier.hh
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License included
|
||||||
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
|
*
|
||||||
|
* Change Date: 2020-01-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source License, use
|
||||||
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
* Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <maxscale/cppdefs.hh>
|
||||||
|
#include <maxscale/router.h>
|
||||||
|
#include <maxscale/session.h>
|
||||||
|
|
||||||
|
namespace maxscale
|
||||||
|
{
|
||||||
|
|
||||||
|
class QueryClassifier
|
||||||
|
{
|
||||||
|
QueryClassifier(const QueryClassifier&) = delete;
|
||||||
|
QueryClassifier& operator = (const QueryClassifier&) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// NOTE: For the time being these must be exactly like the ones in readwritesplit.hh
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TARGET_UNDEFINED = 0x00,
|
||||||
|
TARGET_MASTER = 0x01,
|
||||||
|
TARGET_SLAVE = 0x02,
|
||||||
|
TARGET_ALL = 0x08
|
||||||
|
};
|
||||||
|
|
||||||
|
QueryClassifier(MXS_SESSION* pSession,
|
||||||
|
mxs_target_t use_sql_variables_in);
|
||||||
|
|
||||||
|
void set_load_active(bool active);
|
||||||
|
bool load_active() const;
|
||||||
|
|
||||||
|
uint32_t get_route_target(uint8_t command, uint32_t qtype);
|
||||||
|
|
||||||
|
private:
|
||||||
|
MXS_SESSION* m_pSession;
|
||||||
|
mxs_target_t m_use_sql_variables_in;
|
||||||
|
bool m_load_active;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@ -32,6 +32,7 @@ add_library(maxscale-common SHARED
|
|||||||
mysql_utils.cc
|
mysql_utils.cc
|
||||||
paths.cc
|
paths.cc
|
||||||
poll.cc
|
poll.cc
|
||||||
|
queryclassifier.cc
|
||||||
query_classifier.cc
|
query_classifier.cc
|
||||||
random_jkiss.cc
|
random_jkiss.cc
|
||||||
resultset.cc
|
resultset.cc
|
||||||
|
|||||||
168
server/core/queryclassifier.cc
Normal file
168
server/core/queryclassifier.cc
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License included
|
||||||
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
|
*
|
||||||
|
* Change Date: 2020-01-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source License, use
|
||||||
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
* Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <maxscale/queryclassifier.hh>
|
||||||
|
#include <maxscale/query_classifier.h>
|
||||||
|
#include <maxscale/protocol/mysql.h>
|
||||||
|
|
||||||
|
namespace maxscale
|
||||||
|
{
|
||||||
|
|
||||||
|
QueryClassifier::QueryClassifier(MXS_SESSION* pSession,
|
||||||
|
mxs_target_t use_sql_variables_in)
|
||||||
|
: m_pSession(pSession)
|
||||||
|
, m_use_sql_variables_in(use_sql_variables_in)
|
||||||
|
, m_load_active(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QueryClassifier::set_load_active(bool active)
|
||||||
|
{
|
||||||
|
m_load_active = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QueryClassifier::load_active() const
|
||||||
|
{
|
||||||
|
return m_load_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t QueryClassifier::get_route_target(uint8_t command, uint32_t qtype)
|
||||||
|
{
|
||||||
|
bool trx_active = session_trx_is_active(m_pSession);
|
||||||
|
uint32_t target = TARGET_UNDEFINED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepared statements preparations should go to all servers
|
||||||
|
*/
|
||||||
|
if (qc_query_is_type(qtype, QUERY_TYPE_PREPARE_STMT) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_NAMED_STMT) ||
|
||||||
|
command == MXS_COM_STMT_CLOSE ||
|
||||||
|
command == MXS_COM_STMT_RESET)
|
||||||
|
{
|
||||||
|
target = TARGET_ALL;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* These queries should be routed to all servers
|
||||||
|
*/
|
||||||
|
else if (!m_load_active &&
|
||||||
|
(qc_query_is_type(qtype, QUERY_TYPE_SESSION_WRITE) ||
|
||||||
|
/** Configured to allow writing user variables to all nodes */
|
||||||
|
(m_use_sql_variables_in == TYPE_ALL &&
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_USERVAR_WRITE)) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_WRITE) ||
|
||||||
|
/** enable or disable autocommit are always routed to all */
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT)))
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This is problematic query because it would be routed to all
|
||||||
|
* backends but since this is SELECT that is not possible:
|
||||||
|
* 1. response set is not handled correctly in clientReply and
|
||||||
|
* 2. multiple results can degrade performance.
|
||||||
|
*
|
||||||
|
* Prepared statements are an exception to this since they do not
|
||||||
|
* actually do anything but only prepare the statement to be used.
|
||||||
|
* They can be safely routed to all backends since the execution
|
||||||
|
* is done later.
|
||||||
|
*
|
||||||
|
* With prepared statement caching the task of routing
|
||||||
|
* the execution of the prepared statements to the right server would be
|
||||||
|
* an easy one. Currently this is not supported.
|
||||||
|
*/
|
||||||
|
if (qc_query_is_type(qtype, QUERY_TYPE_READ))
|
||||||
|
{
|
||||||
|
MXS_WARNING("The query can't be routed to all "
|
||||||
|
"backend servers because it includes SELECT and "
|
||||||
|
"SQL variable modifications which is not supported. "
|
||||||
|
"Set use_sql_variables_in=master or split the "
|
||||||
|
"query to two, where SQL variable modifications "
|
||||||
|
"are done in the first and the SELECT in the "
|
||||||
|
"second one.");
|
||||||
|
|
||||||
|
target = TARGET_MASTER;
|
||||||
|
}
|
||||||
|
target |= TARGET_ALL;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Hints may affect on routing of the following queries
|
||||||
|
*/
|
||||||
|
else if (!trx_active && !m_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)))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (session_trx_is_read_only(m_pSession))
|
||||||
|
{
|
||||||
|
/* Force TARGET_SLAVE for READ ONLY transaction (active or ending) */
|
||||||
|
target = TARGET_SLAVE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ss_dassert(trx_active || m_load_active ||
|
||||||
|
(qc_query_is_type(qtype, QUERY_TYPE_WRITE) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_MASTER_READ) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_SESSION_WRITE) ||
|
||||||
|
(qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ) &&
|
||||||
|
m_use_sql_variables_in == TYPE_MASTER) ||
|
||||||
|
(qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) &&
|
||||||
|
m_use_sql_variables_in == TYPE_MASTER) ||
|
||||||
|
(qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ) &&
|
||||||
|
m_use_sql_variables_in == TYPE_MASTER) ||
|
||||||
|
(qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_WRITE) &&
|
||||||
|
m_use_sql_variables_in == TYPE_MASTER) ||
|
||||||
|
(qc_query_is_type(qtype, QUERY_TYPE_USERVAR_WRITE) &&
|
||||||
|
m_use_sql_variables_in == TYPE_MASTER) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_BEGIN_TRX) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_ROLLBACK) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_COMMIT) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_EXEC_STMT) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_CREATE_TMP_TABLE) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_READ_TMP_TABLE) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_UNKNOWN)) ||
|
||||||
|
qc_query_is_type(qtype, QUERY_TYPE_EXEC_STMT));
|
||||||
|
|
||||||
|
target = TARGET_MASTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user