Split BlobFileState into an immutable and a mutable part (#6502)
Summary: It's never too soon to refactor something. The patch splits the recently introduced (`VersionEdit` related) `BlobFileState` into two classes `BlobFileAddition` and `BlobFileGarbage`. The idea is that once blob files are closed, they are immutable, and the only thing that changes is the amount of garbage in them. In the new design, `BlobFileAddition` contains the immutable attributes (currently, the count and total size of all blobs, checksum method, and checksum value), while `BlobFileGarbage` contains the mutable GC-related information elements (count and total size of garbage blobs). This is a better fit for the GC logic and is more consistent with how SST files are handled. Pull Request resolved: https://github.com/facebook/rocksdb/pull/6502 Test Plan: `make check` Differential Revision: D20348352 Pulled By: ltamasi fbshipit-source-id: ff93f0121e80ab15e0e0a6525ba0d6af16a0e008main
parent
4028eba67b
commit
f5bc3b99d5
@ -0,0 +1,16 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
|
||||||
|
#include "rocksdb/rocksdb_namespace.h" |
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
|
||||||
|
constexpr uint64_t kInvalidBlobFileNumber = 0; |
||||||
|
|
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
@ -0,0 +1,65 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <cassert> |
||||||
|
#include <cstdint> |
||||||
|
#include <iosfwd> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "db/blob_constants.h" |
||||||
|
#include "rocksdb/rocksdb_namespace.h" |
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
|
||||||
|
class JSONWriter; |
||||||
|
class Slice; |
||||||
|
class Status; |
||||||
|
|
||||||
|
class BlobFileAddition { |
||||||
|
public: |
||||||
|
BlobFileAddition() = default; |
||||||
|
|
||||||
|
BlobFileAddition(uint64_t blob_file_number, uint64_t total_blob_count, |
||||||
|
uint64_t total_blob_bytes, std::string checksum_method, |
||||||
|
std::string checksum_value) |
||||||
|
: blob_file_number_(blob_file_number), |
||||||
|
total_blob_count_(total_blob_count), |
||||||
|
total_blob_bytes_(total_blob_bytes), |
||||||
|
checksum_method_(std::move(checksum_method)), |
||||||
|
checksum_value_(std::move(checksum_value)) { |
||||||
|
assert(checksum_method_.empty() == checksum_value_.empty()); |
||||||
|
} |
||||||
|
|
||||||
|
uint64_t GetBlobFileNumber() const { return blob_file_number_; } |
||||||
|
uint64_t GetTotalBlobCount() const { return total_blob_count_; } |
||||||
|
uint64_t GetTotalBlobBytes() const { return total_blob_bytes_; } |
||||||
|
const std::string& GetChecksumMethod() const { return checksum_method_; } |
||||||
|
const std::string& GetChecksumValue() const { return checksum_value_; } |
||||||
|
|
||||||
|
void EncodeTo(std::string* output) const; |
||||||
|
Status DecodeFrom(Slice* input); |
||||||
|
|
||||||
|
std::string DebugString() const; |
||||||
|
std::string DebugJSON() const; |
||||||
|
|
||||||
|
private: |
||||||
|
uint64_t blob_file_number_ = kInvalidBlobFileNumber; |
||||||
|
uint64_t total_blob_count_ = 0; |
||||||
|
uint64_t total_blob_bytes_ = 0; |
||||||
|
std::string checksum_method_; |
||||||
|
std::string checksum_value_; |
||||||
|
}; |
||||||
|
|
||||||
|
bool operator==(const BlobFileAddition& lhs, const BlobFileAddition& rhs); |
||||||
|
bool operator!=(const BlobFileAddition& lhs, const BlobFileAddition& rhs); |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, |
||||||
|
const BlobFileAddition& blob_file_addition); |
||||||
|
JSONWriter& operator<<(JSONWriter& jw, |
||||||
|
const BlobFileAddition& blob_file_addition); |
||||||
|
|
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
@ -0,0 +1,206 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
#include "db/blob_file_addition.h" |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <cstring> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "test_util/sync_point.h" |
||||||
|
#include "test_util/testharness.h" |
||||||
|
#include "util/coding.h" |
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
|
||||||
|
class BlobFileAdditionTest : public testing::Test { |
||||||
|
public: |
||||||
|
static void TestEncodeDecode(const BlobFileAddition& blob_file_addition) { |
||||||
|
std::string encoded; |
||||||
|
blob_file_addition.EncodeTo(&encoded); |
||||||
|
|
||||||
|
BlobFileAddition decoded; |
||||||
|
Slice input(encoded); |
||||||
|
ASSERT_OK(decoded.DecodeFrom(&input)); |
||||||
|
|
||||||
|
ASSERT_EQ(blob_file_addition, decoded); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_F(BlobFileAdditionTest, Empty) { |
||||||
|
BlobFileAddition blob_file_addition; |
||||||
|
|
||||||
|
ASSERT_EQ(blob_file_addition.GetBlobFileNumber(), kInvalidBlobFileNumber); |
||||||
|
ASSERT_EQ(blob_file_addition.GetTotalBlobCount(), 0); |
||||||
|
ASSERT_EQ(blob_file_addition.GetTotalBlobBytes(), 0); |
||||||
|
ASSERT_TRUE(blob_file_addition.GetChecksumMethod().empty()); |
||||||
|
ASSERT_TRUE(blob_file_addition.GetChecksumValue().empty()); |
||||||
|
|
||||||
|
TestEncodeDecode(blob_file_addition); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(BlobFileAdditionTest, NonEmpty) { |
||||||
|
constexpr uint64_t blob_file_number = 123; |
||||||
|
constexpr uint64_t total_blob_count = 2; |
||||||
|
constexpr uint64_t total_blob_bytes = 123456; |
||||||
|
const std::string checksum_method("SHA1"); |
||||||
|
const std::string checksum_value("bdb7f34a59dfa1592ce7f52e99f98c570c525cbd"); |
||||||
|
|
||||||
|
BlobFileAddition blob_file_addition(blob_file_number, total_blob_count, |
||||||
|
total_blob_bytes, checksum_method, |
||||||
|
checksum_value); |
||||||
|
|
||||||
|
ASSERT_EQ(blob_file_addition.GetBlobFileNumber(), blob_file_number); |
||||||
|
ASSERT_EQ(blob_file_addition.GetTotalBlobCount(), total_blob_count); |
||||||
|
ASSERT_EQ(blob_file_addition.GetTotalBlobBytes(), total_blob_bytes); |
||||||
|
ASSERT_EQ(blob_file_addition.GetChecksumMethod(), checksum_method); |
||||||
|
ASSERT_EQ(blob_file_addition.GetChecksumValue(), checksum_value); |
||||||
|
|
||||||
|
TestEncodeDecode(blob_file_addition); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(BlobFileAdditionTest, DecodeErrors) { |
||||||
|
std::string str; |
||||||
|
Slice slice(str); |
||||||
|
|
||||||
|
BlobFileAddition blob_file_addition; |
||||||
|
|
||||||
|
{ |
||||||
|
const Status s = blob_file_addition.DecodeFrom(&slice); |
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "blob file number")); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr uint64_t blob_file_number = 123; |
||||||
|
PutVarint64(&str, blob_file_number); |
||||||
|
slice = str; |
||||||
|
|
||||||
|
{ |
||||||
|
const Status s = blob_file_addition.DecodeFrom(&slice); |
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "total blob count")); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr uint64_t total_blob_count = 4567; |
||||||
|
PutVarint64(&str, total_blob_count); |
||||||
|
slice = str; |
||||||
|
|
||||||
|
{ |
||||||
|
const Status s = blob_file_addition.DecodeFrom(&slice); |
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "total blob bytes")); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr uint64_t total_blob_bytes = 12345678; |
||||||
|
PutVarint64(&str, total_blob_bytes); |
||||||
|
slice = str; |
||||||
|
|
||||||
|
{ |
||||||
|
const Status s = blob_file_addition.DecodeFrom(&slice); |
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "checksum method")); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr char checksum_method[] = "SHA1"; |
||||||
|
PutLengthPrefixedSlice(&str, checksum_method); |
||||||
|
slice = str; |
||||||
|
|
||||||
|
{ |
||||||
|
const Status s = blob_file_addition.DecodeFrom(&slice); |
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "checksum value")); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr char checksum_value[] = "bdb7f34a59dfa1592ce7f52e99f98c570c525cbd"; |
||||||
|
PutLengthPrefixedSlice(&str, checksum_value); |
||||||
|
slice = str; |
||||||
|
|
||||||
|
{ |
||||||
|
const Status s = blob_file_addition.DecodeFrom(&slice); |
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "custom field tag")); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr uint32_t custom_tag = 2; |
||||||
|
PutVarint32(&str, custom_tag); |
||||||
|
slice = str; |
||||||
|
|
||||||
|
{ |
||||||
|
const Status s = blob_file_addition.DecodeFrom(&slice); |
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "custom field value")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(BlobFileAdditionTest, ForwardCompatibleCustomField) { |
||||||
|
SyncPoint::GetInstance()->SetCallBack( |
||||||
|
"BlobFileAddition::EncodeTo::CustomFields", [&](void* arg) { |
||||||
|
std::string* output = static_cast<std::string*>(arg); |
||||||
|
|
||||||
|
constexpr uint32_t forward_compatible_tag = 2; |
||||||
|
PutVarint32(output, forward_compatible_tag); |
||||||
|
|
||||||
|
PutLengthPrefixedSlice(output, "deadbeef"); |
||||||
|
}); |
||||||
|
SyncPoint::GetInstance()->EnableProcessing(); |
||||||
|
|
||||||
|
constexpr uint64_t blob_file_number = 678; |
||||||
|
constexpr uint64_t total_blob_count = 9999; |
||||||
|
constexpr uint64_t total_blob_bytes = 100000000; |
||||||
|
const std::string checksum_method("CRC32"); |
||||||
|
const std::string checksum_value("3d87ff57"); |
||||||
|
|
||||||
|
BlobFileAddition blob_file_addition(blob_file_number, total_blob_count, |
||||||
|
total_blob_bytes, checksum_method, |
||||||
|
checksum_value); |
||||||
|
|
||||||
|
TestEncodeDecode(blob_file_addition); |
||||||
|
|
||||||
|
SyncPoint::GetInstance()->DisableProcessing(); |
||||||
|
SyncPoint::GetInstance()->ClearAllCallBacks(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(BlobFileAdditionTest, ForwardIncompatibleCustomField) { |
||||||
|
SyncPoint::GetInstance()->SetCallBack( |
||||||
|
"BlobFileAddition::EncodeTo::CustomFields", [&](void* arg) { |
||||||
|
std::string* output = static_cast<std::string*>(arg); |
||||||
|
|
||||||
|
constexpr uint32_t forward_incompatible_tag = (1 << 6) + 1; |
||||||
|
PutVarint32(output, forward_incompatible_tag); |
||||||
|
|
||||||
|
PutLengthPrefixedSlice(output, "foobar"); |
||||||
|
}); |
||||||
|
SyncPoint::GetInstance()->EnableProcessing(); |
||||||
|
|
||||||
|
constexpr uint64_t blob_file_number = 456; |
||||||
|
constexpr uint64_t total_blob_count = 100; |
||||||
|
constexpr uint64_t total_blob_bytes = 2000000; |
||||||
|
const std::string checksum_method("CRC32B"); |
||||||
|
const std::string checksum_value("6dbdf23a"); |
||||||
|
|
||||||
|
BlobFileAddition blob_file_addition(blob_file_number, total_blob_count, |
||||||
|
total_blob_bytes, checksum_method, |
||||||
|
checksum_value); |
||||||
|
|
||||||
|
std::string encoded; |
||||||
|
blob_file_addition.EncodeTo(&encoded); |
||||||
|
|
||||||
|
BlobFileAddition decoded_blob_file_addition; |
||||||
|
Slice input(encoded); |
||||||
|
const Status s = decoded_blob_file_addition.DecodeFrom(&input); |
||||||
|
|
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "Forward incompatible")); |
||||||
|
|
||||||
|
SyncPoint::GetInstance()->DisableProcessing(); |
||||||
|
SyncPoint::GetInstance()->ClearAllCallBacks(); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
::testing::InitGoogleTest(&argc, argv); |
||||||
|
return RUN_ALL_TESTS(); |
||||||
|
} |
@ -0,0 +1,138 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
#include "db/blob_file_garbage.h" |
||||||
|
|
||||||
|
#include <ostream> |
||||||
|
#include <sstream> |
||||||
|
|
||||||
|
#include "logging/event_logger.h" |
||||||
|
#include "rocksdb/slice.h" |
||||||
|
#include "rocksdb/status.h" |
||||||
|
#include "test_util/sync_point.h" |
||||||
|
#include "util/coding.h" |
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
// Tags for custom fields. Note that these get persisted in the manifest,
|
||||||
|
// so existing tags should not be modified.
|
||||||
|
enum CustomFieldTags : uint32_t { |
||||||
|
kEndMarker, |
||||||
|
|
||||||
|
// Add forward compatible fields here
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
kForwardIncompatibleMask = 1 << 6, |
||||||
|
|
||||||
|
// Add forward incompatible fields here
|
||||||
|
}; |
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
void BlobFileGarbage::EncodeTo(std::string* output) const { |
||||||
|
PutVarint64(output, blob_file_number_); |
||||||
|
PutVarint64(output, garbage_blob_count_); |
||||||
|
PutVarint64(output, garbage_blob_bytes_); |
||||||
|
|
||||||
|
// Encode any custom fields here. The format to use is a Varint32 tag (see
|
||||||
|
// CustomFieldTags above) followed by a length prefixed slice. Unknown custom
|
||||||
|
// fields will be ignored during decoding unless they're in the forward
|
||||||
|
// incompatible range.
|
||||||
|
|
||||||
|
TEST_SYNC_POINT_CALLBACK("BlobFileGarbage::EncodeTo::CustomFields", output); |
||||||
|
|
||||||
|
PutVarint32(output, kEndMarker); |
||||||
|
} |
||||||
|
|
||||||
|
Status BlobFileGarbage::DecodeFrom(Slice* input) { |
||||||
|
constexpr char class_name[] = "BlobFileGarbage"; |
||||||
|
|
||||||
|
if (!GetVarint64(input, &blob_file_number_)) { |
||||||
|
return Status::Corruption(class_name, "Error decoding blob file number"); |
||||||
|
} |
||||||
|
|
||||||
|
if (!GetVarint64(input, &garbage_blob_count_)) { |
||||||
|
return Status::Corruption(class_name, "Error decoding garbage blob count"); |
||||||
|
} |
||||||
|
|
||||||
|
if (!GetVarint64(input, &garbage_blob_bytes_)) { |
||||||
|
return Status::Corruption(class_name, "Error decoding garbage blob bytes"); |
||||||
|
} |
||||||
|
|
||||||
|
while (true) { |
||||||
|
uint32_t custom_field_tag = 0; |
||||||
|
if (!GetVarint32(input, &custom_field_tag)) { |
||||||
|
return Status::Corruption(class_name, "Error decoding custom field tag"); |
||||||
|
} |
||||||
|
|
||||||
|
if (custom_field_tag == kEndMarker) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (custom_field_tag & kForwardIncompatibleMask) { |
||||||
|
return Status::Corruption( |
||||||
|
class_name, "Forward incompatible custom field encountered"); |
||||||
|
} |
||||||
|
|
||||||
|
Slice custom_field_value; |
||||||
|
if (!GetLengthPrefixedSlice(input, &custom_field_value)) { |
||||||
|
return Status::Corruption(class_name, |
||||||
|
"Error decoding custom field value"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return Status::OK(); |
||||||
|
} |
||||||
|
|
||||||
|
std::string BlobFileGarbage::DebugString() const { |
||||||
|
std::ostringstream oss; |
||||||
|
|
||||||
|
oss << *this; |
||||||
|
|
||||||
|
return oss.str(); |
||||||
|
} |
||||||
|
|
||||||
|
std::string BlobFileGarbage::DebugJSON() const { |
||||||
|
JSONWriter jw; |
||||||
|
|
||||||
|
jw << *this; |
||||||
|
|
||||||
|
jw.EndObject(); |
||||||
|
|
||||||
|
return jw.Get(); |
||||||
|
} |
||||||
|
|
||||||
|
bool operator==(const BlobFileGarbage& lhs, const BlobFileGarbage& rhs) { |
||||||
|
return lhs.GetBlobFileNumber() == rhs.GetBlobFileNumber() && |
||||||
|
lhs.GetGarbageBlobCount() == rhs.GetGarbageBlobCount() && |
||||||
|
lhs.GetGarbageBlobBytes() == rhs.GetGarbageBlobBytes(); |
||||||
|
} |
||||||
|
|
||||||
|
bool operator!=(const BlobFileGarbage& lhs, const BlobFileGarbage& rhs) { |
||||||
|
return !(lhs == rhs); |
||||||
|
} |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, |
||||||
|
const BlobFileGarbage& blob_file_garbage) { |
||||||
|
os << "blob_file_number: " << blob_file_garbage.GetBlobFileNumber() |
||||||
|
<< " garbage_blob_count: " << blob_file_garbage.GetGarbageBlobCount() |
||||||
|
<< " garbage_blob_bytes: " << blob_file_garbage.GetGarbageBlobBytes(); |
||||||
|
|
||||||
|
return os; |
||||||
|
} |
||||||
|
|
||||||
|
JSONWriter& operator<<(JSONWriter& jw, |
||||||
|
const BlobFileGarbage& blob_file_garbage) { |
||||||
|
jw << "BlobFileNumber" << blob_file_garbage.GetBlobFileNumber() |
||||||
|
<< "GarbageBlobCount" << blob_file_garbage.GetGarbageBlobCount() |
||||||
|
<< "GarbageBlobBytes" << blob_file_garbage.GetGarbageBlobBytes(); |
||||||
|
|
||||||
|
return jw; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
@ -0,0 +1,55 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <iosfwd> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "db/blob_constants.h" |
||||||
|
#include "rocksdb/rocksdb_namespace.h" |
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
|
||||||
|
class JSONWriter; |
||||||
|
class Slice; |
||||||
|
class Status; |
||||||
|
|
||||||
|
class BlobFileGarbage { |
||||||
|
public: |
||||||
|
BlobFileGarbage() = default; |
||||||
|
|
||||||
|
BlobFileGarbage(uint64_t blob_file_number, uint64_t garbage_blob_count, |
||||||
|
uint64_t garbage_blob_bytes) |
||||||
|
: blob_file_number_(blob_file_number), |
||||||
|
garbage_blob_count_(garbage_blob_count), |
||||||
|
garbage_blob_bytes_(garbage_blob_bytes) {} |
||||||
|
|
||||||
|
uint64_t GetBlobFileNumber() const { return blob_file_number_; } |
||||||
|
uint64_t GetGarbageBlobCount() const { return garbage_blob_count_; } |
||||||
|
uint64_t GetGarbageBlobBytes() const { return garbage_blob_bytes_; } |
||||||
|
|
||||||
|
void EncodeTo(std::string* output) const; |
||||||
|
Status DecodeFrom(Slice* input); |
||||||
|
|
||||||
|
std::string DebugString() const; |
||||||
|
std::string DebugJSON() const; |
||||||
|
|
||||||
|
private: |
||||||
|
uint64_t blob_file_number_ = kInvalidBlobFileNumber; |
||||||
|
uint64_t garbage_blob_count_ = 0; |
||||||
|
uint64_t garbage_blob_bytes_ = 0; |
||||||
|
}; |
||||||
|
|
||||||
|
bool operator==(const BlobFileGarbage& lhs, const BlobFileGarbage& rhs); |
||||||
|
bool operator!=(const BlobFileGarbage& lhs, const BlobFileGarbage& rhs); |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, |
||||||
|
const BlobFileGarbage& blob_file_garbage); |
||||||
|
JSONWriter& operator<<(JSONWriter& jw, |
||||||
|
const BlobFileGarbage& blob_file_garbage); |
||||||
|
|
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
@ -0,0 +1,173 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
#include "db/blob_file_garbage.h" |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <cstring> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "test_util/sync_point.h" |
||||||
|
#include "test_util/testharness.h" |
||||||
|
#include "util/coding.h" |
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
|
||||||
|
class BlobFileGarbageTest : public testing::Test { |
||||||
|
public: |
||||||
|
static void TestEncodeDecode(const BlobFileGarbage& blob_file_garbage) { |
||||||
|
std::string encoded; |
||||||
|
blob_file_garbage.EncodeTo(&encoded); |
||||||
|
|
||||||
|
BlobFileGarbage decoded; |
||||||
|
Slice input(encoded); |
||||||
|
ASSERT_OK(decoded.DecodeFrom(&input)); |
||||||
|
|
||||||
|
ASSERT_EQ(blob_file_garbage, decoded); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_F(BlobFileGarbageTest, Empty) { |
||||||
|
BlobFileGarbage blob_file_garbage; |
||||||
|
|
||||||
|
ASSERT_EQ(blob_file_garbage.GetBlobFileNumber(), kInvalidBlobFileNumber); |
||||||
|
ASSERT_EQ(blob_file_garbage.GetGarbageBlobCount(), 0); |
||||||
|
ASSERT_EQ(blob_file_garbage.GetGarbageBlobBytes(), 0); |
||||||
|
|
||||||
|
TestEncodeDecode(blob_file_garbage); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(BlobFileGarbageTest, NonEmpty) { |
||||||
|
constexpr uint64_t blob_file_number = 123; |
||||||
|
constexpr uint64_t garbage_blob_count = 1; |
||||||
|
constexpr uint64_t garbage_blob_bytes = 9876; |
||||||
|
|
||||||
|
BlobFileGarbage blob_file_garbage(blob_file_number, garbage_blob_count, |
||||||
|
garbage_blob_bytes); |
||||||
|
|
||||||
|
ASSERT_EQ(blob_file_garbage.GetBlobFileNumber(), blob_file_number); |
||||||
|
ASSERT_EQ(blob_file_garbage.GetGarbageBlobCount(), garbage_blob_count); |
||||||
|
ASSERT_EQ(blob_file_garbage.GetGarbageBlobBytes(), garbage_blob_bytes); |
||||||
|
|
||||||
|
TestEncodeDecode(blob_file_garbage); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(BlobFileGarbageTest, DecodeErrors) { |
||||||
|
std::string str; |
||||||
|
Slice slice(str); |
||||||
|
|
||||||
|
BlobFileGarbage blob_file_garbage; |
||||||
|
|
||||||
|
{ |
||||||
|
const Status s = blob_file_garbage.DecodeFrom(&slice); |
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "blob file number")); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr uint64_t blob_file_number = 123; |
||||||
|
PutVarint64(&str, blob_file_number); |
||||||
|
slice = str; |
||||||
|
|
||||||
|
{ |
||||||
|
const Status s = blob_file_garbage.DecodeFrom(&slice); |
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "garbage blob count")); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr uint64_t garbage_blob_count = 4567; |
||||||
|
PutVarint64(&str, garbage_blob_count); |
||||||
|
slice = str; |
||||||
|
|
||||||
|
{ |
||||||
|
const Status s = blob_file_garbage.DecodeFrom(&slice); |
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "garbage blob bytes")); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr uint64_t garbage_blob_bytes = 12345678; |
||||||
|
PutVarint64(&str, garbage_blob_bytes); |
||||||
|
slice = str; |
||||||
|
|
||||||
|
{ |
||||||
|
const Status s = blob_file_garbage.DecodeFrom(&slice); |
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "custom field tag")); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr uint32_t custom_tag = 2; |
||||||
|
PutVarint32(&str, custom_tag); |
||||||
|
slice = str; |
||||||
|
|
||||||
|
{ |
||||||
|
const Status s = blob_file_garbage.DecodeFrom(&slice); |
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "custom field value")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(BlobFileGarbageTest, ForwardCompatibleCustomField) { |
||||||
|
SyncPoint::GetInstance()->SetCallBack( |
||||||
|
"BlobFileGarbage::EncodeTo::CustomFields", [&](void* arg) { |
||||||
|
std::string* output = static_cast<std::string*>(arg); |
||||||
|
|
||||||
|
constexpr uint32_t forward_compatible_tag = 2; |
||||||
|
PutVarint32(output, forward_compatible_tag); |
||||||
|
|
||||||
|
PutLengthPrefixedSlice(output, "deadbeef"); |
||||||
|
}); |
||||||
|
SyncPoint::GetInstance()->EnableProcessing(); |
||||||
|
|
||||||
|
constexpr uint64_t blob_file_number = 678; |
||||||
|
constexpr uint64_t garbage_blob_count = 9999; |
||||||
|
constexpr uint64_t garbage_blob_bytes = 100000000; |
||||||
|
|
||||||
|
BlobFileGarbage blob_file_garbage(blob_file_number, garbage_blob_count, |
||||||
|
garbage_blob_bytes); |
||||||
|
|
||||||
|
TestEncodeDecode(blob_file_garbage); |
||||||
|
|
||||||
|
SyncPoint::GetInstance()->DisableProcessing(); |
||||||
|
SyncPoint::GetInstance()->ClearAllCallBacks(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(BlobFileGarbageTest, ForwardIncompatibleCustomField) { |
||||||
|
SyncPoint::GetInstance()->SetCallBack( |
||||||
|
"BlobFileGarbage::EncodeTo::CustomFields", [&](void* arg) { |
||||||
|
std::string* output = static_cast<std::string*>(arg); |
||||||
|
|
||||||
|
constexpr uint32_t forward_incompatible_tag = (1 << 6) + 1; |
||||||
|
PutVarint32(output, forward_incompatible_tag); |
||||||
|
|
||||||
|
PutLengthPrefixedSlice(output, "foobar"); |
||||||
|
}); |
||||||
|
SyncPoint::GetInstance()->EnableProcessing(); |
||||||
|
|
||||||
|
constexpr uint64_t blob_file_number = 456; |
||||||
|
constexpr uint64_t garbage_blob_count = 100; |
||||||
|
constexpr uint64_t garbage_blob_bytes = 2000000; |
||||||
|
|
||||||
|
BlobFileGarbage blob_file_garbage(blob_file_number, garbage_blob_count, |
||||||
|
garbage_blob_bytes); |
||||||
|
|
||||||
|
std::string encoded; |
||||||
|
blob_file_garbage.EncodeTo(&encoded); |
||||||
|
|
||||||
|
BlobFileGarbage decoded_blob_file_addition; |
||||||
|
Slice input(encoded); |
||||||
|
const Status s = decoded_blob_file_addition.DecodeFrom(&input); |
||||||
|
|
||||||
|
ASSERT_TRUE(s.IsCorruption()); |
||||||
|
ASSERT_TRUE(std::strstr(s.getState(), "Forward incompatible")); |
||||||
|
|
||||||
|
SyncPoint::GetInstance()->DisableProcessing(); |
||||||
|
SyncPoint::GetInstance()->ClearAllCallBacks(); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
::testing::InitGoogleTest(&argc, argv); |
||||||
|
return RUN_ALL_TESTS(); |
||||||
|
} |
@ -1,102 +0,0 @@ |
|||||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
|
||||||
// This source code is licensed under both the GPLv2 (found in the
|
|
||||||
// COPYING file in the root directory) and Apache 2.0 License
|
|
||||||
// (found in the LICENSE.Apache file in the root directory).
|
|
||||||
|
|
||||||
#pragma once |
|
||||||
|
|
||||||
#include "rocksdb/rocksdb_namespace.h" |
|
||||||
|
|
||||||
#include <cassert> |
|
||||||
#include <cstdint> |
|
||||||
#include <iosfwd> |
|
||||||
#include <string> |
|
||||||
|
|
||||||
namespace ROCKSDB_NAMESPACE { |
|
||||||
|
|
||||||
constexpr uint64_t kInvalidBlobFileNumber = 0; |
|
||||||
|
|
||||||
class JSONWriter; |
|
||||||
class Slice; |
|
||||||
class Status; |
|
||||||
|
|
||||||
class BlobFileState { |
|
||||||
public: |
|
||||||
BlobFileState() = default; |
|
||||||
|
|
||||||
BlobFileState(uint64_t blob_file_number, uint64_t total_blob_count, |
|
||||||
uint64_t total_blob_bytes, std::string checksum_method, |
|
||||||
std::string checksum_value) |
|
||||||
: blob_file_number_(blob_file_number), |
|
||||||
total_blob_count_(total_blob_count), |
|
||||||
total_blob_bytes_(total_blob_bytes), |
|
||||||
checksum_method_(std::move(checksum_method)), |
|
||||||
checksum_value_(std::move(checksum_value)) { |
|
||||||
assert(checksum_method_.empty() == checksum_value_.empty()); |
|
||||||
} |
|
||||||
|
|
||||||
BlobFileState(uint64_t blob_file_number, uint64_t total_blob_count, |
|
||||||
uint64_t total_blob_bytes, uint64_t garbage_blob_count, |
|
||||||
uint64_t garbage_blob_bytes, std::string checksum_method, |
|
||||||
std::string checksum_value) |
|
||||||
: blob_file_number_(blob_file_number), |
|
||||||
total_blob_count_(total_blob_count), |
|
||||||
total_blob_bytes_(total_blob_bytes), |
|
||||||
garbage_blob_count_(garbage_blob_count), |
|
||||||
garbage_blob_bytes_(garbage_blob_bytes), |
|
||||||
checksum_method_(std::move(checksum_method)), |
|
||||||
checksum_value_(std::move(checksum_value)) { |
|
||||||
assert(checksum_method_.empty() == checksum_value_.empty()); |
|
||||||
assert(garbage_blob_count_ <= total_blob_count_); |
|
||||||
assert(garbage_blob_bytes_ <= total_blob_bytes_); |
|
||||||
} |
|
||||||
|
|
||||||
uint64_t GetBlobFileNumber() const { return blob_file_number_; } |
|
||||||
|
|
||||||
uint64_t GetTotalBlobCount() const { return total_blob_count_; } |
|
||||||
uint64_t GetTotalBlobBytes() const { return total_blob_bytes_; } |
|
||||||
|
|
||||||
void AddGarbageBlob(uint64_t size) { |
|
||||||
assert(garbage_blob_count_ < total_blob_count_); |
|
||||||
assert(garbage_blob_bytes_ + size <= total_blob_bytes_); |
|
||||||
|
|
||||||
++garbage_blob_count_; |
|
||||||
garbage_blob_bytes_ += size; |
|
||||||
} |
|
||||||
|
|
||||||
uint64_t GetGarbageBlobCount() const { return garbage_blob_count_; } |
|
||||||
uint64_t GetGarbageBlobBytes() const { return garbage_blob_bytes_; } |
|
||||||
|
|
||||||
bool IsObsolete() const { |
|
||||||
assert(garbage_blob_count_ <= total_blob_count_); |
|
||||||
|
|
||||||
return !(garbage_blob_count_ < total_blob_count_); |
|
||||||
} |
|
||||||
|
|
||||||
const std::string& GetChecksumMethod() const { return checksum_method_; } |
|
||||||
const std::string& GetChecksumValue() const { return checksum_value_; } |
|
||||||
|
|
||||||
void EncodeTo(std::string* output) const; |
|
||||||
Status DecodeFrom(Slice* input); |
|
||||||
|
|
||||||
std::string DebugString() const; |
|
||||||
std::string DebugJSON() const; |
|
||||||
|
|
||||||
private: |
|
||||||
uint64_t blob_file_number_ = kInvalidBlobFileNumber; |
|
||||||
uint64_t total_blob_count_ = 0; |
|
||||||
uint64_t total_blob_bytes_ = 0; |
|
||||||
uint64_t garbage_blob_count_ = 0; |
|
||||||
uint64_t garbage_blob_bytes_ = 0; |
|
||||||
std::string checksum_method_; |
|
||||||
std::string checksum_value_; |
|
||||||
}; |
|
||||||
|
|
||||||
bool operator==(const BlobFileState& lhs, const BlobFileState& rhs); |
|
||||||
bool operator!=(const BlobFileState& lhs, const BlobFileState& rhs); |
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, |
|
||||||
const BlobFileState& blob_file_state); |
|
||||||
JSONWriter& operator<<(JSONWriter& jw, const BlobFileState& blob_file_state); |
|
||||||
|
|
||||||
} // namespace ROCKSDB_NAMESPACE
|
|
@ -1,284 +0,0 @@ |
|||||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
|
||||||
// This source code is licensed under both the GPLv2 (found in the
|
|
||||||
// COPYING file in the root directory) and Apache 2.0 License
|
|
||||||
// (found in the LICENSE.Apache file in the root directory).
|
|
||||||
|
|
||||||
#include "db/blob_file_state.h" |
|
||||||
#include "test_util/sync_point.h" |
|
||||||
#include "test_util/testharness.h" |
|
||||||
#include "util/coding.h" |
|
||||||
|
|
||||||
#include <cstdint> |
|
||||||
#include <cstring> |
|
||||||
#include <string> |
|
||||||
|
|
||||||
namespace ROCKSDB_NAMESPACE { |
|
||||||
|
|
||||||
class BlobFileStateTest : public testing::Test { |
|
||||||
public: |
|
||||||
static void TestEncodeDecode(const BlobFileState& blob_file_state) { |
|
||||||
std::string encoded; |
|
||||||
blob_file_state.EncodeTo(&encoded); |
|
||||||
|
|
||||||
BlobFileState decoded; |
|
||||||
Slice input(encoded); |
|
||||||
ASSERT_OK(decoded.DecodeFrom(&input)); |
|
||||||
|
|
||||||
ASSERT_EQ(blob_file_state, decoded); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
TEST_F(BlobFileStateTest, Empty) { |
|
||||||
BlobFileState blob_file_state; |
|
||||||
|
|
||||||
ASSERT_EQ(blob_file_state.GetBlobFileNumber(), kInvalidBlobFileNumber); |
|
||||||
ASSERT_EQ(blob_file_state.GetTotalBlobCount(), 0); |
|
||||||
ASSERT_EQ(blob_file_state.GetTotalBlobBytes(), 0); |
|
||||||
ASSERT_EQ(blob_file_state.GetGarbageBlobCount(), 0); |
|
||||||
ASSERT_EQ(blob_file_state.GetGarbageBlobBytes(), 0); |
|
||||||
ASSERT_TRUE(blob_file_state.IsObsolete()); |
|
||||||
ASSERT_TRUE(blob_file_state.GetChecksumMethod().empty()); |
|
||||||
ASSERT_TRUE(blob_file_state.GetChecksumValue().empty()); |
|
||||||
|
|
||||||
TestEncodeDecode(blob_file_state); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(BlobFileStateTest, NonEmpty) { |
|
||||||
constexpr uint64_t blob_file_number = 123; |
|
||||||
constexpr uint64_t total_blob_count = 2; |
|
||||||
constexpr uint64_t total_blob_bytes = 123456; |
|
||||||
constexpr uint64_t garbage_blob_count = 1; |
|
||||||
constexpr uint64_t garbage_blob_bytes = 9876; |
|
||||||
const std::string checksum_method("SHA1"); |
|
||||||
const std::string checksum_value("bdb7f34a59dfa1592ce7f52e99f98c570c525cbd"); |
|
||||||
|
|
||||||
BlobFileState blob_file_state( |
|
||||||
blob_file_number, total_blob_count, total_blob_bytes, garbage_blob_count, |
|
||||||
garbage_blob_bytes, checksum_method, checksum_value); |
|
||||||
|
|
||||||
ASSERT_EQ(blob_file_state.GetBlobFileNumber(), blob_file_number); |
|
||||||
ASSERT_EQ(blob_file_state.GetTotalBlobCount(), total_blob_count); |
|
||||||
ASSERT_EQ(blob_file_state.GetTotalBlobBytes(), total_blob_bytes); |
|
||||||
ASSERT_EQ(blob_file_state.GetGarbageBlobCount(), garbage_blob_count); |
|
||||||
ASSERT_EQ(blob_file_state.GetGarbageBlobBytes(), garbage_blob_bytes); |
|
||||||
ASSERT_FALSE(blob_file_state.IsObsolete()); |
|
||||||
ASSERT_EQ(blob_file_state.GetChecksumMethod(), checksum_method); |
|
||||||
ASSERT_EQ(blob_file_state.GetChecksumValue(), checksum_value); |
|
||||||
|
|
||||||
TestEncodeDecode(blob_file_state); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(BlobFileStateTest, AddGarbageBlob) { |
|
||||||
constexpr uint64_t blob_file_number = 123; |
|
||||||
constexpr uint64_t total_blob_count = 2; |
|
||||||
constexpr uint64_t total_blob_bytes = 123456; |
|
||||||
const std::string checksum_method("MD5"); |
|
||||||
const std::string checksum_value("d8f72233c67a68c5ec2bd51c6be7556e"); |
|
||||||
|
|
||||||
BlobFileState blob_file_state(blob_file_number, total_blob_count, |
|
||||||
total_blob_bytes, checksum_method, |
|
||||||
checksum_value); |
|
||||||
|
|
||||||
ASSERT_EQ(blob_file_state.GetBlobFileNumber(), blob_file_number); |
|
||||||
ASSERT_EQ(blob_file_state.GetTotalBlobCount(), total_blob_count); |
|
||||||
ASSERT_EQ(blob_file_state.GetTotalBlobBytes(), total_blob_bytes); |
|
||||||
ASSERT_EQ(blob_file_state.GetGarbageBlobCount(), 0); |
|
||||||
ASSERT_EQ(blob_file_state.GetGarbageBlobBytes(), 0); |
|
||||||
ASSERT_FALSE(blob_file_state.IsObsolete()); |
|
||||||
ASSERT_EQ(blob_file_state.GetChecksumMethod(), checksum_method); |
|
||||||
ASSERT_EQ(blob_file_state.GetChecksumValue(), checksum_value); |
|
||||||
|
|
||||||
TestEncodeDecode(blob_file_state); |
|
||||||
|
|
||||||
blob_file_state.AddGarbageBlob(123000); |
|
||||||
|
|
||||||
ASSERT_EQ(blob_file_state.GetBlobFileNumber(), blob_file_number); |
|
||||||
ASSERT_EQ(blob_file_state.GetTotalBlobCount(), total_blob_count); |
|
||||||
ASSERT_EQ(blob_file_state.GetTotalBlobBytes(), total_blob_bytes); |
|
||||||
ASSERT_EQ(blob_file_state.GetGarbageBlobCount(), 1); |
|
||||||
ASSERT_EQ(blob_file_state.GetGarbageBlobBytes(), 123000); |
|
||||||
ASSERT_FALSE(blob_file_state.IsObsolete()); |
|
||||||
ASSERT_EQ(blob_file_state.GetChecksumMethod(), checksum_method); |
|
||||||
ASSERT_EQ(blob_file_state.GetChecksumValue(), checksum_value); |
|
||||||
|
|
||||||
TestEncodeDecode(blob_file_state); |
|
||||||
|
|
||||||
blob_file_state.AddGarbageBlob(456); |
|
||||||
|
|
||||||
ASSERT_EQ(blob_file_state.GetBlobFileNumber(), blob_file_number); |
|
||||||
ASSERT_EQ(blob_file_state.GetTotalBlobCount(), total_blob_count); |
|
||||||
ASSERT_EQ(blob_file_state.GetTotalBlobBytes(), total_blob_bytes); |
|
||||||
ASSERT_EQ(blob_file_state.GetGarbageBlobCount(), total_blob_count); |
|
||||||
ASSERT_EQ(blob_file_state.GetGarbageBlobBytes(), total_blob_bytes); |
|
||||||
ASSERT_TRUE(blob_file_state.IsObsolete()); |
|
||||||
ASSERT_EQ(blob_file_state.GetChecksumMethod(), checksum_method); |
|
||||||
ASSERT_EQ(blob_file_state.GetChecksumValue(), checksum_value); |
|
||||||
|
|
||||||
TestEncodeDecode(blob_file_state); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(BlobFileStateTest, DecodeErrors) { |
|
||||||
std::string str; |
|
||||||
Slice slice(str); |
|
||||||
|
|
||||||
BlobFileState blob_file_state; |
|
||||||
|
|
||||||
{ |
|
||||||
const Status s = blob_file_state.DecodeFrom(&slice); |
|
||||||
ASSERT_TRUE(s.IsCorruption()); |
|
||||||
ASSERT_TRUE(std::strstr(s.getState(), "blob file number")); |
|
||||||
} |
|
||||||
|
|
||||||
constexpr uint64_t blob_file_number = 123; |
|
||||||
PutVarint64(&str, blob_file_number); |
|
||||||
slice = str; |
|
||||||
|
|
||||||
{ |
|
||||||
const Status s = blob_file_state.DecodeFrom(&slice); |
|
||||||
ASSERT_TRUE(s.IsCorruption()); |
|
||||||
ASSERT_TRUE(std::strstr(s.getState(), "total blob count")); |
|
||||||
} |
|
||||||
|
|
||||||
constexpr uint64_t total_blob_count = 4567; |
|
||||||
PutVarint64(&str, total_blob_count); |
|
||||||
slice = str; |
|
||||||
|
|
||||||
{ |
|
||||||
const Status s = blob_file_state.DecodeFrom(&slice); |
|
||||||
ASSERT_TRUE(s.IsCorruption()); |
|
||||||
ASSERT_TRUE(std::strstr(s.getState(), "total blob bytes")); |
|
||||||
} |
|
||||||
|
|
||||||
constexpr uint64_t total_blob_bytes = 12345678; |
|
||||||
PutVarint64(&str, total_blob_bytes); |
|
||||||
slice = str; |
|
||||||
|
|
||||||
{ |
|
||||||
const Status s = blob_file_state.DecodeFrom(&slice); |
|
||||||
ASSERT_TRUE(s.IsCorruption()); |
|
||||||
ASSERT_TRUE(std::strstr(s.getState(), "garbage blob count")); |
|
||||||
} |
|
||||||
|
|
||||||
constexpr uint64_t garbage_blob_count = 1234; |
|
||||||
PutVarint64(&str, garbage_blob_count); |
|
||||||
slice = str; |
|
||||||
|
|
||||||
{ |
|
||||||
const Status s = blob_file_state.DecodeFrom(&slice); |
|
||||||
ASSERT_TRUE(s.IsCorruption()); |
|
||||||
ASSERT_TRUE(std::strstr(s.getState(), "garbage blob bytes")); |
|
||||||
} |
|
||||||
|
|
||||||
constexpr uint64_t garbage_blob_bytes = 5678; |
|
||||||
PutVarint64(&str, garbage_blob_bytes); |
|
||||||
slice = str; |
|
||||||
|
|
||||||
{ |
|
||||||
const Status s = blob_file_state.DecodeFrom(&slice); |
|
||||||
ASSERT_TRUE(s.IsCorruption()); |
|
||||||
ASSERT_TRUE(std::strstr(s.getState(), "checksum method")); |
|
||||||
} |
|
||||||
|
|
||||||
constexpr char checksum_method[] = "SHA1"; |
|
||||||
PutLengthPrefixedSlice(&str, checksum_method); |
|
||||||
slice = str; |
|
||||||
|
|
||||||
{ |
|
||||||
const Status s = blob_file_state.DecodeFrom(&slice); |
|
||||||
ASSERT_TRUE(s.IsCorruption()); |
|
||||||
ASSERT_TRUE(std::strstr(s.getState(), "checksum value")); |
|
||||||
} |
|
||||||
|
|
||||||
constexpr char checksum_value[] = "bdb7f34a59dfa1592ce7f52e99f98c570c525cbd"; |
|
||||||
PutLengthPrefixedSlice(&str, checksum_value); |
|
||||||
slice = str; |
|
||||||
|
|
||||||
{ |
|
||||||
const Status s = blob_file_state.DecodeFrom(&slice); |
|
||||||
ASSERT_TRUE(s.IsCorruption()); |
|
||||||
ASSERT_TRUE(std::strstr(s.getState(), "custom field tag")); |
|
||||||
} |
|
||||||
|
|
||||||
constexpr uint32_t custom_tag = 2; |
|
||||||
PutVarint32(&str, custom_tag); |
|
||||||
slice = str; |
|
||||||
|
|
||||||
{ |
|
||||||
const Status s = blob_file_state.DecodeFrom(&slice); |
|
||||||
ASSERT_TRUE(s.IsCorruption()); |
|
||||||
ASSERT_TRUE(std::strstr(s.getState(), "custom field value")); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(BlobFileStateTest, ForwardCompatibleCustomField) { |
|
||||||
SyncPoint::GetInstance()->SetCallBack( |
|
||||||
"BlobFileState::EncodeTo::CustomFields", [&](void* arg) { |
|
||||||
std::string* output = static_cast<std::string*>(arg); |
|
||||||
|
|
||||||
constexpr uint32_t forward_compatible_tag = 2; |
|
||||||
PutVarint32(output, forward_compatible_tag); |
|
||||||
|
|
||||||
PutLengthPrefixedSlice(output, "deadbeef"); |
|
||||||
}); |
|
||||||
SyncPoint::GetInstance()->EnableProcessing(); |
|
||||||
|
|
||||||
constexpr uint64_t blob_file_number = 678; |
|
||||||
constexpr uint64_t total_blob_count = 9999; |
|
||||||
constexpr uint64_t total_blob_bytes = 100000000; |
|
||||||
constexpr uint64_t garbage_blob_count = 3333; |
|
||||||
constexpr uint64_t garbage_blob_bytes = 2500000; |
|
||||||
const std::string checksum_method("CRC32"); |
|
||||||
const std::string checksum_value("3d87ff57"); |
|
||||||
|
|
||||||
BlobFileState blob_file_state( |
|
||||||
blob_file_number, total_blob_count, total_blob_bytes, garbage_blob_count, |
|
||||||
garbage_blob_bytes, checksum_method, checksum_value); |
|
||||||
|
|
||||||
TestEncodeDecode(blob_file_state); |
|
||||||
|
|
||||||
SyncPoint::GetInstance()->DisableProcessing(); |
|
||||||
SyncPoint::GetInstance()->ClearAllCallBacks(); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(BlobFileStateTest, ForwardIncompatibleCustomField) { |
|
||||||
SyncPoint::GetInstance()->SetCallBack( |
|
||||||
"BlobFileState::EncodeTo::CustomFields", [&](void* arg) { |
|
||||||
std::string* output = static_cast<std::string*>(arg); |
|
||||||
|
|
||||||
constexpr uint32_t forward_incompatible_tag = (1 << 6) + 1; |
|
||||||
PutVarint32(output, forward_incompatible_tag); |
|
||||||
|
|
||||||
PutLengthPrefixedSlice(output, "foobar"); |
|
||||||
}); |
|
||||||
SyncPoint::GetInstance()->EnableProcessing(); |
|
||||||
|
|
||||||
constexpr uint64_t blob_file_number = 456; |
|
||||||
constexpr uint64_t total_blob_count = 100; |
|
||||||
constexpr uint64_t total_blob_bytes = 2000000; |
|
||||||
const std::string checksum_method("CRC32B"); |
|
||||||
const std::string checksum_value("6dbdf23a"); |
|
||||||
|
|
||||||
BlobFileState blob_file_state(blob_file_number, total_blob_count, |
|
||||||
total_blob_bytes, checksum_method, |
|
||||||
checksum_value); |
|
||||||
|
|
||||||
std::string encoded; |
|
||||||
blob_file_state.EncodeTo(&encoded); |
|
||||||
|
|
||||||
BlobFileState decoded_blob_file_state; |
|
||||||
Slice input(encoded); |
|
||||||
const Status s = decoded_blob_file_state.DecodeFrom(&input); |
|
||||||
|
|
||||||
ASSERT_TRUE(s.IsCorruption()); |
|
||||||
ASSERT_TRUE(std::strstr(s.getState(), "Forward incompatible")); |
|
||||||
|
|
||||||
SyncPoint::GetInstance()->DisableProcessing(); |
|
||||||
SyncPoint::GetInstance()->ClearAllCallBacks(); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace ROCKSDB_NAMESPACE
|
|
||||||
|
|
||||||
int main(int argc, char** argv) { |
|
||||||
::testing::InitGoogleTest(&argc, argv); |
|
||||||
return RUN_ALL_TESTS(); |
|
||||||
} |
|
Loading…
Reference in new issue