Cache: Add simple cache

A simple cache is one that does not transparently use multiple
storages in the background. That will be the case when a separate
cache per thread is used. Both CacheST and CacheMT are now derived
from CacheSimple.

A fair amount of what used to be in Cache has now been moved to
CacheSimple. What remains is what surely is common for all cache
types.
This commit is contained in:
Johan Wikman
2016-11-25 16:13:45 +02:00
parent 5bae5b8b8d
commit 124453b756
9 changed files with 258 additions and 151 deletions

View File

@ -1,5 +1,5 @@
if (JANSSON_FOUND)
add_library(cache SHARED cache.cc cachefilter.cc cachemt.cc cachest.cc rules.cc sessioncache.cc storage.cc storagefactory.cc)
add_library(cache SHARED cache.cc cachefilter.cc cachemt.cc cachesimple.cc cachest.cc rules.cc sessioncache.cc storage.cc storagefactory.cc)
target_link_libraries(cache maxscale-common jansson)
set_target_properties(cache PROPERTIES VERSION "1.0.0")
set_target_properties(cache PROPERTIES LINK_FLAGS -Wl,-z,defs)

View File

@ -19,76 +19,30 @@
#include "storagefactory.h"
#include "storage.h"
// Initial size of hashtable used for storing keys of queries that
// are being fetches.
#define CACHE_PENDING_ITEMS 50
/**
* Hashes a cache key to an integer.
*
* @param key Pointer to cache key.
*
* @returns Corresponding integer hash.
*/
static int hash_of_key(const void* key)
{
int hash = 0;
const char* i = (const char*)key;
const char* end = i + CACHE_KEY_MAXLEN;
while (i < end)
{
int c = *i;
hash = c + (hash << 6) + (hash << 16) - hash;
++i;
}
return hash;
}
static int hashfn(const void* address)
{
// TODO: Hash the address; pointers are not evenly distributed.
return (long)address;
}
static int hashcmp(const void* address1, const void* address2)
{
return (long)address2 - (long)address1;
}
Cache::Cache(const char* zName,
CACHE_CONFIG& config,
CACHE_RULES* pRules,
StorageFactory* pFactory,
Storage* pStorage,
HASHTABLE* pPending)
StorageFactory* pFactory)
: m_zName(zName)
, m_config(config)
, m_pRules(pRules)
, m_pFactory(pFactory)
, m_pStorage(pStorage)
, m_pPending(pPending)
{
cache_config_reset(config);
}
Cache::~Cache()
{
// TODO: Free everything.
ss_dassert(false);
cache_rules_free(m_pRules);
delete m_pFactory;
}
//static
bool Cache::Create(const CACHE_CONFIG& config,
CACHE_RULES** ppRules,
StorageFactory** ppFactory,
HASHTABLE** ppPending)
StorageFactory** ppFactory)
{
CACHE_RULES* pRules = NULL;
HASHTABLE* pPending = NULL;
StorageFactory* pFactory = NULL;
if (config.rules)
@ -102,31 +56,24 @@ bool Cache::Create(const CACHE_CONFIG& config,
if (pRules)
{
pPending = hashtable_alloc(CACHE_PENDING_ITEMS, hashfn, hashcmp);
pFactory = StorageFactory::Open(config.storage);
if (pPending)
if (!pFactory)
{
pFactory = StorageFactory::Open(config.storage);
if (!pFactory)
{
MXS_ERROR("Could not open storage factory '%s'.", config.storage);
}
MXS_ERROR("Could not open storage factory '%s'.", config.storage);
}
}
bool rv = (pRules && pPending && pFactory);
bool rv = (pRules && pFactory);
if (rv)
{
*ppRules = pRules;
*ppPending = pPending;
*ppFactory = pFactory;
}
else
{
cache_rules_free(pRules);
hashtable_free(pPending);
delete pFactory;
}
@ -143,56 +90,4 @@ bool Cache::shouldUse(const SESSION* pSession)
return cache_rules_should_use(m_pRules, pSession);
}
cache_result_t Cache::getKey(const char* zDefaultDb,
const GWBUF* pQuery,
CACHE_KEY* pKey)
{
return m_pStorage->getKey(zDefaultDb, pQuery, pKey);
}
cache_result_t Cache::getValue(const CACHE_KEY& key,
uint32_t flags,
GWBUF** ppValue)
{
return m_pStorage->getValue(key, flags, ppValue);
}
cache_result_t Cache::putValue(const CACHE_KEY& key,
const GWBUF* pValue)
{
return m_pStorage->putValue(key, pValue);
}
cache_result_t Cache::delValue(const CACHE_KEY& key)
{
return m_pStorage->delValue(key);
}
// protected
long Cache::hashOfKey(const CACHE_KEY& key)
{
return hash_of_key(key.data);
}
// protected
bool Cache::mustRefresh(long key, const SessionCache* pSessionCache)
{
void *pValue = hashtable_fetch(m_pPending, (void*)key);
if (!pValue)
{
// It's not being fetched, so we make a note that we are.
hashtable_add(m_pPending, (void*)key, (void*)pSessionCache);
}
return !pValue;
}
// protected
void Cache::refreshed(long key, const SessionCache* pSessionCache)
{
ss_dassert(hashtable_fetch(m_pPending, (void*)key) == pSessionCache);
ss_debug(int n =) hashtable_delete(m_pPending, (void*)key);
ss_dassert(n == 1);
}

View File

@ -14,7 +14,6 @@
#include <maxscale/cdefs.h>
#include <maxscale/buffer.h>
#include <maxscale/filter.h>
#include <maxscale/session.h>
#include "cachefilter.h"
#include "cache_storage_api.h"
@ -24,7 +23,9 @@ class SessionCache;
class Cache
{
public:
~Cache();
virtual ~Cache();
const CACHE_CONFIG& config() const { return m_config; }
/**
* Returns whether the results of a particular query should be stored.
@ -63,44 +64,31 @@ public:
*/
virtual void refreshed(const CACHE_KEY& key, const SessionCache* pSessionCache) = 0;
const CACHE_CONFIG& config() const { return m_config; }
virtual cache_result_t getKey(const char* zDefaultDb, const GWBUF* pQuery, CACHE_KEY* pKey) = 0;
cache_result_t getKey(const char* zDefaultDb, const GWBUF* pQuery, CACHE_KEY* pKey);
virtual cache_result_t getValue(const CACHE_KEY& key, uint32_t flags, GWBUF** ppValue) = 0;
cache_result_t getValue(const CACHE_KEY& key, uint32_t flags, GWBUF** ppValue);
virtual cache_result_t putValue(const CACHE_KEY& key, const GWBUF* pValue) = 0;
cache_result_t putValue(const CACHE_KEY& key, const GWBUF* pValue);
cache_result_t delValue(const CACHE_KEY& key);
virtual cache_result_t delValue(const CACHE_KEY& key) = 0;
protected:
Cache(const char* zName,
CACHE_CONFIG& config,
CACHE_RULES* pRules,
StorageFactory* pFactory,
Storage* pStorage,
HASHTABLE* pPending);
StorageFactory* pFactory);
static bool Create(const CACHE_CONFIG& config,
CACHE_RULES** ppRules,
StorageFactory** ppFactory,
HASHTABLE** ppPending);
long hashOfKey(const CACHE_KEY& key);
bool mustRefresh(long key, const SessionCache* pSessionCache);
void refreshed(long key, const SessionCache* pSessionCache);
StorageFactory** ppFactory);
private:
Cache(const Cache&);
Cache& operator = (const Cache&);
protected:
const char* m_zName; // The name of the instance; the section name in the config.
CACHE_CONFIG m_config; // The configuration of the cache instance.
CACHE_RULES* m_pRules; // The rules of the cache instance.
StorageFactory* m_pFactory; // The storage factory.
Storage* m_pStorage; // The storage instance to use.
HASHTABLE* m_pPending; // Pending items; being fetched from the backend.
const char* m_zName; // The name of the instance; the section name in the config.
CACHE_CONFIG m_config; // The configuration of the cache instance.
CACHE_RULES* m_pRules; // The rules of the cache instance.
StorageFactory* m_pFactory; // The storage factory.
};

