MXS-1475 Introduce @maxscale.cache.[soft_ttl|hard_ttl]

Now it is possible to control the soft and hard ttl of the
cache on a session basis. That is, it is possible to use
different TTLs for different SELECTs.
This commit is contained in:
Johan Wikman 2018-03-15 14:09:35 +02:00
parent 2c49f90bc4
commit 51251fd9f3
3 changed files with 280 additions and 36 deletions

View File

@ -378,6 +378,61 @@ SELECT @maxscale.cache.use;
```
but only after it has explicitly been set once.
### `@maxscale.cache.soft_ttl`
Using the variable `@maxscale.cache.soft_ttl` it is possible to specify
at runtime what _soft ttl_ should be applied. Its initial value is the
value of the configuration parameter `soft_ttl`. That is, by default the
value is 0.
The purpose of this variable is make it possible for an application to decide
statement by statement what _soft ttl_ should be applied.
```
set @maxscale.cache.soft_ttl=600;
SELECT a, b FROM unimportant;
set @maxscale.cache.soft_ttl=60;
SELECT c, d FROM important;
```
When data is `SELECT`ed from the unimportant table `unimportant`, the data
will be returned from the cache provided it is no older than 10 minutes,
but when data is `SELECT`ed from the important table `important`, the
data will be returned from the cache provided it is no older than 1 minute.
Note that `@maxscale.cache.hard_ttl` overrules `@maxscale.cache.soft_ttl`
in the sense that if the former is less that the latter, then _soft ttl_
will, when used, be adjusted down to the value of _hard ttl_.
The value of `@maxscale.cache.soft_ttl` can be queried
```
SELECT @maxscale.cache.soft_ttl;
```
but only after it has explicitly been set once.
### `@maxscale.cache.hard_ttl`
Using the variable `@maxscale.cache.hard_ttl` it is possible to specify
at runtime what _hard ttl_ should be applied. Its initial value is the
value of the configuration parameter `hard_ttl`. That is, by default the
value is 0.
The purpose of this variable is make it possible for an application to decide
statement by statement what _hard ttl_ should be applied.
Note that as `@maxscale.cache.hard_ttl` overrules `@maxscale.cache.soft_ttl`,
is is important to ensure that the former is at least as large as the latter
and for best overall performance that it is larger.
```
set @maxscale.cache.soft_ttl=600, @maxscale.cache.hard_ttl=610;
SELECT a, b FROM unimportant;
set @maxscale.cache.soft_ttl=60, @maxscale.cache.hard_ttl=65;
SELECT c, d FROM important;
```
The value of `@maxscale.cache.hard_ttl` can be queried
```
SELECT @maxscale.cache.hard_ttl;
```
but only after it has explicitly been set once.
### Client Driven Caching
With `@maxscale.cache.populate` and `@maxscale.cache.use` is it possible
to make the caching completely client driven.

View File

@ -39,7 +39,9 @@ namespace
{
const char SV_MAXSCALE_CACHE_POPULATE[] = "@maxscale.cache.populate";
const char SV_MAXSCALE_CACHE_USE[] = "@maxscale.cache.use";
const char SV_MAXSCALE_CACHE_USE[] = "@maxscale.cache.use";
const char SV_MAXSCALE_CACHE_SOFT_TTL[] = "@maxscale.cache.soft_ttl";
const char SV_MAXSCALE_CACHE_HARD_TTL[] = "@maxscale.cache.hard_ttl";
const char* NON_CACHEABLE_FUNCTIONS[] =
{
@ -198,20 +200,40 @@ CacheFilterSession::CacheFilterSession(MXS_SESSION* pSession, Cache* pCache, cha
reset_response_state();
if (!session_add_variable(pSession, SV_MAXSCALE_CACHE_POPULATE,
&CacheFilterSession::session_variable_handler, this))
&CacheFilterSession::set_cache_populate, this))
{
ss_dassert(!true);
MXS_ERROR("Could not add MaxScale user variable '%s', dynamically "
"enabling/disabling the populating of the cache is not possible.",
SV_MAXSCALE_CACHE_POPULATE);
}
if (!session_add_variable(pSession, SV_MAXSCALE_CACHE_USE,
&CacheFilterSession::session_variable_handler, this))
&CacheFilterSession::set_cache_use, this))
{
ss_dassert(!true);
MXS_ERROR("Could not add MaxScale user variable '%s', dynamically "
"enabling/disabling the using of the cache not possible.",
SV_MAXSCALE_CACHE_USE);
}
if (!session_add_variable(pSession, SV_MAXSCALE_CACHE_SOFT_TTL,
&CacheFilterSession::set_cache_soft_ttl, this))
{
ss_dassert(!true);
MXS_ERROR("Could not add MaxScale user variable '%s', dynamically "
"setting the soft TTL not possible.",
SV_MAXSCALE_CACHE_SOFT_TTL);
}
if (!session_add_variable(pSession, SV_MAXSCALE_CACHE_HARD_TTL,
&CacheFilterSession::set_cache_hard_ttl, this))
{
ss_dassert(!true);
MXS_ERROR("Could not add MaxScale user variable '%s', dynamically "
"setting the hard TTL not possible.",
SV_MAXSCALE_CACHE_HARD_TTL);
}
}
CacheFilterSession::~CacheFilterSession()
@ -1172,54 +1194,200 @@ bool get_truth_value(const char* begin, const char* end, bool* pValue)
return rv;
}
bool get_uint32_value(const char* begin, const char* end, uint32_t* pValue)
{
bool rv = false;
size_t len = end - begin;
char copy[len + 1];
memcpy(copy, begin, len);
copy[len] = 0;
errno = 0;
char* p;
long int l = strtol(copy, &p, 10);
if ((errno == 0) && (*p == 0))
{
if (l >= 0)
{
*pValue = l;
rv = true;
}
}
return rv;
}
char* CacheFilterSession::handle_session_variable(const char* zName,
const char* pValue_begin,
const char* pValue_end)
char* create_bool_error_message(const char* zName, const char* pValue_begin, const char* pValue_end)
{
static const char FORMAT[] = "The variable %s can only have the values true/false/1/0";
int n = snprintf(NULL, 0, FORMAT, zName) + 1;
char* zMessage = static_cast<char*>(MXS_MALLOC(n));
if (zMessage)
{
sprintf(zMessage, FORMAT, zName);
}
int len = pValue_end - pValue_begin;
MXS_WARNING("Attempt to set the variable %s to the invalid value \"%.*s\".",
zName, len, pValue_begin);
return zMessage;
}
char* create_uint32_error_message(const char* zName, const char* pValue_begin, const char* pValue_end)
{
static const char FORMAT[] = "The variable %s can have as value 0 or a positive integer.";
int n = snprintf(NULL, 0, FORMAT, zName) + 1;
char* zMessage = static_cast<char*>(MXS_MALLOC(n));
if (zMessage)
{
sprintf(zMessage, FORMAT, zName);
}
int len = pValue_end - pValue_begin;
MXS_WARNING("Attempt to set the variable %s to the invalid value \"%.*s\".",
zName, len, pValue_begin);
return zMessage;
}
}
char* CacheFilterSession::set_cache_populate(const char* zName,
const char* pValue_begin,
const char* pValue_end)
{
ss_dassert(strcmp(SV_MAXSCALE_CACHE_POPULATE, zName) == 0);
char* zMessage = NULL;
bool enabled;
if (get_truth_value(pValue_begin, pValue_end, &enabled))
{
if (strcmp(zName, SV_MAXSCALE_CACHE_POPULATE) == 0)
{
m_populate = enabled;
}
else
{
ss_dassert(strcmp(zName, SV_MAXSCALE_CACHE_USE) == 0);
m_use = enabled;
}
m_populate = enabled;
}
else
{
static const char FORMAT[] = "The variable %s can only have the values true/false/1/0";
int n = snprintf(NULL, 0, FORMAT, zName) + 1;
zMessage = create_bool_error_message(zName, pValue_begin, pValue_end);
}
zMessage = static_cast<char*>(MXS_MALLOC(n));
return zMessage;
}
if (zMessage)
{
sprintf(zMessage, FORMAT, zName);
}
char* CacheFilterSession::set_cache_use(const char* zName,
const char* pValue_begin,
const char* pValue_end)
{
ss_dassert(strcmp(SV_MAXSCALE_CACHE_USE, zName) == 0);
MXS_WARNING("Attempt to set the variable %s to the invalid value \"%.*s\".",
zName, n, pValue_begin);
char* zMessage = NULL;
bool enabled;
if (get_truth_value(pValue_begin, pValue_end, &enabled))
{
m_use = enabled;
}
else
{
zMessage = create_bool_error_message(zName, pValue_begin, pValue_end);
}
return zMessage;
}
char* CacheFilterSession::set_cache_soft_ttl(const char* zName,
const char* pValue_begin,
const char* pValue_end)
{
ss_dassert(strcmp(SV_MAXSCALE_CACHE_SOFT_TTL, zName) == 0);
char* zMessage = NULL;
uint32_t value;
if (get_uint32_value(pValue_begin, pValue_end, &value))
{
m_soft_ttl = value;
}
else
{
zMessage = create_uint32_error_message(zName, pValue_begin, pValue_end);
}
return zMessage;
}
char* CacheFilterSession::set_cache_hard_ttl(const char* zName,
const char* pValue_begin,
const char* pValue_end)
{
ss_dassert(strcmp(SV_MAXSCALE_CACHE_HARD_TTL, zName) == 0);
char* zMessage = NULL;
uint32_t value;
if (get_uint32_value(pValue_begin, pValue_end, &value))
{
m_hard_ttl = value;
}
else
{
zMessage = create_uint32_error_message(zName, pValue_begin, pValue_end);
}
return zMessage;
}
//static
char* CacheFilterSession::session_variable_handler(void* pContext,
const char* zName,
const char* pValue_begin,
const char* pValue_end)
char* CacheFilterSession::set_cache_populate(void* pContext,
const char* zName,
const char* pValue_begin,
const char* pValue_end)
{
CacheFilterSession* pThis = static_cast<CacheFilterSession*>(pContext);
return pThis->handle_session_variable(zName, pValue_begin, pValue_end);
return pThis->set_cache_populate(zName, pValue_begin, pValue_end);
}
//static
char* CacheFilterSession::set_cache_use(void* pContext,
const char* zName,
const char* pValue_begin,
const char* pValue_end)
{
CacheFilterSession* pThis = static_cast<CacheFilterSession*>(pContext);
return pThis->set_cache_use(zName, pValue_begin, pValue_end);
}
//static
char* CacheFilterSession::set_cache_soft_ttl(void* pContext,
const char* zName,
const char* pValue_begin,
const char* pValue_end)
{
CacheFilterSession* pThis = static_cast<CacheFilterSession*>(pContext);
return pThis->set_cache_soft_ttl(zName, pValue_begin, pValue_end);
}
//static
char* CacheFilterSession::set_cache_hard_ttl(void* pContext,
const char* zName,
const char* pValue_begin,
const char* pValue_end)
{
CacheFilterSession* pThis = static_cast<CacheFilterSession*>(pContext);
return pThis->set_cache_hard_ttl(zName, pValue_begin, pValue_end);
}

View File

@ -141,14 +141,35 @@ private:
routing_action_t route_COM_QUERY(GWBUF* pPacket);
routing_action_t route_SELECT(cache_action_t action, GWBUF* pPacket);
char* handle_session_variable(const char* zName,
const char* pValue_begin,
const char* pValue_end);
char* set_cache_populate(const char* zName,
const char* pValue_begin,
const char* pValue_end);
char* set_cache_use(const char* zName,
const char* pValue_begin,
const char* pValue_end);
char* set_cache_soft_ttl(const char* zName,
const char* pValue_begin,
const char* pValue_end);
char* set_cache_hard_ttl(const char* zName,
const char* pValue_begin,
const char* pValue_end);
static char* session_variable_handler(void* pContext,
const char* zName,
const char* pValue_begin,
const char* pValue_end);
static char* set_cache_populate(void* pContext,
const char* zName,
const char* pValue_begin,
const char* pValue_end);
static char* set_cache_use(void* pContext,
const char* zName,
const char* pValue_begin,
const char* pValue_end);
static char* set_cache_soft_ttl(void* pContext,
const char* zName,
const char* pValue_begin,
const char* pValue_end);
static char* set_cache_hard_ttl(void* pContext,
const char* zName,
const char* pValue_begin,
const char* pValue_end);
private:
CacheFilterSession(MXS_SESSION* pSession, Cache* pCache, char* zDefaultDb);