Finish MockBlockStore to also collect other performance metrics, and implement the standard block store tests for it.

This commit is contained in:
Sebastian Messmer 2016-07-15 10:23:09 +02:00
parent fdcd3b1314
commit d626349802
11 changed files with 273 additions and 108 deletions

View File

@ -32,6 +32,8 @@ set(SOURCES
implementations/versioncounting/KnownBlockVersions.cpp
implementations/versioncounting/ClientIdAndBlockKey.cpp
implementations/versioncounting/IntegrityViolationError.cpp
implementations/mock/MockBlockStore.cpp
implementations/mock/MockBlock.cpp
)
add_library(${PROJECT_NAME} STATIC ${SOURCES})

View File

@ -0,0 +1,18 @@
#include "MockBlock.h"
#include "MockBlockStore.h"
namespace blockstore {
namespace mock {
void MockBlock::write(const void *source, uint64_t offset, uint64_t size) {
_blockStore->_increaseNumWrittenBlocks(key());
return _baseBlock->write(source, offset, size);
}
void MockBlock::resize(size_t newSize) {
_blockStore->_increaseNumResizedBlocks(key());
return _baseBlock->resize(newSize);
}
}
}

View File

@ -0,0 +1,50 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_MOCK_MOCKBLOCK_H_
#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_MOCK_MOCKBLOCK_H_
#include <blockstore/interface/Block.h>
#include <cpp-utils/pointer/unique_ref.h>
namespace blockstore {
namespace mock {
class MockBlockStore;
class MockBlock final : public blockstore::Block {
public:
MockBlock(cpputils::unique_ref<blockstore::Block> baseBlock, MockBlockStore *blockStore)
:Block(baseBlock->key()), _baseBlock(std::move(baseBlock)), _blockStore(blockStore) {
}
const void *data() const override {
return _baseBlock->data();
}
void write(const void *source, uint64_t offset, uint64_t size) override;
void flush() override {
return _baseBlock->flush();
}
size_t size() const override {
return _baseBlock->size();
}
void resize(size_t newSize) override;
cpputils::unique_ref<blockstore::Block> releaseBaseBlock() {
return std::move(_baseBlock);
}
private:
cpputils::unique_ref<blockstore::Block> _baseBlock;
MockBlockStore *_blockStore;
DISALLOW_COPY_AND_ASSIGN(MockBlock);
};
}
}
#endif

View File

@ -0,0 +1 @@
#include "MockBlockStore.h"

View File

