Cache: Add in memory storage

Storage implementation that simply uses std::unordered_map as
storage structure.
This commit is contained in:
Johan Wikman
2016-11-29 21:18:14 +02:00
parent b87ea735fb
commit 23bb7c4de7
8 changed files with 681 additions and 0 deletions

View File

@ -0,0 +1,10 @@
add_library(storage_inmemory SHARED
inmemorystorage.cc
inmemorystoragest.cc
inmemorystoragemt.cc
storage_inmemory.cc
)
target_link_libraries(storage_inmemory cache maxscale-common)
set_target_properties(storage_inmemory PROPERTIES VERSION "1.0.0")
set_target_properties(storage_inmemory PROPERTIES LINK_FLAGS -Wl,-z,defs)
install_module(storage_inmemory experimental)

View File

@ -0,0 +1,195 @@
/*
* 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.
*/
#define MXS_MODULE_NAME "storage_inmemory"
#include "inmemorystorage.h"
#include <openssl/sha.h>
#include <algorithm>
#include <set>
#include <maxscale/alloc.h>
#include <maxscale/modutil.h>
#include <maxscale/query_classifier.h>
using std::set;
using std::string;
namespace
{
const size_t INMEMORY_KEY_LENGTH = 2 * SHA512_DIGEST_LENGTH;
#if INMEMORY_KEY_LENGTH > CACHE_KEY_MAXLEN
#error storage_inmemory key is too long.
#endif
}
InMemoryStorage::InMemoryStorage(const string& name,
uint32_t ttl)
: name_(name)
, ttl_(ttl)
{
}
InMemoryStorage::~InMemoryStorage()
{
}
cache_result_t InMemoryStorage::get_key(const char* zdefault_db, const GWBUF* pquery, CACHE_KEY* pkey)
{
ss_dassert(GWBUF_IS_CONTIGUOUS(pquery));
int n;
bool fullnames = true;
char** pztables = qc_get_table_names(const_cast<GWBUF*>(pquery), &n, fullnames);
set<string> dbs; // Elements in set are sorted.
for (int i = 0; i < n; ++i)
{
char *ztable = pztables[i];
char *zdot = strchr(ztable, '.');
if (zdot)
{
*zdot = 0;
dbs.insert(ztable);
}
else if (zdefault_db)
{
// If zdefault_db is NULL, then there will be a table for which we
// do not know the database. However, that will fail in the server,
// so nothing will be stored.
dbs.insert(zdefault_db);
}
MXS_FREE(ztable);
}
MXS_FREE(pztables);
// dbs now contain each accessed database in sorted order. Now copy them to a single string.
string tag;
for (set<string>::const_iterator i = dbs.begin(); i != dbs.end(); ++i)
{
tag.append(*i);
}
memset(pkey->data, 0, CACHE_KEY_MAXLEN);
const unsigned char* pdata;
// We store the databases in the first half of the key. That will ensure that
// identical queries targeting different default databases will not clash.
// This will also mean that entries related to the same databases will
// be placed near each other.
pdata = reinterpret_cast<const unsigned char*>(tag.data());
SHA512(pdata, tag.length(), reinterpret_cast<unsigned char*>(pkey->data));
char *psql;
int length;
modutil_extract_SQL(const_cast<GWBUF*>(pquery), &psql, &length);
// Then we store the query itself in the second half of the key.
pdata = reinterpret_cast<const unsigned char*>(psql);
SHA512(pdata, length, reinterpret_cast<unsigned char*>(pkey->data) + SHA512_DIGEST_LENGTH);
return CACHE_RESULT_OK;
}
cache_result_t InMemoryStorage::do_get_value(const CACHE_KEY& key, uint32_t flags, GWBUF** ppresult)
{
cache_result_t result = CACHE_RESULT_NOT_FOUND;
Entries::iterator i = entries_.find(key);
if (i != entries_.end())
{
Entry& entry = i->second;
uint32_t now = time(NULL);
bool is_stale = (now - entry.time > ttl_);
if (!is_stale || ((flags & CACHE_FLAGS_INCLUDE_STALE) != 0))
{
size_t length = entry.value.size();
*ppresult = gwbuf_alloc(length);
if (*ppresult)
{
memcpy(GWBUF_DATA(*ppresult), entry.value.data(), length);
if (is_stale)
{
result = CACHE_RESULT_STALE;
}
else
{
result = CACHE_RESULT_OK;
}
}
}
else
{
MXS_NOTICE("Cache item is stale, not using.");
result = CACHE_RESULT_NOT_FOUND;
}
}
else
{
result = CACHE_RESULT_NOT_FOUND;
}
return result;
}
cache_result_t InMemoryStorage::do_put_value(const CACHE_KEY& key, const GWBUF* pvalue)
{
ss_dassert(GWBUF_IS_CONTIGUOUS(pvalue));
const uint8_t* pdata = GWBUF_DATA(pvalue);
size_t size = GWBUF_LENGTH(pvalue);
Entry& entry = entries_[key];
if (size < entry.value.capacity())
{
// If the needed value is less than what is currently stored,
// we shrink the buffer so as not to waste space.
Value value(size);
entry.value.swap(value);
}
else
{
entry.value.resize(size);
}
copy(GWBUF_DATA(pvalue), GWBUF_DATA(pvalue) + size, entry.value.begin());
entry.time = time(NULL);
return CACHE_RESULT_OK;
}
cache_result_t InMemoryStorage::do_del_value(const CACHE_KEY& key)
{
Entries::iterator i = entries_.find(key);
if (i != entries_.end())
{
entries_.erase(i);
}
return i != entries_.end() ? CACHE_RESULT_OK : CACHE_RESULT_NOT_FOUND;
}

