diff --git a/server/modules/filter/cache/CMakeLists.txt b/server/modules/filter/cache/CMakeLists.txt index 94ba9e012..b085753ef 100644 --- a/server/modules/filter/cache/CMakeLists.txt +++ b/server/modules/filter/cache/CMakeLists.txt @@ -1,5 +1,20 @@ if (JANSSON_FOUND) - add_library(cache SHARED cache.cc cachefilter.cc cachemt.cc cachept.cc cachesimple.cc cachest.cc rules.cc sessioncache.cc storage.cc storagefactory.cc storagereal.cc) + add_library(cache SHARED + cache.cc + cachefilter.cc + cachemt.cc + cachept.cc + cachesimple.cc + cachest.cc + lrustorage.cc + lrustoragemt.cc + lrustoragest.cc + rules.cc + sessioncache.cc + storage.cc + storagefactory.cc + storagereal.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) diff --git a/server/modules/filter/cache/lrustorage.cc b/server/modules/filter/cache/lrustorage.cc new file mode 100644 index 000000000..72e1de16f --- /dev/null +++ b/server/modules/filter/cache/lrustorage.cc @@ -0,0 +1,314 @@ +/* + * 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 "lrustorage.h" + + +LRUStorage::LRUStorage(Storage* pstorage, size_t max_count, size_t max_size) + : pstorage_(pstorage) + , max_count_(max_count) + , max_size_(max_size) + , count_(0) + , size_(0) + , phead_(NULL) + , ptail_(NULL) +{ +} + +LRUStorage::~LRUStorage() +{ +} + +cache_result_t LRUStorage::get_key(const char* zdefault_db, + const GWBUF* pquery, + CACHE_KEY* pkey) +{ + return pstorage_->get_key(zdefault_db, pquery, pkey); +} + +cache_result_t LRUStorage::do_get_value(const CACHE_KEY& key, + uint32_t flags, + GWBUF** ppvalue) +{ + NodesPerKey::iterator i = nodes_per_key_.find(key); + bool existed = (i != nodes_per_key_.end()); + + cache_result_t result = pstorage_->get_value(key, flags, ppvalue); + + if (result == CACHE_RESULT_OK) + { + if (existed) + { + if (ptail_ == i->second) + { + ptail_ = i->second->next(); + } + + phead_ = i->second->prepend(phead_); + } + else + { + MXS_ERROR("Item found in storage, but not in key mapping."); + } + } + + return result; +} + +cache_result_t LRUStorage::do_put_value(const CACHE_KEY& key, + const GWBUF* pvalue) +{ + cache_result_t result = CACHE_RESULT_ERROR; + + size_t value_size = GWBUF_LENGTH(pvalue); + size_t new_size = size_ + value_size; + + Node* pnode = NULL; + + NodesPerKey::iterator i = nodes_per_key_.find(key); + bool existed = (i != nodes_per_key_.end()); + + if (existed) + { + // TODO: Also in this case max_size_ needs to be honoured. + pnode = i->second; + } + else + { + if ((new_size > max_size_) || (count_ == max_count_)) + { + if (new_size > max_size_) + { + MXS_NOTICE("New size %lu > max size %lu. Removing least recently used.", + new_size, max_size_); + + pnode = free_lru(value_size); + } + else + { + ss_dassert(count_ == max_count_); + MXS_NOTICE("Max count %lu reached, removing least recently used.", max_count_); + pnode = free_lru(); + } + } + else + { + pnode = new (std::nothrow) Node; + } + + if (pnode) + { + try + { + std::pair + rv = nodes_per_key_.insert(std::make_pair(key, pnode)); + ss_dassert(rv.second); + + i = rv.first; + } + catch (const std::exception& x) + { + delete pnode; + pnode = NULL; + result = CACHE_RESULT_OUT_OF_RESOURCES; + } + } + } + + if (pnode) + { + result = pstorage_->put_value(key, pvalue); + + if (result == CACHE_RESULT_OK) + { + if (existed) + { + size_ -= pnode->size(); + } + else + { + ++count_; + } + + pnode->reset(&i->first, value_size); + size_ += pnode->size(); + + if (ptail_ == pnode) + { + ptail_ = pnode->prev(); + } + + phead_ = pnode->prepend(phead_); + + if (!ptail_) + { + ptail_ = phead_; + } + } + else if (!existed) + { + MXS_ERROR("Could not put a value to the storage."); + nodes_per_key_.erase(i); + delete pnode; + } + } + + return result; +} + +cache_result_t LRUStorage::do_del_value(const CACHE_KEY& key) +{ + NodesPerKey::iterator i = nodes_per_key_.find(key); + + cache_result_t result = pstorage_->del_value(key); + + if (result == CACHE_RESULT_OK) + { + if (i == nodes_per_key_.end()) + { + Node* pnode = i->second; + + ss_dassert(size_ > pnode->size()); + ss_dassert(count_ > 0); + + size_ -= pnode->size(); + --count_; + + phead_ = pnode->remove(); + delete pnode; + + if (!phead_) + { + ptail_ = NULL; + } + + nodes_per_key_.erase(i); + } + else + { + MXS_ERROR("Key was found from storage, but not from LRU register."); + } + } + + return result; +} + +/** + * Free the data associated with the least recently used node. + * + * @return The node itself, for reuse. + */ +LRUStorage::Node* LRUStorage::free_lru() +{ + ss_dassert(ptail_); + + Node* pnode = NULL; + + if (free_node_data(ptail_)) + { + pnode = ptail_; + } + + return pnode; +} + +/** + * Free the data associated with sufficient number of least recently used nodes, + * to make the required space available. + * + * @return The last node whose data was freed, for reuse. + */ +LRUStorage::Node* LRUStorage::free_lru(size_t needed_space) +{ + Node* pnode = NULL; + + size_t freed_space = 0; + bool error = false; + + while (!error && ptail_ && (freed_space < needed_space)) + { + size_t size = ptail_->size(); + + if (free_node_data(ptail_)) + { + freed_space += size; + + pnode = ptail_; + ptail_ = ptail_->remove(); + + if (freed_space < needed_space) + { + delete pnode; + pnode = NULL; + } + } + else + { + error = true; + } + } + + if (pnode) + { + pnode->reset(); + } + + return pnode; +} + +/** + * Free the data associated with a node. + * + * @return True, if the data could be freed, false otherwise. + */ +bool LRUStorage::free_node_data(Node* pnode) +{ + bool success = true; + + const CACHE_KEY* pkey = pnode->key(); + ss_dassert(pkey); + + NodesPerKey::iterator i = nodes_per_key_.find(*pkey); + + if (i != nodes_per_key_.end()) + { + MXS_ERROR("Item in LRU list was not found in key mapping."); + } + + cache_result_t result = pstorage_->del_value(*pkey); + + switch (result) + { + case CACHE_RESULT_NOT_FOUND: + MXS_ERROR("Item in LRU list was not found in storage."); + case CACHE_RESULT_OK: + if (i != nodes_per_key_.end()) + { + nodes_per_key_.erase(i); + } + + ss_dassert(size_ >= pnode->size()); + ss_dassert(count_ > 0); + + size_ -= pnode->size(); + count_ -= 1; + break; + + default: + MXS_ERROR("Could not remove value from storage, cannot " + "remove from LRU list or key mapping either."); + success = false; + } + + return success; +} diff --git a/server/modules/filter/cache/lrustorage.h b/server/modules/filter/cache/lrustorage.h new file mode 100644 index 000000000..59bc57e2f --- /dev/null +++ b/server/modules/filter/cache/lrustorage.h @@ -0,0 +1,177 @@ +#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 "storage.h" +#include "cachefilter.h" + +class LRUStorage : public Storage +{ +public: + ~LRUStorage(); + + /** + * @see Storage::get_key + */ + cache_result_t get_key(const char* zDefaultDb, + const GWBUF* pQuery, + CACHE_KEY* pKey); + +protected: + LRUStorage(Storage* pstorage, size_t max_count, size_t max_size); + + /** + * Fetches the value from the underlying storage and, if found, moves the + * entry to the top of the LRU list. + * + * @see Storage::get_value + */ + cache_result_t do_get_value(const CACHE_KEY& key, + uint32_t flags, + GWBUF** ppValue); + + /** + * Stores the value to the underlying storage and, if successful, either + * places the entry at or moves the existing entry to the top of the LRU + * list. + * + * @see Storage::put_value + */ + cache_result_t do_put_value(const CACHE_KEY& key, + const GWBUF* pValue); + + /** + * Deletes the value from the underlying storage and, if successful, removes + * the entry from the LRU list. + * + * @see Storage::del_value + */ + cache_result_t do_del_value(const CACHE_KEY& key); + +private: + LRUStorage(const LRUStorage&); + LRUStorage& operator = (const LRUStorage&); + + /** + * The Node class is used for maintaining LRU information. + */ + class Node + { + public: + Node() + : pkey_(NULL) + , size_(0) + , pnext_(NULL) + , pprev_(NULL) + {} + ~Node() + { + if (pnext_) + { + pnext_->pprev_ = pprev_; + } + + if (pprev_) + { + pprev_->pnext_ = pnext_; + } + } + + const CACHE_KEY* key() const { return pkey_; } + size_t size() const { return size_; } + Node* next() const { return pnext_; } + Node* prev() const { return pprev_; } + + /** + * Move the node before the node provided as argument. + * + * @param pnode The node in front of which this should be moved. + * @return This node. + */ + Node* prepend(Node* pnode) + { + if (pnode) + { + if (pprev_) + { + pprev_->pnext_ = pnext_; + } + + if (pnext_) + { + pnext_->pprev_ = pprev_; + } + + if (pnode->pprev_) + { + pnode->pprev_->pnext_ = this; + } + + pnode->pprev_ = this; + pnext_ = pnode; + } + + return this; + } + + /** + * Remove this node from the list. + * + * @return The previous node if there is one, or the next node. + */ + Node* remove() + { + if (pprev_) + { + pprev_->pnext_ = pnext_; + } + + if (pnext_) + { + pnext_->pprev_ = pprev_; + } + + return pprev_ ? pprev_ : pnext_; + } + + void reset(const CACHE_KEY* pkey = NULL, size_t size = 0) + { + pkey_ = pkey; + size_ = size; + } + + private: + const CACHE_KEY* pkey_; /*< Points at the key stored in nodes_per_key_ below. */ + size_t size_; /*< The size of the data referred to by pkey_. */ + Node* pnext_; /*< The next node in the LRU list. */ + Node* pprev_; /*< The previous node in the LRU list. */ + }; + + Node* free_lru(); + Node* free_lru(size_t space); + bool free_node_data(Node* pnode); + +private: + typedef std::tr1::unordered_map NodesPerKey; + + Storage* pstorage_; /*< The actual storage. */ + size_t max_count_; /*< The maximum number of items in the LRU list, */ + size_t max_size_; /*< The maximum size of all cached items. */ + size_t count_; /*< The current count of cached items. */ + size_t size_; /*< The current size of all cached items. */ + NodesPerKey nodes_per_key_; /*< Mapping from cache keys to corresponding Node. */ + Node* phead_; /*< The node at the LRU list. */ + Node* ptail_; /*< The node at bottom of the LRU list.*/ +}; diff --git a/server/modules/filter/cache/lrustoragemt.cc b/server/modules/filter/cache/lrustoragemt.cc new file mode 100644 index 000000000..608d66997 --- /dev/null +++ b/server/modules/filter/cache/lrustoragemt.cc @@ -0,0 +1,63 @@ +/* + * 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 "lrustoragemt.h" + +LRUStorageMT::LRUStorageMT(Storage* pstorage, size_t max_count, size_t max_size) + : LRUStorage(pstorage, max_count, max_size) +{ + spinlock_init(&lock_); +} + +LRUStorageMT::~LRUStorageMT() +{ +} + +LRUStorageMT* LRUStorageMT::create(Storage* pstorage, size_t max_count, size_t max_size) +{ + LRUStorageMT* plru_storage = NULL; + + CPP_GUARD(plru_storage = new LRUStorageMT(pstorage, max_count, max_size)); + + return plru_storage; +} + +cache_result_t LRUStorageMT::get_value(const CACHE_KEY& key, + uint32_t flags, + GWBUF** ppvalue) +{ + spinlock_acquire(&lock_); + cache_result_t rv = LRUStorage::do_get_value(key, flags, ppvalue); + spinlock_release(&lock_); + + return rv; +} + +cache_result_t LRUStorageMT::put_value(const CACHE_KEY& key, + const GWBUF* pvalue) +{ + spinlock_acquire(&lock_); + cache_result_t rv = LRUStorage::do_put_value(key, pvalue); + spinlock_release(&lock_); + + return rv; +} + +cache_result_t LRUStorageMT::del_value(const CACHE_KEY& key) +{ + spinlock_acquire(&lock_); + cache_result_t rv = LRUStorage::do_del_value(key); + spinlock_release(&lock_); + + return rv; +} diff --git a/server/modules/filter/cache/lrustoragemt.h b/server/modules/filter/cache/lrustoragemt.h new file mode 100644 index 000000000..0a13ca49f --- /dev/null +++ b/server/modules/filter/cache/lrustoragemt.h @@ -0,0 +1,43 @@ +#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 "lrustorage.h" + +class LRUStorageMT : public LRUStorage +{ +public: + ~LRUStorageMT(); + + LRUStorageMT* create(Storage* pstorage, size_t max_count, size_t max_size); + + cache_result_t get_value(const CACHE_KEY& key, + uint32_t flags, + GWBUF** ppvalue); + + cache_result_t put_value(const CACHE_KEY& key, + const GWBUF* pvalue); + + cache_result_t del_value(const CACHE_KEY& key); + +private: + LRUStorageMT(Storage* pstorage, size_t max_count, size_t max_size); + + LRUStorageMT(const LRUStorageMT&); + LRUStorageMT& operator = (const LRUStorageMT&); + +private: + SPINLOCK lock_; +}; diff --git a/server/modules/filter/cache/lrustoragest.cc b/server/modules/filter/cache/lrustoragest.cc new file mode 100644 index 000000000..89e1f73a5 --- /dev/null +++ b/server/modules/filter/cache/lrustoragest.cc @@ -0,0 +1,50 @@ +/* + * 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 "lrustoragest.h" + +LRUStorageST::LRUStorageST(Storage* pstorage, size_t max_count, size_t max_size) + : LRUStorage(pstorage, max_count, max_size) +{ +} + +LRUStorageST::~LRUStorageST() +{ +} + +LRUStorageST* LRUStorageST::create(Storage* pstorage, size_t max_count, size_t max_size) +{ + LRUStorageST* plru_storage = NULL; + + CPP_GUARD(plru_storage = new LRUStorageST(pstorage, max_count, max_size)); + + return plru_storage; +} + +cache_result_t LRUStorageST::get_value(const CACHE_KEY& key, + uint32_t flags, + GWBUF** ppvalue) +{ + return LRUStorage::do_get_value(key, flags, ppvalue); +} + +cache_result_t LRUStorageST::put_value(const CACHE_KEY& key, + const GWBUF* pvalue) +{ + return LRUStorage::do_put_value(key, pvalue); +} + +cache_result_t LRUStorageST::del_value(const CACHE_KEY& key) +{ + return LRUStorage::do_del_value(key); +} diff --git a/server/modules/filter/cache/lrustoragest.h b/server/modules/filter/cache/lrustoragest.h new file mode 100644 index 000000000..f707bdbc8 --- /dev/null +++ b/server/modules/filter/cache/lrustoragest.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 "lrustorage.h" + +class LRUStorageST : public LRUStorage +{ +public: + ~LRUStorageST(); + + LRUStorageST* create(Storage* pstorage, size_t max_count, size_t max_size); + + cache_result_t get_value(const CACHE_KEY& key, + uint32_t flags, + GWBUF** ppValue); + + cache_result_t put_value(const CACHE_KEY& key, + const GWBUF* pValue); + + cache_result_t del_value(const CACHE_KEY& key); + +private: + LRUStorageST(Storage* pstorage, size_t max_count, size_t max_size); + + LRUStorageST(const LRUStorageST&); + LRUStorageST& operator = (const LRUStorageST&); +};