libsnapshot: Add cluster breaks after ops

Previously, we'd check if a new cluster was needed before we added a Cow
Operation. This would cause an op's associated data to go to the wrong
location, so instead we check if we'll need a new cluster after writing
each op.

Bug: 172026020
Test: cow_api_test (ClusterCompressGz)
Change-Id: Ia43afedcfd430961b34f5914da4265b89e6fadb9
This commit is contained in:
Daniel Rosenberg 2020-12-22 21:36:24 -08:00
parent 819ca32a0a
commit 770099bde1
2 changed files with 69 additions and 5 deletions

View file

@ -171,6 +171,70 @@ TEST_F(CowTest, CompressGz) {
ASSERT_TRUE(iter->Done());
}
TEST_F(CowTest, ClusterCompressGz) {
CowOptions options;
options.compression = "gz";
options.cluster_ops = 2;
CowWriter writer(options);
ASSERT_TRUE(writer.Initialize(cow_->fd));
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
std::string data2 = "More data!";
data2.resize(options.block_size, '\0');
ASSERT_TRUE(writer.AddRawBlocks(51, data2.data(), data2.size()));
ASSERT_TRUE(writer.Finalize());
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
CowReader reader;
ASSERT_TRUE(reader.Parse(cow_->fd));
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
ASSERT_FALSE(iter->Done());
auto op = &iter->Get();
StringSink sink;
ASSERT_EQ(op->type, kCowReplaceOp);
ASSERT_EQ(op->compression, kCowCompressGz);
ASSERT_EQ(op->data_length, 56); // compressed!
ASSERT_EQ(op->new_block, 50);
ASSERT_TRUE(reader.ReadData(*op, &sink));
ASSERT_EQ(sink.stream(), data);
iter->Next();
ASSERT_FALSE(iter->Done());
op = &iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
ASSERT_FALSE(iter->Done());
op = &iter->Get();
sink.Reset();
ASSERT_EQ(op->compression, kCowCompressGz);
ASSERT_EQ(op->data_length, 41); // compressed!
ASSERT_EQ(op->new_block, 51);
ASSERT_TRUE(reader.ReadData(*op, &sink));
ASSERT_EQ(sink.stream(), data2);
iter->Next();
ASSERT_FALSE(iter->Done());
op = &iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
ASSERT_TRUE(iter->Done());
}
TEST_F(CowTest, CompressTwoBlocks) {
CowOptions options;
options.compression = "gz";

View file

@ -433,11 +433,6 @@ bool CowWriter::GetDataPos(uint64_t* pos) {
}
bool CowWriter::WriteOperation(const CowOperation& op, const void* data, size_t size) {
// If there isn't room for this op and the cluster end op, end the current cluster
if (cluster_size_ && op.type != kCowClusterOp &&
cluster_size_ < current_cluster_size_ + 2 * sizeof(op)) {
if (!EmitCluster()) return false;
}
if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
PLOG(ERROR) << "lseek failed for writing operation.";
return false;
@ -449,6 +444,11 @@ bool CowWriter::WriteOperation(const CowOperation& op, const void* data, size_t
if (!WriteRawData(data, size)) return false;
}
AddOperation(op);
// If there isn't room for another op and the cluster end op, end the current cluster
if (cluster_size_ && op.type != kCowClusterOp &&
cluster_size_ < current_cluster_size_ + 2 * sizeof(op)) {
if (!EmitCluster()) return false;
}
return true;
}