View File

@ -0,0 +1,62 @@
#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 <memory>
#include <string>
#include <vector>
#include <tr1/unordered_map>
#include "../../cachefilter.h"
class InMemoryStorage
{
public:
virtual ~InMemoryStorage();
cache_result_t get_key(const char* zdefault_db, const GWBUF* pquery, CACHE_KEY* pkey);
virtual cache_result_t get_value(const CACHE_KEY& key, uint32_t flags, GWBUF** ppresult) = 0;
virtual cache_result_t put_value(const CACHE_KEY& key, const GWBUF* pvalue) = 0;
virtual cache_result_t del_value(const CACHE_KEY& key) = 0;
protected:
InMemoryStorage(const std::string& name, uint32_t ttl);
cache_result_t do_get_value(const CACHE_KEY& key, uint32_t flags, GWBUF** ppresult);
cache_result_t do_put_value(const CACHE_KEY& key, const GWBUF* pvalue);
cache_result_t do_del_value(const CACHE_KEY& key);
private:
InMemoryStorage(const InMemoryStorage&);
InMemoryStorage& operator = (const InMemoryStorage&);
private:
typedef std::vector<uint8_t> Value;
struct Entry
{
Entry()
: time(0)
{}
uint32_t time;
Value value;
};
typedef std::tr1::unordered_map<CACHE_KEY, Entry> Entries;
std::string name_;
uint32_t ttl_;
Entries entries_;
};

View File

@ -0,0 +1,60 @@
/*
* 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.
*/
#define MXS_MODULE_NAME "storage_inmemory"
#include "inmemorystoragemt.h"
InMemoryStorageMT::InMemoryStorageMT(const std::string& name, uint32_t ttl)
: InMemoryStorage(name, ttl)
{
spinlock_init(&lock_);
}
InMemoryStorageMT::~InMemoryStorageMT()
{
}
// static
InMemoryStorageMT* InMemoryStorageMT::create(const std::string& name,
uint32_t ttl,
int argc, char* argv[])
{
return new InMemoryStorageMT(name, ttl);
}
cache_result_t InMemoryStorageMT::get_value(const CACHE_KEY& key, uint32_t flags, GWBUF** ppresult)
{
spinlock_acquire(&lock_);
cache_result_t result = do_get_value(key, flags, ppresult);
spinlock_release(&lock_);
return result;
}
cache_result_t InMemoryStorageMT::put_value(const CACHE_KEY& key, const GWBUF* pvalue)
{
spinlock_acquire(&lock_);
cache_result_t result = do_put_value(key, pvalue);
spinlock_release(&lock_);
return result;
}
cache_result_t InMemoryStorageMT::del_value(const CACHE_KEY& key)
{
spinlock_acquire(&lock_);
cache_result_t result = do_del_value(key);
spinlock_release(&lock_);
return result;
}

