When shrinking a data node, the old space is overwritten with zeroes
This commit is contained in:
parent
24c81dd0b0
commit
c3d543619e
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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()
|
|
||||||
|
Loading…
Reference in New Issue
Block a user