@ -0,0 +1,143 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_MOCK_MOCKBLOCKSTORE_H_
#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_MOCK_MOCKBLOCKSTORE_H_
#include <blockstore/implementations/testfake/FakeBlockStore.h>
#include <mutex>
#include "MockBlock.h"
namespace blockstore {
namespace mock {
/**
* This is a blockstore that counts the number of loaded, resized, written, ... blocks.
* It is used for testing that operations only access few blocks (performance tests).
*/
class MockBlockStore final : public BlockStore {
public:
MockBlockStore(cpputils::unique_ref<BlockStore> baseBlockStore = cpputils::make_unique_ref<testfake::FakeBlockStore>())
: _mutex(), _baseBlockStore(std::move(baseBlockStore)), _loadedBlocks(), _createdBlocks(0), _writtenBlocks(), _resizedBlocks(), _removedBlocks() {
}
Key createKey() override {
return _baseBlockStore->createKey();
}
boost::optional<cpputils::unique_ref<Block>> tryCreate(const Key &key, cpputils::Data data) override {
_increaseNumCreatedBlocks();
auto base = _baseBlockStore->tryCreate(key, std::move(data));
if (base == boost::none) {
return boost::none;
}
return boost::optional<cpputils::unique_ref<Block>>(cpputils::make_unique_ref<MockBlock>(std::move(*base), this));
}
boost::optional<cpputils::unique_ref<Block>> load(const Key &key) override {
_increaseNumLoadedBlocks(key);
auto base = _baseBlockStore->load(key);
if (base == boost::none) {
return boost::none;
}
return boost::optional<cpputils::unique_ref<Block>>(cpputils::make_unique_ref<MockBlock>(std::move(*base), this));
}
void remove(const Key &key) override {
_increaseNumRemovedBlocks(key);
return _baseBlockStore->remove(key);
}
uint64_t numBlocks() const override {
return _baseBlockStore->numBlocks();
}
uint64_t estimateNumFreeBytes() const override {
return _baseBlockStore->estimateNumFreeBytes();
}
uint64_t blockSizeFromPhysicalBlockSize(uint64_t blockSize) const override {
return _baseBlockStore->blockSizeFromPhysicalBlockSize(blockSize);
}
void forEachBlock(std::function<void(const Key &)> callback) const override {
return _baseBlockStore->forEachBlock(callback);
}
void remove(cpputils::unique_ref<Block> block) override {
_increaseNumRemovedBlocks(block->key());
auto mockBlock = cpputils::dynamic_pointer_move<MockBlock>(block);
ASSERT(mockBlock != boost::none, "Wrong block type");
return _baseBlockStore->remove((*mockBlock)->releaseBaseBlock());
}
void resetCounters() {
_loadedBlocks = {};
_createdBlocks = 0;
_removedBlocks = {};
_resizedBlocks = {};
_writtenBlocks = {};
}
uint64_t createdBlocks() const {
return _createdBlocks;
}
const std::vector<Key> loadedBlocks() const {
return _loadedBlocks;
}
const std::vector<Key> removedBlocks() const {
return _removedBlocks;
}
const std::vector<Key> resizedBlocks() const {
return _resizedBlocks;
}
const std::vector<Key> writtenBlocks() const {
return _writtenBlocks;
}
private:
void _increaseNumCreatedBlocks() {
std::unique_lock<std::mutex> lock(_mutex);
_createdBlocks += 1;
}
void _increaseNumLoadedBlocks(const Key &key) {
std::unique_lock<std::mutex> lock(_mutex);
_loadedBlocks.push_back(key);
}
void _increaseNumRemovedBlocks(const Key &key) {
std::unique_lock<std::mutex> lock(_mutex);
_removedBlocks.push_back(key);
}
void _increaseNumResizedBlocks(const Key &key) {
std::unique_lock<std::mutex> lock(_mutex);
_resizedBlocks.push_back(key);
}
void _increaseNumWrittenBlocks(const Key &key) {
std::unique_lock<std::mutex> lock(_mutex);
_writtenBlocks.push_back(key);
}
friend class MockBlock;
std::mutex _mutex;
cpputils::unique_ref<BlockStore> _baseBlockStore;
std::vector<Key> _loadedBlocks;
uint64_t _createdBlocks;
std::vector<Key> _writtenBlocks;
std::vector<Key> _resizedBlocks;
std::vector<Key> _removedBlocks;
DISALLOW_COPY_AND_ASSIGN(MockBlockStore);
};
}
}
#endif

View File