View File

@ -0,0 +1,39 @@
#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/spinlock.h>
#include "inmemorystorage.h"
class InMemoryStorageMT : public InMemoryStorage
{
public:
~InMemoryStorageMT();
static InMemoryStorageMT* create(const std::string& name, uint32_t ttl, int argc, char* argv[]);
cache_result_t get_value(const CACHE_KEY& key, uint32_t flags, GWBUF** ppresult);
cache_result_t put_value(const CACHE_KEY& key, const GWBUF* pvalue);
cache_result_t del_value(const CACHE_KEY& key);
private:
InMemoryStorageMT(const std::string& name, uint32_t ttl);
private:
InMemoryStorageMT(const InMemoryStorageMT&);
InMemoryStorageMT& operator = (const InMemoryStorageMT&);
private:
SPINLOCK lock_;
};

View File

@ -0,0 +1,47 @@
/*
* 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.
*/
#define MXS_MODULE_NAME "storage_inmemory"
#include "inmemorystoragest.h"
InMemoryStorageST::InMemoryStorageST(const std::string& name, uint32_t ttl)
: InMemoryStorage(name, ttl)
{
}
InMemoryStorageST::~InMemoryStorageST()
{
}
// static
InMemoryStorageST* InMemoryStorageST::create(const std::string& name,
uint32_t ttl,
int argc, char* argv[])
{
return new InMemoryStorageST(name, ttl);
}
cache_result_t InMemoryStorageST::get_value(const CACHE_KEY& key, uint32_t flags, GWBUF** ppresult)
{
return do_get_value(key, flags, ppresult);
}
cache_result_t InMemoryStorageST::put_value(const CACHE_KEY& key, const GWBUF* pvalue)
{
return do_put_value(key, pvalue);
}
cache_result_t InMemoryStorageST::del_value(const CACHE_KEY& key)
{
return do_del_value(key);
}

View File

@ -0,0 +1,35 @@
#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 "inmemorystorage.h"
class InMemoryStorageST : public InMemoryStorage
{
public:
~InMemoryStorageST();
static InMemoryStorageST* create(const std::string& name, uint32_t ttl, int argc, char* argv[]);
cache_result_t get_value(const CACHE_KEY& key, uint32_t flags, GWBUF** ppresult);
cache_result_t put_value(const CACHE_KEY& key, const GWBUF* pvalue);
cache_result_t del_value(const CACHE_KEY& key);
private:
InMemoryStorageST(const std::string& name, uint32_t ttl);
private:
InMemoryStorageST(const InMemoryStorageST&);
InMemoryStorageST& operator = (const InMemoryStorageST&);
};

View File

