When shrinking a data node, the old space is overwritten with zeroes

This commit is contained in:
Sebastian Messmer 2014-12-11 00:20:23 +01:00
parent 24c81dd0b0
commit c3d543619e
3 changed files with 68 additions and 13 deletions

View File

@ -30,6 +30,11 @@ void DataLeafNode::write(off_t offset, size_t count, const Data &data) {
void DataLeafNode::InitializeNewNode() { void DataLeafNode::InitializeNewNode() {
*_node.MagicNumber() = _node.magicNumberLeaf; *_node.MagicNumber() = _node.magicNumberLeaf;
*_node.Size() = 0; *_node.Size() = 0;
//fillDataWithZeroes(); not needed, because a newly created block will be zeroed out. DataLeafNodeTest.SpaceIsZeroFilledWhenGrowing ensures this.
}
void DataLeafNode::fillDataWithZeroesFromTo(off_t begin, off_t end) {
std::memset(_node.DataBegin<unsigned char>()+begin, 0, end-begin);
} }
uint64_t DataLeafNode::numBytesInThisNode() { uint64_t DataLeafNode::numBytesInThisNode() {
@ -39,6 +44,10 @@ uint64_t DataLeafNode::numBytesInThisNode() {
void DataLeafNode::resize(uint64_t newsize_bytes) { void DataLeafNode::resize(uint64_t newsize_bytes) {
assert(newsize_bytes <= MAX_STORED_BYTES); assert(newsize_bytes <= MAX_STORED_BYTES);
if (newsize_bytes < *_node.Size()) {
fillDataWithZeroesFromTo(newsize_bytes, *_node.Size());
}
*_node.Size() = newsize_bytes; *_node.Size() = newsize_bytes;
} }

View File

@ -22,6 +22,8 @@ public:
uint64_t numBytesInThisNode() override; uint64_t numBytesInThisNode() override;
void resize(uint64_t newsize_bytes) override; void resize(uint64_t newsize_bytes) override;
private:
void fillDataWithZeroesFromTo(off_t begin, off_t end);
}; };
} }

View File