@ -28,8 +28,8 @@ TEST_F(DataTreeTest_Performance, DeletingDoesntLoadLeaves_Twolevel_DeleteByTree)
treeStore.remove(std::move(tree));
EXPECT_EQ(1u, blockStore->loadedBlocks.size()); // First loading is from loading the tree, second one from removing it (i.e. loading the root)
EXPECT_EQ(0u, blockStore->createdBlocks);
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // First loading is from loading the tree, second one from removing it (i.e. loading the root)
EXPECT_EQ(0u, blockStore->createdBlocks());
}
TEST_F(DataTreeTest_Performance, DeletingDoesntLoadLeaves_Twolevel_DeleteByKey) {
@ -38,8 +38,8 @@ TEST_F(DataTreeTest_Performance, DeletingDoesntLoadLeaves_Twolevel_DeleteByKey)
treeStore.remove(key);
EXPECT_EQ(1u, blockStore->loadedBlocks.size());
EXPECT_EQ(0u, blockStore->createdBlocks);
EXPECT_EQ(1u, blockStore->loadedBlocks().size());
EXPECT_EQ(0u, blockStore->createdBlocks());
}
TEST_F(DataTreeTest_Performance, DeletingDoesntLoadLeaves_Threelevel_DeleteByTree) {
@ -49,8 +49,8 @@ TEST_F(DataTreeTest_Performance, DeletingDoesntLoadLeaves_Threelevel_DeleteByTre
treeStore.remove(std::move(tree));
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->loadedBlocks.size());
EXPECT_EQ(0u, blockStore->createdBlocks);
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->loadedBlocks().size());
EXPECT_EQ(0u, blockStore->createdBlocks());
}
TEST_F(DataTreeTest_Performance, DeletingDoesntLoadLeaves_Threelevel_DeleteByKey) {
@ -59,8 +59,8 @@ TEST_F(DataTreeTest_Performance, DeletingDoesntLoadLeaves_Threelevel_DeleteByKey
treeStore.remove(key);
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->loadedBlocks.size());
EXPECT_EQ(0u, blockStore->createdBlocks);
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->loadedBlocks().size());
EXPECT_EQ(0u, blockStore->createdBlocks());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Twolevel_All) {
@ -70,8 +70,8 @@ TEST_F(DataTreeTest_Performance, TraverseLeaves_Twolevel_All) {
Traverse(tree.get(), 0, maxChildrenPerInnerNode);
EXPECT_EQ(maxChildrenPerInnerNode, blockStore->loadedBlocks.size()); // Loads all leaves (not the root, because it is already loaded in the tree)
EXPECT_EQ(0u, blockStore->createdBlocks);
EXPECT_EQ(maxChildrenPerInnerNode, blockStore->loadedBlocks().size()); // Loads all leaves (not the root, because it is already loaded in the tree)
EXPECT_EQ(0u, blockStore->createdBlocks());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Twolevel_Some) {
@ -81,8 +81,8 @@ TEST_F(DataTreeTest_Performance, TraverseLeaves_Twolevel_Some) {
Traverse(tree.get(), 3, 5);
EXPECT_EQ(2u, blockStore->loadedBlocks.size()); // Loads both leaves (not the root, because it is already loaded in the tree)
EXPECT_EQ(0u, blockStore->createdBlocks);
EXPECT_EQ(2u, blockStore->loadedBlocks().size()); // Loads both leaves (not the root, because it is already loaded in the tree)
EXPECT_EQ(0u, blockStore->createdBlocks());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_All) {
@ -92,8 +92,8 @@ TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_All) {
Traverse(tree.get(), 0, maxChildrenPerInnerNode * maxChildrenPerInnerNode);
EXPECT_EQ(maxChildrenPerInnerNode + maxChildrenPerInnerNode * maxChildrenPerInnerNode, blockStore->loadedBlocks.size()); // Loads inner nodes and all leaves once (not the root, because it is already loaded in the tree)
EXPECT_EQ(0u, blockStore->createdBlocks);
EXPECT_EQ(maxChildrenPerInnerNode + maxChildrenPerInnerNode * maxChildrenPerInnerNode, blockStore->loadedBlocks().size()); // Loads inner nodes and all leaves once (not the root, because it is already loaded in the tree)
EXPECT_EQ(0u, blockStore->createdBlocks());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_InOneInner) {
@ -103,8 +103,8 @@ TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_InOneInner) {
Traverse(tree.get(), 3, 5);
EXPECT_EQ(3u, blockStore->loadedBlocks.size()); // Loads inner node and both leaves (not the root, because it is already loaded in the tree)
EXPECT_EQ(0u, blockStore->createdBlocks);
EXPECT_EQ(3u, blockStore->loadedBlocks().size()); // Loads inner node and both leaves (not the root, because it is already loaded in the tree)
EXPECT_EQ(0u, blockStore->createdBlocks());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_InTwoInner) {
@ -114,8 +114,8 @@ TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_InTwoInner) {
Traverse(tree.get(), 3, 3 + maxChildrenPerInnerNode);
EXPECT_EQ(2u + maxChildrenPerInnerNode, blockStore->loadedBlocks.size()); // Loads inner node and both leaves (not the root, because it is already loaded in the tree)
EXPECT_EQ(0u, blockStore->createdBlocks);
EXPECT_EQ(2u + maxChildrenPerInnerNode, blockStore->loadedBlocks().size()); // Loads inner node and both leaves (not the root, because it is already loaded in the tree)
EXPECT_EQ(0u, blockStore->createdBlocks());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_WholeInner) {
@ -125,8 +125,8 @@ TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_WholeInner) {
Traverse(tree.get(), maxChildrenPerInnerNode, 2*maxChildrenPerInnerNode);
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->loadedBlocks.size()); // Loads inner node and leaves (not the root, because it is already loaded in the tree)f
EXPECT_EQ(0u, blockStore->createdBlocks);
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->loadedBlocks().size()); // Loads inner node and leaves (not the root, because it is already loaded in the tree)f
EXPECT_EQ(0u, blockStore->createdBlocks());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTree_StartingInside) {
@ -136,8 +136,8 @@ TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTree_StartingInside) {
Traverse(tree.get(), 1, 4);
EXPECT_EQ(1u, blockStore->loadedBlocks.size()); // Loads last old child (for growing it)
EXPECT_EQ(2u, blockStore->createdBlocks);
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // Loads last old child (for growing it)
EXPECT_EQ(2u, blockStore->createdBlocks());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTree_StartingOutside_TwoLevel) {
@ -147,8 +147,8 @@ TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTree_StartingOutside_TwoL
Traverse(tree.get(), 4, 5);
EXPECT_EQ(1u, blockStore->loadedBlocks.size()); // Loads last old leaf for growing it
EXPECT_EQ(3u, blockStore->createdBlocks);
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // Loads last old leaf for growing it
EXPECT_EQ(3u, blockStore->createdBlocks());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTree_StartingOutside_ThreeLevel) {
@ -158,8 +158,8 @@ TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTree_StartingOutside_Thre
Traverse(tree.get(), 2*maxChildrenPerInnerNode+1, 2*maxChildrenPerInnerNode+2);
EXPECT_EQ(2u, blockStore->loadedBlocks.size()); // Loads last old leaf (and its inner node) for growing it
EXPECT_EQ(3u, blockStore->createdBlocks); // inner node and two leaves
EXPECT_EQ(2u, blockStore->loadedBlocks().size()); // Loads last old leaf (and its inner node) for growing it
EXPECT_EQ(3u, blockStore->createdBlocks()); // inner node and two leaves
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTree_StartingAtBeginOfChild) {
@ -169,8 +169,8 @@ TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTree_StartingAtBeginOfChi
Traverse(tree.get(), maxChildrenPerInnerNode, 3*maxChildrenPerInnerNode);
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->loadedBlocks.size()); // Inner node and its leaves
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->createdBlocks); // Creates an inner node and its leaves
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->loadedBlocks().size()); // Inner node and its leaves
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->createdBlocks()); // Creates an inner node and its leaves
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTreeDepth_StartingInOldDepth) {
@ -180,8 +180,8 @@ TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTreeDepth_StartingInOldDe
Traverse(tree.get(), 4, maxChildrenPerInnerNode+2);
EXPECT_EQ(1u, blockStore->loadedBlocks.size()); // Loads last old leaf for growing it
EXPECT_EQ(2 + maxChildrenPerInnerNode, blockStore->createdBlocks); // 2x new inner node + leaves
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // Loads last old leaf for growing it
EXPECT_EQ(2 + maxChildrenPerInnerNode, blockStore->createdBlocks()); // 2x new inner node + leaves
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTreeDepth_StartingInNewDepth) {
@ -191,6 +191,6 @@ TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTreeDepth_StartingInNewDe
Traverse(tree.get(), maxChildrenPerInnerNode, maxChildrenPerInnerNode+2);
EXPECT_EQ(1u, blockStore->loadedBlocks.size()); // Loads last old leaf for growing it
EXPECT_EQ(2 + maxChildrenPerInnerNode, blockStore->createdBlocks); // 2x new inner node + leaves
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // Loads last old leaf for growing it
EXPECT_EQ(2 + maxChildrenPerInnerNode, blockStore->createdBlocks()); // 2x new inner node + leaves
}