@ -0,0 +1,233 @@
/*
* 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.
*/
#define MXS_MODULE_NAME "storage_inmemory"
#include <inttypes.h>
#include "../../cache_storage_api.h"
#include "inmemorystoragest.h"
#include "inmemorystoragemt.h"
namespace
{
bool initialize(uint32_t* pcapabilities)
{
*pcapabilities = CACHE_STORAGE_CAP_ST;
*pcapabilities = CACHE_STORAGE_CAP_MT;
return true;
}
CACHE_STORAGE* createInstance(cache_thread_model_t model,
const char* zname,
uint32_t ttl,
uint32_t max_count,
uint64_t max_size,
int argc, char* argv[])
{
ss_dassert(zname);
CACHE_STORAGE* pStorage = 0;
if (max_count != 0)
{
MXS_WARNING("A maximum item count of %" PRIu32 " specified, although 'storage_inMemory' "
"does not enforce such a limit.", max_count);
}
if (max_size != 0)
{
MXS_WARNING("A maximum size of %" PRIu64 " specified, although 'storage_inMemory' "
"does not enforce such a limit.", max_size);
}
try
{
switch (model)
{
case CACHE_THREAD_MODEL_ST:
pStorage = reinterpret_cast<CACHE_STORAGE*>(InMemoryStorageST::create(zname, ttl, argc, argv));
break;
default:
MXS_ERROR("Unknown thread model %d, creating multi-thread aware storage.", (int)model);
case CACHE_THREAD_MODEL_MT:
pStorage = reinterpret_cast<CACHE_STORAGE*>(InMemoryStorageST::create(zname, ttl, argc, argv));
}
MXS_NOTICE("Storage module created.");
}
catch (const std::bad_alloc&)
{
MXS_OOM();
}
catch (const std::exception& x)
{
MXS_ERROR("Standard exception caught: %s", x.what());
}
catch (...)
{
MXS_ERROR("Unknown exception caught.");
}
return pStorage;
}
void freeInstance(CACHE_STORAGE* pinstance)
{
delete reinterpret_cast<InMemoryStorage*>(pinstance);
}
cache_result_t getKey(CACHE_STORAGE* pstorage,
const char* zdefault_db,
const GWBUF* pquery,
CACHE_KEY* pkey)
{
ss_dassert(pstorage);
// zdefault_db may be NULL.
ss_dassert(pquery);
ss_dassert(pkey);
cache_result_t result = CACHE_RESULT_ERROR;
try
{
result = reinterpret_cast<InMemoryStorage*>(pstorage)->get_key(zdefault_db, pquery, pkey);
}
catch (const std::bad_alloc&)
{
MXS_OOM();
}
catch (const std::exception& x)
{
MXS_ERROR("Standard exception caught: %s", x.what());
}
catch (...)
{
MXS_ERROR("Unknown exception caught.");
}
return result;
}
cache_result_t getValue(CACHE_STORAGE* pstorage,
const CACHE_KEY* pkey,
uint32_t flags,
GWBUF** ppresult)
{
ss_dassert(pstorage);
ss_dassert(pkey);
ss_dassert(ppresult);
cache_result_t result = CACHE_RESULT_ERROR;
try
{
result = reinterpret_cast<InMemoryStorage*>(pstorage)->get_value(*pkey, flags, ppresult);
}
catch (const std::bad_alloc&)
{
MXS_OOM();
}
catch (const std::exception& x)
{
MXS_ERROR("Standard exception caught: %s", x.what());
}
catch (...)
{
MXS_ERROR("Unknown exception caught.");
}
return result;
}
cache_result_t putValue(CACHE_STORAGE* pstorage,
const CACHE_KEY* pkey,
const GWBUF* pvalue)
{
ss_dassert(pstorage);
ss_dassert(pkey);
ss_dassert(pvalue);
cache_result_t result = CACHE_RESULT_ERROR;
try
{
result = reinterpret_cast<InMemoryStorage*>(pstorage)->put_value(*pkey, pvalue);
}
catch (const std::bad_alloc&)
{
MXS_OOM();
}
catch (const std::exception& x)
{
MXS_ERROR("Standard exception caught: %s", x.what());
}
catch (...)
{
MXS_ERROR("Unknown exception caught.");
}
return result;
}
cache_result_t delValue(CACHE_STORAGE* pstorage,
const CACHE_KEY* pkey)
{
ss_dassert(pstorage);
ss_dassert(pkey);
cache_result_t result = CACHE_RESULT_ERROR;
try
{
result = reinterpret_cast<InMemoryStorage*>(pstorage)->del_value(*pkey);
}
catch (const std::bad_alloc&)
{
MXS_OOM();
}
catch (const std::exception& x)
{
MXS_ERROR("Standard exception caught: %s", x.what());
}
catch (...)
{
MXS_ERROR("Unknown exception caught.");
}
return result;
}
}
extern "C"
{
CACHE_STORAGE_API* CacheGetStorageAPI()
{
static CACHE_STORAGE_API api =
{
initialize,
createInstance,
freeInstance,
getKey,
getValue,
putValue,
delValue,
};
return &api;
}
}