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:
		@ -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.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										226
									
								
								server/modules/filter/cache/cachefiltersession.cc
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										226
									
								
								server/modules/filter/cache/cachefiltersession.cc
									
									
									
									
										vendored
									
									
								
							@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user