MXS-1150: Do not cache non-cachable functions
The resultset of SELECTs that use functions whose result will always vary or whose result depend upon the user executing the query should not be cached. The list of functions is the same as that specified for the query cache of MariaDB: https://mariadb.com/kb/en/mariadb/query-cache/
This commit is contained in:
@ -3,12 +3,19 @@
|
||||
This filter was introduced in MariaDB MaxScale 2.1.
|
||||
|
||||
## Overview
|
||||
_Note that the cache is still experimental and that non-backward compatible
|
||||
changes may be made._
|
||||
|
||||
The cache filter is a simple cache that is capable of caching the result of
|
||||
SELECTs, so that subsequent identical SELECTs are served directly by MaxScale,
|
||||
without the queries being routed to any server.
|
||||
|
||||
_Note that the cache is still experimental and that non-backward compatible
|
||||
changes may be made._
|
||||
SELECTs using the following functions will not be cached: `BENCHMARK`,
|
||||
`CONNECTION_ID`, `CONVERT_TZ`, `CURDATE`, `CURRENT_DATE`, `CURRENT_TIMESTAMP`,
|
||||
`CURTIME`, `DATABASE`, `ENCRYPT`, `FOUND_ROWS`, `GET_LOCK`, `IS_FREE_LOCK`,
|
||||
`IS_USED_LOCK`, `LAST_INSERT_ID`, `LOAD_FILE`, `LOCALTIME`, `LOCALTIMESTAMP`,
|
||||
`MASTER_POS_WAIT`, `NOW`, `RAND`, `RELEASE_LOCK`, `SESSION_USER`, `SLEEP`,
|
||||
`SYSDATE`, `SYSTEM_USER`, `UNIX_TIMESTAMP`, `USER`, `UUID`, `UUID_SHORT`.
|
||||
|
||||
Note that installing the cache causes all statements to be parsed. The
|
||||
implication of that is that unless statements _already_ need to be parsed,
|
||||
@ -28,7 +35,7 @@ Resultsets of prepared statements are **not** cached.
|
||||
### Transactions
|
||||
The cache will be used and populated in the following circumstances:
|
||||
|
||||
* There is _no_ on-going transaction, that is, _autocommit_ is used,
|
||||
* There is _no_ explicit transaction active, that is, _autocommit_ is used,
|
||||
* there is an _explicitly_ read-only transaction (that is,`START TRANSACTION
|
||||
READ ONLY`) active, or
|
||||
* there is a transaction active and _no_ statement that modify the database
|
||||
@ -44,7 +51,6 @@ If user or system variables are used in the _SELECT_ statement, the result
|
||||
will not be cached.
|
||||
|
||||
### Security
|
||||
|
||||
The cache is **not** aware of grants.
|
||||
|
||||
The implication is that unless the cache has been explicitly configured
|
||||
|
||||
112
server/modules/filter/cache/cachefiltersession.cc
vendored
112
server/modules/filter/cache/cachefiltersession.cc
vendored
@ -35,6 +35,108 @@ inline bool cache_max_resultset_size_exceeded(const CACHE_CONFIG& config, uint64
|
||||
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const char* NON_CACHEABLE_FUNCTIONS[] =
|
||||
{
|
||||
"benchmark",
|
||||
"connection_id",
|
||||
"convert_tz",
|
||||
"curdate",
|
||||
"current_date",
|
||||
"current_timestamp",
|
||||
"curtime",
|
||||
"database",
|
||||
"encrypt",
|
||||
"found_rows",
|
||||
"get_lock",
|
||||
"is_free_lock",
|
||||
"is_used_lock",
|
||||
"last_insert_id",
|
||||
"load_file",
|
||||
"localtime",
|
||||
"localtimestamp",
|
||||
"master_pos_wait",
|
||||
"now",
|
||||
"rand",
|
||||
"release_lock",
|
||||
"session_user",
|
||||
"sleep",
|
||||
"sysdate",
|
||||
"system_user",
|
||||
"unix_timestamp",
|
||||
"user",
|
||||
"uuid",
|
||||
"uuid_short",
|
||||
};
|
||||
|
||||
const char* NON_CACHEABLE_VARIABLES[] =
|
||||
{
|
||||
"current_date",
|
||||
"current_timestamp",
|
||||
"localtime",
|
||||
"localtimestamp",
|
||||
};
|
||||
|
||||
const size_t N_NON_CACHEABLE_FUNCTIONS = sizeof(NON_CACHEABLE_FUNCTIONS)/sizeof(NON_CACHEABLE_FUNCTIONS[0]);
|
||||
const size_t N_NON_CACHEABLE_VARIABLES = sizeof(NON_CACHEABLE_VARIABLES)/sizeof(NON_CACHEABLE_VARIABLES[0]);
|
||||
|
||||
int compare_name(const void* pLeft, const void* pRight)
|
||||
{
|
||||
return strcasecmp((const char*)pLeft, *(const char**)pRight);
|
||||
}
|
||||
|
||||
inline bool uses_name(const char* zName, const char** pzNames, size_t nNames)
|
||||
{
|
||||
return bsearch(zName, pzNames, nNames, sizeof(const char*), compare_name) != NULL;
|
||||
}
|
||||
|
||||
bool uses_non_cacheable_function(GWBUF* pPacket)
|
||||
{
|
||||
bool rv = false;
|
||||
|
||||
const QC_FUNCTION_INFO* pInfo;
|
||||
size_t nInfos;
|
||||
|
||||
qc_get_function_info(pPacket, &pInfo, &nInfos);
|
||||
|
||||
const QC_FUNCTION_INFO* pEnd = pInfo + nInfos;
|
||||
|
||||
while (!rv && (pInfo != pEnd))
|
||||
{
|
||||
rv = uses_name(pInfo->name, NON_CACHEABLE_FUNCTIONS, N_NON_CACHEABLE_FUNCTIONS);
|
||||
|
||||
++pInfo;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool uses_non_cacheable_variable(GWBUF* pPacket)
|
||||
{
|
||||
bool rv = false;
|
||||
|
||||
const QC_FIELD_INFO* pInfo;
|
||||
size_t nInfos;
|
||||
|
||||
qc_get_field_info(pPacket, &pInfo, &nInfos);
|
||||
|
||||
const QC_FIELD_INFO* pEnd = pInfo + nInfos;
|
||||
|
||||
while (!rv && (pInfo != pEnd))
|
||||
{
|
||||
rv = uses_name(pInfo->column, NON_CACHEABLE_VARIABLES, N_NON_CACHEABLE_VARIABLES);
|
||||
|
||||
++pInfo;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
CacheFilterSession::CacheFilterSession(MXS_SESSION* pSession, Cache* pCache, char* zDefaultDb)
|
||||
: maxscale::FilterSession(pSession)
|
||||
, m_state(CACHE_EXPECTING_NOTHING)
|
||||
@ -719,6 +821,16 @@ bool CacheFilterSession::should_consult_cache(GWBUF* pPacket)
|
||||
consult_cache = false;
|
||||
zReason = "system variables are read";
|
||||
}
|
||||
else if (uses_non_cacheable_function(pPacket))
|
||||
{
|
||||
consult_cache = false;
|
||||
zReason = "uses non-cacheable function";
|
||||
}
|
||||
else if (uses_non_cacheable_variable(pPacket))
|
||||
{
|
||||
consult_cache = false;
|
||||
zReason = "uses non-cacheable variable";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user