diff --git a/server/modules/filter/cache/storage/storage_inmemory/CMakeLists.txt b/server/modules/filter/cache/storage/storage_inmemory/CMakeLists.txt new file mode 100644 index 000000000..93a39ea58 --- /dev/null +++ b/server/modules/filter/cache/storage/storage_inmemory/CMakeLists.txt @@ -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) diff --git a/server/modules/filter/cache/storage/storage_inmemory/inmemorystorage.cc b/server/modules/filter/cache/storage/storage_inmemory/inmemorystorage.cc new file mode 100644 index 000000000..874c68f2b --- /dev/null +++ b/server/modules/filter/cache/storage/storage_inmemory/inmemorystorage.cc @@ -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 +#include +#include +#include +#include +#include + +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(pquery), &n, fullnames); + + set 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::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(tag.data()); + SHA512(pdata, tag.length(), reinterpret_cast(pkey->data)); + + char *psql; + int length; + + modutil_extract_SQL(const_cast(pquery), &psql, &length); + + // Then we store the query itself in the second half of the key. + pdata = reinterpret_cast(psql); + SHA512(pdata, length, reinterpret_cast(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; +} diff --git a/server/modules/filter/cache/storage/storage_inmemory/inmemorystorage.h b/server/modules/filter/cache/storage/storage_inmemory/inmemorystorage.h new file mode 100644 index 000000000..d5bfeb610 --- /dev/null +++ b/server/modules/filter/cache/storage/storage_inmemory/inmemorystorage.h @@ -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 +#include +#include +#include +#include +#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 Value; + + struct Entry + { + Entry() + : time(0) + {} + + uint32_t time; + Value value; + }; + + typedef std::tr1::unordered_map Entries; + + std::string name_; + uint32_t ttl_; + Entries entries_; +}; diff --git a/server/modules/filter/cache/storage/storage_inmemory/inmemorystoragemt.cc b/server/modules/filter/cache/storage/storage_inmemory/inmemorystoragemt.cc new file mode 100644 index 000000000..b5497c27f --- /dev/null +++ b/server/modules/filter/cache/storage/storage_inmemory/inmemorystoragemt.cc @@ -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; +} diff --git a/server/modules/filter/cache/storage/storage_inmemory/inmemorystoragemt.h b/server/modules/filter/cache/storage/storage_inmemory/inmemorystoragemt.h new file mode 100644 index 000000000..0ad14d36e --- /dev/null +++ b/server/modules/filter/cache/storage/storage_inmemory/inmemorystoragemt.h @@ -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 +#include +#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_; +}; diff --git a/server/modules/filter/cache/storage/storage_inmemory/inmemorystoragest.cc b/server/modules/filter/cache/storage/storage_inmemory/inmemorystoragest.cc new file mode 100644 index 000000000..0b81dd799 --- /dev/null +++ b/server/modules/filter/cache/storage/storage_inmemory/inmemorystoragest.cc @@ -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); +} diff --git a/server/modules/filter/cache/storage/storage_inmemory/inmemorystoragest.h b/server/modules/filter/cache/storage/storage_inmemory/inmemorystoragest.h new file mode 100644 index 000000000..4dac01c5d --- /dev/null +++ b/server/modules/filter/cache/storage/storage_inmemory/inmemorystoragest.h @@ -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 +#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&); +}; diff --git a/server/modules/filter/cache/storage/storage_inmemory/storage_inmemory.cc b/server/modules/filter/cache/storage/storage_inmemory/storage_inmemory.cc new file mode 100644 index 000000000..b9cb1cd69 --- /dev/null +++ b/server/modules/filter/cache/storage/storage_inmemory/storage_inmemory.cc @@ -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 +#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(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(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(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(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(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(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(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; +} + +}