diff --git a/implementations/caching2/Cache.cpp b/implementations/caching2/Cache.cpp new file mode 100644 index 00000000..113b002c --- /dev/null +++ b/implementations/caching2/Cache.cpp @@ -0,0 +1,50 @@ +#include "Cache.h" + +using std::unique_ptr; +using std::make_unique; +using std::mutex; +using std::lock_guard; +using std::pair; + +namespace blockstore { +namespace caching2 { + +constexpr uint32_t Cache::MAX_ENTRIES; + +Cache::Cache(): _cachedBlocks() { +} + +Cache::~Cache() { +} + +unique_ptr Cache::pop(const Key &key) { + lock_guard lock(_mutex); + auto found = _cachedBlocks.find(key); + if (found == _cachedBlocks.end()) { + return nullptr; + } + auto block = found->second.releaseBlock(); + _cachedBlocks.erase(found); + return block; +} + +void Cache::push(unique_ptr block) { + lock_guard lock(_mutex); + if (_cachedBlocks.size() > MAX_ENTRIES) { + deleteOldestEntry(); + assert(_cachedBlocks.size() == MAX_ENTRIES-1); + } + Key key = block->key(); + _cachedBlocks.emplace(key, std::move(block)); +} + +void Cache::deleteOldestEntry() { + auto oldestEntry = std::min_element(_cachedBlocks.begin(), _cachedBlocks.end(), [] (const pair &lhs, const pair &rhs) { + return lhs.second.ageSeconds() > rhs.second.ageSeconds(); + }); + //printf("Deleting age %f (vs %f)\n", oldestEntry->second.ageSeconds(), _cachedBlocks.begin()->second.ageSeconds()); + _cachedBlocks.erase(oldestEntry); +} + +} +} diff --git a/implementations/caching2/Cache.h b/implementations/caching2/Cache.h new file mode 100644 index 00000000..52e10fda --- /dev/null +++ b/implementations/caching2/Cache.h @@ -0,0 +1,33 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING2_CACHE_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING2_CACHE_H_ + +#include "../../interface/Block.h" +#include "CacheEntry.h" +#include +#include + +namespace blockstore { +namespace caching2 { + +class Cache { +public: + static constexpr uint32_t MAX_ENTRIES = 1000; + + Cache(); + virtual ~Cache(); + + void push(std::unique_ptr block); + std::unique_ptr pop(const Key &key); + +private: + mutable std::mutex _mutex; + std::map _cachedBlocks; + + void deleteOldestEntry(); +}; + +} +} + +#endif diff --git a/implementations/caching2/CacheEntry.h b/implementations/caching2/CacheEntry.h index ce04eb81..8b1a0f89 100644 --- a/implementations/caching2/CacheEntry.h +++ b/implementations/caching2/CacheEntry.h @@ -15,7 +15,9 @@ public: CacheEntry(std::unique_ptr block): _lastAccess(time(nullptr)), _block(std::move(block)) { } - double ageSeconds() { + CacheEntry(CacheEntry &&) = default; + + double ageSeconds() const { return difftime(time(nullptr), _lastAccess); } diff --git a/implementations/caching2/CachedBlock.cpp b/implementations/caching2/CachedBlock.cpp new file mode 100644 index 00000000..fc02b5ca --- /dev/null +++ b/implementations/caching2/CachedBlock.cpp @@ -0,0 +1,36 @@ +#include "CachedBlock.h" +#include "Caching2BlockStore.h" + +using std::make_unique; + +namespace blockstore { +namespace caching2 { + +CachedBlock::CachedBlock(std::unique_ptr baseBlock, Caching2BlockStore *blockStore) + :Block(baseBlock->key()), + _blockStore(blockStore), + _baseBlock(std::move(baseBlock)) { +} + +CachedBlock::~CachedBlock() { + _blockStore->release(std::move(_baseBlock)); +} + +const void *CachedBlock::data() const { + return _baseBlock->data(); +} + +void CachedBlock::write(const void *source, uint64_t offset, uint64_t size) { + return _baseBlock->write(source, offset, size); +} + +void CachedBlock::flush() { + return _baseBlock->flush(); +} + +size_t CachedBlock::size() const { + return _baseBlock->size(); +} + +} +} diff --git a/implementations/caching2/CachedBlock.h b/implementations/caching2/CachedBlock.h new file mode 100644 index 00000000..01a5a5b9 --- /dev/null +++ b/implementations/caching2/CachedBlock.h @@ -0,0 +1,36 @@ +#pragma once +#ifndef BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTEDBLOCK_H_ +#define BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTEDBLOCK_H_ + +#include "../../interface/Block.h" + +#include "messmer/cpp-utils/macros.h" +#include + +namespace blockstore { +namespace caching2 { +class Caching2BlockStore; + +class CachedBlock: public Block { +public: + //TODO Storing key twice (in parent class and in object pointed to). Once would be enough. + CachedBlock(std::unique_ptr baseBlock, Caching2BlockStore *blockStore); + virtual ~CachedBlock(); + + const void *data() const override; + void write(const void *source, uint64_t offset, uint64_t size) override; + void flush() override; + + size_t size() const override; + +private: + Caching2BlockStore *_blockStore; + std::unique_ptr _baseBlock; + + DISALLOW_COPY_AND_ASSIGN(CachedBlock); +}; + +} +} + +#endif diff --git a/implementations/caching2/Caching2BlockStore.cpp b/implementations/caching2/Caching2BlockStore.cpp index 4d77e160..30e44816 100644 --- a/implementations/caching2/Caching2BlockStore.cpp +++ b/implementations/caching2/Caching2BlockStore.cpp @@ -1,7 +1,11 @@ #include "Caching2BlockStore.h" +#include "CachedBlock.h" #include "../../interface/Block.h" +#include + using std::unique_ptr; +using std::make_unique; namespace blockstore { namespace caching2 { @@ -15,11 +19,15 @@ unique_ptr Caching2BlockStore::create(size_t size) { // When writing back is done efficiently in the base store (e.g. only one safe-to-disk, not one in the create() and then one in the save(), this is not supported by the current BlockStore interface), // then the base store could actually directly create a block in the create() call, OnDiskBlockStore wouldn't have to avoid file creation in the create() call for performance reasons and I could also adapt the OnDiskBlockStore test cases and remove a lot of flush() calls there because then blocks are loadable directly after the create call() without a flush. // Currently, OnDiskBlockStore doesn't create new blocks directly but only after they're destructed (performance reasons), but this means a newly created block can't be loaded directly. - return _baseBlockStore->create(size); + return make_unique(_baseBlockStore->create(size), this); } unique_ptr Caching2BlockStore::load(const Key &key) { - return _baseBlockStore->load(key); + auto block = _cache.pop(key); + if (block.get() != nullptr) { + return make_unique(std::move(block), this); + } + return make_unique(_baseBlockStore->load(key), this); } void Caching2BlockStore::remove(std::unique_ptr block) { @@ -30,5 +38,9 @@ uint64_t Caching2BlockStore::numBlocks() const { return _baseBlockStore->numBlocks(); } +void Caching2BlockStore::release(unique_ptr block) { + _cache.push(std::move(block)); +} + } } diff --git a/implementations/caching2/Caching2BlockStore.h b/implementations/caching2/Caching2BlockStore.h index f89de57b..19650934 100644 --- a/implementations/caching2/Caching2BlockStore.h +++ b/implementations/caching2/Caching2BlockStore.h @@ -3,7 +3,7 @@ #define BLOCKSTORE_IMPLEMENTATIONS_CACHING2_CACHINGBLOCKSTORE_H_ #include "../../interface/BlockStore.h" -#include "CacheEntry.h" +#include "Cache.h" namespace blockstore { namespace caching2 { @@ -17,10 +17,11 @@ public: void remove(std::unique_ptr block) override; uint64_t numBlocks() const override; -private: + void release(std::unique_ptr block); +private: std::unique_ptr _baseBlockStore; - std::map _cachedBlocks; + Cache _cache; DISALLOW_COPY_AND_ASSIGN(Caching2BlockStore); };