View File

@ -9,7 +9,7 @@ using blobstore::onblocks::datanodestore::DataNode;
using blobstore::onblocks::datanodestore::DataInnerNode;
using blobstore::onblocks::datanodestore::DataLeafNode;
using blobstore::onblocks::datatreestore::DataTree;
using blockstore::testfake::FakeBlockStore;
using blockstore::mock::MockBlockStore;
using blockstore::Key;
using cpputils::unique_ref;
using cpputils::make_unique_ref;

View File

@ -10,7 +10,7 @@
#include "blobstore/implementations/onblocks/datanodestore/DataLeafNode.h"
#include "blobstore/implementations/onblocks/datatreestore/DataTree.h"
#include "blobstore/implementations/onblocks/datatreestore/DataTreeStore.h"
#include "MockBlockStore.h"
#include "blockstore/implementations/mock/MockBlockStore.h"
class DataTreeTest: public ::testing::Test {
public:
@ -46,8 +46,8 @@ public:
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateFullThreeLevelWithLastLeafSize(uint32_t size);
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateFourLevelMinDataWithLastLeafSize(uint32_t size);
cpputils::unique_ref<MockBlockStore> _blockStore;
MockBlockStore *blockStore;
cpputils::unique_ref<blockstore::mock::MockBlockStore> _blockStore;
blockstore::mock::MockBlockStore *blockStore;
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataNodeStore> _nodeStore;
blobstore::onblocks::datanodestore::DataNodeStore *nodeStore;
blobstore::onblocks::datatreestore::DataTreeStore treeStore;

View File

@ -1,72 +0,0 @@
#pragma once
#ifndef MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_MOCKBLOCKSTORE_H_
#define MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_MOCKBLOCKSTORE_H_
#include <gmock/gmock.h>
#include <blockstore/implementations/testfake/FakeBlockStore.h>
#include <mutex>
class MockBlockStore final : public blockstore::BlockStore {
public:
MockBlockStore(cpputils::unique_ref<BlockStore> baseBlockStore = cpputils::make_unique_ref<blockstore::testfake::FakeBlockStore>())
: loadedBlocks(), createdBlocks(0), _mutex(), _baseBlockStore(std::move(baseBlockStore)) {
}
blockstore::Key createKey() override {
return _baseBlockStore->createKey();
}
boost::optional<cpputils::unique_ref<blockstore::Block>> tryCreate(const blockstore::Key &key, cpputils::Data data) override {
{
std::unique_lock<std::mutex> lock(_mutex);
createdBlocks += 1;
}
return _baseBlockStore->tryCreate(key, std::move(data));
}
boost::optional<cpputils::unique_ref<blockstore::Block>> load(const blockstore::Key &key) override {
{
std::unique_lock<std::mutex> lock(_mutex);
loadedBlocks.push_back(key);
}
return _baseBlockStore->load(key);
}
void remove(const blockstore::Key &key) override {
return _baseBlockStore->remove(key);
}
uint64_t numBlocks() const override {
return _baseBlockStore->numBlocks();
}
uint64_t estimateNumFreeBytes() const override {
return _baseBlockStore->estimateNumFreeBytes();
}
uint64_t blockSizeFromPhysicalBlockSize(uint64_t blockSize) const override {
return _baseBlockStore->blockSizeFromPhysicalBlockSize(blockSize);
}
void forEachBlock(std::function<void(const blockstore::Key &)> callback) const override {
return _baseBlockStore->forEachBlock(callback);
}
void remove(cpputils::unique_ref<blockstore::Block> block) override {
return _baseBlockStore->remove(std::move(block));
}
void resetCounters() {
loadedBlocks = {};
createdBlocks = 0;
}
std::vector<blockstore::Key> loadedBlocks;
uint64_t createdBlocks;
private:
std::mutex _mutex;
cpputils::unique_ref<blockstore::BlockStore> _baseBlockStore;
};
#endif

View File

@ -6,6 +6,7 @@ set(SOURCES
interface/BlockStoreTest.cpp
interface/BlockTest.cpp
implementations/testfake/TestFakeBlockStoreTest.cpp
implementations/mock/MockBlockStoreTest.cpp
implementations/inmemory/InMemoryBlockStoreTest.cpp
implementations/parallelaccess/ParallelAccessBlockStoreTest_Generic.cpp
implementations/parallelaccess/ParallelAccessBlockStoreTest_Specific.cpp

View File

@ -0,0 +1,22 @@
#include "blockstore/implementations/mock/MockBlock.h"
#include "blockstore/implementations/mock/MockBlockStore.h"
#include "../../testutils/BlockStoreTest.h"
#include "../../testutils/BlockStoreWithRandomKeysTest.h"
#include <gtest/gtest.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
using blockstore::BlockStore;
using blockstore::BlockStoreWithRandomKeys;
using blockstore::mock::MockBlockStore;
using blockstore::testfake::FakeBlockStore;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
class MockBlockStoreTestFixture: public BlockStoreTestFixture {
public:
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<MockBlockStore>(make_unique_ref<FakeBlockStore>());
}
};
INSTANTIATE_TYPED_TEST_CASE_P(Mock, BlockStoreTest, MockBlockStoreTestFixture);