@ -14,6 +14,7 @@ using std::make_unique;
using std::string; using std::string;
using blockstore::BlockStore; using blockstore::BlockStore;
using blockstore::BlockWithKey;
using blockstore::Data; using blockstore::Data;
using blockstore::inmemory::InMemoryBlockStore; using blockstore::inmemory::InMemoryBlockStore;
using namespace blobstore; using namespace blobstore;
@ -23,9 +24,18 @@ using namespace blobstore::onblocks;
class DataLeafNodeTest: public Test { class DataLeafNodeTest: public Test {
public: public:
unique_ptr<BlockStore> blockStore = make_unique<InMemoryBlockStore>();
DataLeafNodeTest(): randomData(DataLeafNode::MAX_STORED_BYTES) { DataLeafNodeTest():
ZEROES(DataLeafNode::MAX_STORED_BYTES),
randomData(DataLeafNode::MAX_STORED_BYTES),
blockStore(make_unique<InMemoryBlockStore>()),
block(blockStore->create(DataNodeView::BLOCKSIZE_BYTES)),
leafblock(blockStore->create(DataNodeView::BLOCKSIZE_BYTES)),
leafblockdata((uint8_t*)leafblock.block->data()),
leaf(DataNode::createNewLeafNode(std::move(leafblock.block))) {
ZEROES.FillWithZeroes();
DataBlockFixture dataFixture(DataLeafNode::MAX_STORED_BYTES); DataBlockFixture dataFixture(DataLeafNode::MAX_STORED_BYTES);
std::memcpy(randomData.data(), dataFixture.data(), randomData.size()); std::memcpy(randomData.data(), dataFixture.data(), randomData.size());
@ -39,18 +49,27 @@ public:
return block.key; return block.key;
} }
void ReadDataFromLeafBlock(Key key, Data *data) { void FillLeafBlockWithData() {
leaf->resize(randomData.size());
leaf->write(0, randomData.size(), randomData);
}
void ReadDataFromLoadedLeafBlock(Key key, Data *data) {
auto leaf = DataNode::load(blockStore->load(key)); auto leaf = DataNode::load(blockStore->load(key));
EXPECT_IS_PTR_TYPE(DataLeafNode, leaf.get()); EXPECT_IS_PTR_TYPE(DataLeafNode, leaf.get());
leaf->read(0, data->size(), data); leaf->read(0, data->size(), data);
} }
Data ZEROES;
Data randomData; Data randomData;
unique_ptr<BlockStore> blockStore;
BlockWithKey block;
BlockWithKey leafblock;
const uint8_t *leafblockdata;
unique_ptr<DataNode> leaf;
}; };
TEST_F(DataLeafNodeTest, ReadWrittenDataImmediately) { TEST_F(DataLeafNodeTest, ReadWrittenDataImmediately) {
auto block = blockStore->create(DataNodeView::BLOCKSIZE_BYTES);
auto leaf = DataNode::createNewLeafNode(std::move(block.block));
leaf->resize(randomData.size()); leaf->resize(randomData.size());
leaf->write(0, randomData.size(), randomData); leaf->write(0, randomData.size(), randomData);
@ -63,19 +82,16 @@ TEST_F(DataLeafNodeTest, ReadWrittenDataAfterReloadingBLock) {
Key key = WriteDataToNewLeafBlockAndReturnKey(); Key key = WriteDataToNewLeafBlockAndReturnKey();
Data data(DataLeafNode::MAX_STORED_BYTES); Data data(DataLeafNode::MAX_STORED_BYTES);
ReadDataFromLeafBlock(key, &data); ReadDataFromLoadedLeafBlock(key, &data);
EXPECT_EQ(0, std::memcmp(randomData.data(), data.data(), randomData.size())); EXPECT_EQ(0, std::memcmp(randomData.data(), data.data(), randomData.size()));
} }
TEST_F(DataLeafNodeTest, NewLeafNodeHasSizeZero) { TEST_F(DataLeafNodeTest, NewLeafNodeHasSizeZero) {
auto block = blockStore->create(DataNodeView::BLOCKSIZE_BYTES);
auto leaf = DataNode::createNewLeafNode(std::move(block.block));
EXPECT_EQ(0u, leaf->numBytesInThisNode()); EXPECT_EQ(0u, leaf->numBytesInThisNode());
} }
TEST_F(DataLeafNodeTest, NewLeafNodeHasSizeZero_AfterLoading) { TEST_F(DataLeafNodeTest, NewLeafNodeHasSizeZero_AfterLoading) {
auto block = blockStore->create(DataNodeView::BLOCKSIZE_BYTES);
{ {
DataNode::createNewLeafNode(std::move(block.block)); DataNode::createNewLeafNode(std::move(block.block));
} }
@ -88,14 +104,11 @@ class DataLeafNodeSizeTest: public DataLeafNodeTest, public WithParamInterface<u
INSTANTIATE_TEST_CASE_P(DataLeafNodeSizeTest, DataLeafNodeSizeTest, Values(0, 1, 5, 16, 32, 512, DataLeafNode::MAX_STORED_BYTES)); INSTANTIATE_TEST_CASE_P(DataLeafNodeSizeTest, DataLeafNodeSizeTest, Values(0, 1, 5, 16, 32, 512, DataLeafNode::MAX_STORED_BYTES));
TEST_P(DataLeafNodeSizeTest, ResizeNode_ReadSizeImmediately) { TEST_P(DataLeafNodeSizeTest, ResizeNode_ReadSizeImmediately) {
auto block = blockStore->create(DataNodeView::BLOCKSIZE_BYTES);
auto leaf = DataNode::createNewLeafNode(std::move(block.block));
leaf->resize(GetParam()); leaf->resize(GetParam());
EXPECT_EQ(GetParam(), leaf->numBytesInThisNode()); EXPECT_EQ(GetParam(), leaf->numBytesInThisNode());
} }
TEST_P(DataLeafNodeSizeTest, ResizeNode_ReadSizeAfterLoading) { TEST_P(DataLeafNodeSizeTest, ResizeNode_ReadSizeAfterLoading) {
auto block = blockStore->create(DataNodeView::BLOCKSIZE_BYTES);
{ {
auto leaf = DataNode::createNewLeafNode(std::move(block.block)); auto leaf = DataNode::createNewLeafNode(std::move(block.block));
leaf->resize(GetParam()); leaf->resize(GetParam());
@ -104,5 +117,36 @@ TEST_P(DataLeafNodeSizeTest, ResizeNode_ReadSizeAfterLoading) {
EXPECT_EQ(GetParam(), leaf->numBytesInThisNode()); EXPECT_EQ(GetParam(), leaf->numBytesInThisNode());
} }
TEST_F(DataLeafNodeTest, SpaceIsZeroFilledWhenGrowing) {
leaf->resize(randomData.size());
Data read(randomData.size());
leaf->read(0, read.size(), &read);
EXPECT_EQ(0, std::memcmp(ZEROES.data(), read.data(), read.size()));
}
TEST_F(DataLeafNodeTest, SpaceGetsZeroFilledWhenShrinkingAndRegrowing) {
FillLeafBlockWithData();
// resize it smaller and then back to original size
uint32_t smaller_size = randomData.size() - 100;
leaf->resize(smaller_size);
leaf->resize(randomData.size());
//Check that the space was filled with zeroes
Data read(100);
leaf->read(smaller_size, read.size(), &read);
EXPECT_EQ(0, std::memcmp(ZEROES.data(), read.data(), read.size()));
}
TEST_F(DataLeafNodeTest, DataGetsZeroFilledWhenShrinking) {
FillLeafBlockWithData();
uint32_t smaller_size = randomData.size() - 100;
//At first, we expect there to be random data in the underlying data block
EXPECT_EQ(0, std::memcmp((char*)randomData.data()+smaller_size, leafblockdata+DataNodeView::HEADERSIZE_BYTES+smaller_size, 100));
//After shrinking, we expect there to be zeroes in the underlying data block
leaf->resize(smaller_size);
EXPECT_EQ(0, std::memcmp(ZEROES.data(), leafblockdata+DataNodeView::HEADERSIZE_BYTES+smaller_size, 100));
}
//TODO Write tests that only read part of the data //TODO Write tests that only read part of the data
//TODO Test numBytesInThisNode()