View File

@ -21,7 +21,7 @@ CacheMT::CacheMT(const char* zName,
StorageFactory* pFactory,
Storage* pStorage,
HASHTABLE* pPending)
: Cache(zName, config, pRules, pFactory, pStorage, pPending)
: CacheSimple(zName, config, pRules, pFactory, pStorage, pPending)
{
spinlock_init(&m_lockPending);
}
@ -38,7 +38,7 @@ CacheMT* CacheMT::Create(const char* zName, CACHE_CONFIG& config)
HASHTABLE* pPending = NULL;
StorageFactory* pFactory = NULL;
if (Cache::Create(config, &pRules, &pFactory, &pPending))
if (CacheSimple::Create(config, &pRules, &pFactory, &pPending))
{
uint32_t ttl = config.ttl;
int argc = config.storage_argc;
@ -73,7 +73,7 @@ bool CacheMT::mustRefresh(const CACHE_KEY& key, const SessionCache* pSessionCach
long k = hashOfKey(key);
spinlock_acquire(&m_lockPending);
bool rv = Cache::mustRefresh(k, pSessionCache);
bool rv = CacheSimple::mustRefresh(k, pSessionCache);
spinlock_release(&m_lockPending);
return rv;
@ -84,6 +84,6 @@ void CacheMT::refreshed(const CACHE_KEY& key, const SessionCache* pSessionCache
long k = hashOfKey(key);
spinlock_acquire(&m_lockPending);
Cache::refreshed(k, pSessionCache);
CacheSimple::refreshed(k, pSessionCache);
spinlock_release(&m_lockPending);
}

View File

@ -14,9 +14,9 @@
#include <maxscale/cdefs.h>
#include <maxscale/spinlock.h>
#include "cache.h"
#include "cachesimple.h"
class CacheMT : public Cache
class CacheMT : public CacheSimple
{
public:
~CacheMT();

View File

@ -0,0 +1,164 @@
/*
* 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/bsl.
*
* Change Date: 2019-07-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 "cachesimple.h"
#include "storage.h"
#include "storagefactory.h"
namespace
{
// Initial size of hashtable used for storing keys of queries that
// are being fetches.
const size_t CACHE_PENDING_ITEMS = 50;
/**
* Hashes a cache key to an integer.
*
* @param key Pointer to cache key.
*
* @returns Corresponding integer hash.
*/
int hash_of_key(const CACHE_KEY& key)
{
int hash = 0;
const char* i = key.data;
const char* end = i + CACHE_KEY_MAXLEN;
while (i < end)
{
int c = *i;
hash = c + (hash << 6) + (hash << 16) - hash;
++i;
}
return hash;
}
int hashfn(const void* address)
{
// TODO: Hash the address; pointers are not evenly distributed.
return (long)address;
}
int hashcmp(const void* address1, const void* address2)
{
return (long)address2 - (long)address1;
}
}
CacheSimple::CacheSimple(const char* zName,
CACHE_CONFIG& config,
CACHE_RULES* pRules,
StorageFactory* pFactory,
Storage* pStorage,
HASHTABLE* pPending)
: Cache(zName, config, pRules, pFactory)
, m_pStorage(pStorage)
, m_pPending(pPending)
{
}
CacheSimple::~CacheSimple()
{
delete m_pStorage;
hashtable_free(m_pPending);
}
// static protected
bool CacheSimple::Create(const CACHE_CONFIG& config,
CACHE_RULES** ppRules,
StorageFactory** ppFactory,
HASHTABLE** ppPending)
{
int rv = false;
CACHE_RULES* pRules = NULL;
StorageFactory* pFactory = NULL;
HASHTABLE* pPending = NULL;
if (Cache::Create(config, &pRules, &pFactory))
{
pPending = hashtable_alloc(CACHE_PENDING_ITEMS, hashfn, hashcmp);
if (pPending)
{
*ppRules = pRules;
*ppPending = pPending;
*ppFactory = pFactory;
}
else
{
cache_rules_free(pRules);
delete pFactory;
}
}
return pPending != NULL;
}
cache_result_t CacheSimple::getKey(const char* zDefaultDb,
const GWBUF* pQuery,
CACHE_KEY* pKey)
{
return m_pStorage->getKey(zDefaultDb, pQuery, pKey);
}
cache_result_t CacheSimple::getValue(const CACHE_KEY& key,
uint32_t flags,
GWBUF** ppValue)
{
return m_pStorage->getValue(key, flags, ppValue);
}
cache_result_t CacheSimple::putValue(const CACHE_KEY& key,
const GWBUF* pValue)
{
return m_pStorage->putValue(key, pValue);
}
cache_result_t CacheSimple::delValue(const CACHE_KEY& key)
{
return m_pStorage->delValue(key);
}
// protected
long CacheSimple::hashOfKey(const CACHE_KEY& key)
{
return hash_of_key(key);
}
// protected
bool CacheSimple::mustRefresh(long key, const SessionCache* pSessionCache)
{
void *pValue = hashtable_fetch(m_pPending, (void*)key);
if (!pValue)
{
// It's not being fetched, so we make a note that we are.
hashtable_add(m_pPending, (void*)key, (void*)pSessionCache);
}
return !pValue;
}
// protected
void CacheSimple::refreshed(long key, const SessionCache* pSessionCache)
{
ss_dassert(hashtable_fetch(m_pPending, (void*)key) == pSessionCache);
ss_debug(int n =) hashtable_delete(m_pPending, (void*)key);
ss_dassert(n == 1);
}

View File

@ -0,0 +1,60 @@
#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/bsl.
*
* Change Date: 2019-07-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/cdefs.h>
#include <maxscale/hashtable.h>
#include "cache.h"
class Storage;
class CacheSimple : public Cache
{
public:
~CacheSimple();
protected:
CacheSimple(const char* zName,
CACHE_CONFIG& config,
CACHE_RULES* pRules,
StorageFactory* pFactory,
Storage* pStorage,
HASHTABLE* pPending);
static bool Create(const CACHE_CONFIG& config,
CACHE_RULES** ppRules,
StorageFactory** ppFactory,
HASHTABLE** ppPending);
cache_result_t getKey(const char* zDefaultDb, const GWBUF* pQuery, CACHE_KEY* pKey);
cache_result_t getValue(const CACHE_KEY& key, uint32_t flags, GWBUF** ppValue);
cache_result_t putValue(const CACHE_KEY& key, const GWBUF* pValue);
cache_result_t delValue(const CACHE_KEY& key);
long hashOfKey(const CACHE_KEY& key);
bool mustRefresh(long key, const SessionCache* pSessionCache);
void refreshed(long key, const SessionCache* pSessionCache);
private:
CacheSimple(const Cache&);
CacheSimple& operator = (const CacheSimple&);
protected:
Storage* m_pStorage; // The storage instance to use.
HASHTABLE* m_pPending; // Pending items; being fetched from the backend.
};

View File

@ -21,7 +21,7 @@ CacheST::CacheST(const char* zName,
StorageFactory* pFactory,
Storage* pStorage,
HASHTABLE* pPending)
: Cache(zName, config, pRules, pFactory, pStorage, pPending)
: CacheSimple(zName, config, pRules, pFactory, pStorage, pPending)
{
}
@ -37,7 +37,7 @@ CacheST* CacheST::Create(const char* zName, CACHE_CONFIG& config)
HASHTABLE* pPending = NULL;
StorageFactory* pFactory = NULL;
if (Cache::Create(config, &pRules, &pFactory, &pPending))
if (CacheSimple::Create(config, &pRules, &pFactory, &pPending))
{
uint32_t ttl = config.ttl;
int argc = config.storage_argc;
@ -71,12 +71,12 @@ bool CacheST::mustRefresh(const CACHE_KEY& key, const SessionCache* pSessionCach
{
long k = hashOfKey(key);
return Cache::mustRefresh(k, pSessionCache);
return CacheSimple::mustRefresh(k, pSessionCache);
}
void CacheST::refreshed(const CACHE_KEY& key, const SessionCache* pSessionCache)
{
long k = hashOfKey(key);
Cache::refreshed(k, pSessionCache);
CacheSimple::refreshed(k, pSessionCache);
}

View File

@ -13,9 +13,9 @@
*/
#include <maxscale/cdefs.h>
#include "cache.h"
#include "cachesimple.h"
class CacheST : public Cache
class CacheST : public CacheSimple
{
public:
~CacheST();