// 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). // // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. #include "db/version_edit.h" #include "test_util/sync_point.h" #include "test_util/testharness.h" #include "util/coding.h" #include "util/string_util.h" namespace ROCKSDB_NAMESPACE { static void TestEncodeDecode(const VersionEdit& edit) { std::string encoded, encoded2; edit.EncodeTo(&encoded); VersionEdit parsed; Status s = parsed.DecodeFrom(encoded); ASSERT_TRUE(s.ok()) << s.ToString(); parsed.EncodeTo(&encoded2); ASSERT_EQ(encoded, encoded2); } class VersionEditTest : public testing::Test {}; TEST_F(VersionEditTest, EncodeDecode) { static const uint64_t kBig = 1ull << 50; static const uint32_t kBig32Bit = 1ull << 30; VersionEdit edit; for (int i = 0; i < 4; i++) { TestEncodeDecode(edit); edit.AddFile(3, kBig + 300 + i, kBig32Bit + 400 + i, 0, InternalKey("foo", kBig + 500 + i, kTypeValue), InternalKey("zoo", kBig + 600 + i, kTypeDeletion), kBig + 500 + i, kBig + 600 + i, false, kInvalidBlobFileNumber, 888, 678, "234", "crc32c"); edit.DeleteFile(4, kBig + 700 + i); } edit.SetComparatorName("foo"); edit.SetLogNumber(kBig + 100); edit.SetNextFile(kBig + 200); edit.SetLastSequence(kBig + 1000); edit.SetStateUponManifestSwitch(true); TestEncodeDecode(edit); } TEST_F(VersionEditTest, EncodeDecodeNewFile4) { static const uint64_t kBig = 1ull << 50; VersionEdit edit; edit.AddFile(3, 300, 3, 100, InternalKey("foo", kBig + 500, kTypeValue), InternalKey("zoo", kBig + 600, kTypeDeletion), kBig + 500, kBig + 600, true, kInvalidBlobFileNumber, kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownFileChecksum, kUnknownFileChecksumFuncName); edit.AddFile(4, 301, 3, 100, InternalKey("foo", kBig + 501, kTypeValue), InternalKey("zoo", kBig + 601, kTypeDeletion), kBig + 501, kBig + 601, false, kInvalidBlobFileNumber, kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownFileChecksum, kUnknownFileChecksumFuncName); edit.AddFile(5, 302, 0, 100, InternalKey("foo", kBig + 502, kTypeValue), InternalKey("zoo", kBig + 602, kTypeDeletion), kBig + 502, kBig + 602, true, kInvalidBlobFileNumber, 666, 888, kUnknownFileChecksum, kUnknownFileChecksumFuncName); edit.AddFile(5, 303, 0, 100, InternalKey("foo", kBig + 503, kTypeBlobIndex), InternalKey("zoo", kBig + 603, kTypeBlobIndex), kBig + 503, kBig + 603, true, 1001, kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownFileChecksum, kUnknownFileChecksumFuncName); ; edit.DeleteFile(4, 700); edit.SetComparatorName("foo"); edit.SetLogNumber(kBig + 100); edit.SetNextFile(kBig + 200); edit.SetLastSequence(kBig + 1000); edit.SetStateUponManifestSwitch(true); TestEncodeDecode(edit); std::string encoded, encoded2; edit.EncodeTo(&encoded); VersionEdit parsed; Status s = parsed.DecodeFrom(encoded); ASSERT_TRUE(s.ok()) << s.ToString(); auto& new_files = parsed.GetNewFiles(); ASSERT_TRUE(new_files[0].second.marked_for_compaction); ASSERT_TRUE(!new_files[1].second.marked_for_compaction); ASSERT_TRUE(new_files[2].second.marked_for_compaction); ASSERT_TRUE(new_files[3].second.marked_for_compaction); ASSERT_EQ(3u, new_files[0].second.fd.GetPathId()); ASSERT_EQ(3u, new_files[1].second.fd.GetPathId()); ASSERT_EQ(0u, new_files[2].second.fd.GetPathId()); ASSERT_EQ(0u, new_files[3].second.fd.GetPathId()); ASSERT_EQ(kInvalidBlobFileNumber, new_files[0].second.oldest_blob_file_number); ASSERT_EQ(kInvalidBlobFileNumber, new_files[1].second.oldest_blob_file_number); ASSERT_EQ(kInvalidBlobFileNumber, new_files[2].second.oldest_blob_file_number); ASSERT_EQ(1001, new_files[3].second.oldest_blob_file_number); ASSERT_TRUE(parsed.GetStateUponManifestSwitch()); } TEST_F(VersionEditTest, ForwardCompatibleNewFile4) { static const uint64_t kBig = 1ull << 50; VersionEdit edit; edit.AddFile(3, 300, 3, 100, InternalKey("foo", kBig + 500, kTypeValue), InternalKey("zoo", kBig + 600, kTypeDeletion), kBig + 500, kBig + 600, true, kInvalidBlobFileNumber, kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownFileChecksum, kUnknownFileChecksumFuncName); edit.AddFile(4, 301, 3, 100, InternalKey("foo", kBig + 501, kTypeValue), InternalKey("zoo", kBig + 601, kTypeDeletion), kBig + 501, kBig + 601, false, kInvalidBlobFileNumber, 686, 868, "234", "crc32c"); edit.DeleteFile(4, 700); edit.SetComparatorName("foo"); edit.SetLogNumber(kBig + 100); edit.SetNextFile(kBig + 200); edit.SetLastSequence(kBig + 1000); std::string encoded; // Call back function to add extra customized builds. bool first = true; ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( "VersionEdit::EncodeTo:NewFile4:CustomizeFields", [&](void* arg) { std::string* str = reinterpret_cast(arg); PutVarint32(str, 33); const std::string str1 = "random_string"; PutLengthPrefixedSlice(str, str1); if (first) { first = false; PutVarint32(str, 22); const std::string str2 = "s"; PutLengthPrefixedSlice(str, str2); } }); ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); edit.EncodeTo(&encoded); ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); VersionEdit parsed; Status s = parsed.DecodeFrom(encoded); ASSERT_TRUE(s.ok()) << s.ToString(); ASSERT_TRUE(!first); auto& new_files = parsed.GetNewFiles(); ASSERT_TRUE(new_files[0].second.marked_for_compaction); ASSERT_TRUE(!new_files[1].second.marked_for_compaction); ASSERT_EQ(3u, new_files[0].second.fd.GetPathId()); ASSERT_EQ(3u, new_files[1].second.fd.GetPathId()); ASSERT_EQ(1u, parsed.GetDeletedFiles().size()); } TEST_F(VersionEditTest, NewFile4NotSupportedField) { static const uint64_t kBig = 1ull << 50; VersionEdit edit; edit.AddFile(3, 300, 3, 100, InternalKey("foo", kBig + 500, kTypeValue), InternalKey("zoo", kBig + 600, kTypeDeletion), kBig + 500, kBig + 600, true, kInvalidBlobFileNumber, kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownFileChecksum, kUnknownFileChecksumFuncName); edit.SetComparatorName("foo"); edit.SetLogNumber(kBig + 100); edit.SetNextFile(kBig + 200); edit.SetLastSequence(kBig + 1000); std::string encoded; // Call back function to add extra customized builds. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( "VersionEdit::EncodeTo:NewFile4:CustomizeFields", [&](void* arg) { std::string* str = reinterpret_cast(arg); const std::string str1 = "s"; PutLengthPrefixedSlice(str, str1); }); ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); edit.EncodeTo(&encoded); ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); VersionEdit parsed; Status s = parsed.DecodeFrom(encoded); ASSERT_NOK(s); } TEST_F(VersionEditTest, EncodeEmptyFile) { VersionEdit edit; edit.AddFile(0, 0, 0, 0, InternalKey(), InternalKey(), 0, 0, false, kInvalidBlobFileNumber, kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownFileChecksum, kUnknownFileChecksumFuncName); std::string buffer; ASSERT_TRUE(!edit.EncodeTo(&buffer)); } TEST_F(VersionEditTest, ColumnFamilyTest) { VersionEdit edit; edit.SetColumnFamily(2); edit.AddColumnFamily("column_family"); edit.SetMaxColumnFamily(5); TestEncodeDecode(edit); edit.Clear(); edit.SetColumnFamily(3); edit.DropColumnFamily(); TestEncodeDecode(edit); } TEST_F(VersionEditTest, MinLogNumberToKeep) { VersionEdit edit; edit.SetMinLogNumberToKeep(13); TestEncodeDecode(edit); edit.Clear(); edit.SetMinLogNumberToKeep(23); TestEncodeDecode(edit); } TEST_F(VersionEditTest, AtomicGroupTest) { VersionEdit edit; edit.MarkAtomicGroup(1); TestEncodeDecode(edit); } TEST_F(VersionEditTest, IgnorableField) { VersionEdit ve; std::string encoded; // Size of ignorable field is too large PutVarint32Varint64(&encoded, 2 /* kLogNumber */, 66); // This is a customized ignorable tag PutVarint32Varint64(&encoded, 0x2710 /* A field with kTagSafeIgnoreMask set */, 5 /* fieldlength 5 */); encoded += "abc"; // Only fills 3 bytes, ASSERT_NOK(ve.DecodeFrom(encoded)); encoded.clear(); // Error when seeing unidentified tag that is not ignorable PutVarint32Varint64(&encoded, 2 /* kLogNumber */, 66); // This is a customized ignorable tag PutVarint32Varint64(&encoded, 666 /* A field with kTagSafeIgnoreMask unset */, 3 /* fieldlength 3 */); encoded += "abc"; // Fill 3 bytes PutVarint32Varint64(&encoded, 3 /* next file number */, 88); ASSERT_NOK(ve.DecodeFrom(encoded)); // Safely ignore an identified but safely ignorable entry encoded.clear(); PutVarint32Varint64(&encoded, 2 /* kLogNumber */, 66); // This is a customized ignorable tag PutVarint32Varint64(&encoded, 0x2710 /* A field with kTagSafeIgnoreMask set */, 3 /* fieldlength 3 */); encoded += "abc"; // Fill 3 bytes PutVarint32Varint64(&encoded, 3 /* kNextFileNumber */, 88); ASSERT_OK(ve.DecodeFrom(encoded)); ASSERT_TRUE(ve.HasLogNumber()); ASSERT_TRUE(ve.HasNextFile()); ASSERT_EQ(66, ve.GetLogNumber()); ASSERT_EQ(88, ve.GetNextFile()); } TEST_F(VersionEditTest, DbId) { VersionEdit edit; edit.SetDBId("ab34-cd12-435f-er00"); TestEncodeDecode(edit); edit.Clear(); edit.SetDBId("34ba-cd12-435f-er01"); TestEncodeDecode(edit); } TEST_F(VersionEditTest, ManifestSwitchTag) { VersionEdit edit1, decode1; edit1.SetStateUponManifestSwitch(true); TestEncodeDecode(edit1); std::string encoded1; edit1.EncodeTo(&encoded1); ASSERT_OK(decode1.DecodeFrom(encoded1)); ASSERT_TRUE(decode1.GetStateUponManifestSwitch()); ASSERT_TRUE(!decode1.GetManifestSwitched()); VersionEdit edit2, decode2; edit2.SetManifestSwitched(true); TestEncodeDecode(edit2); std::string encoded2; edit2.EncodeTo(&encoded2); ASSERT_OK(decode2.DecodeFrom(encoded2)); ASSERT_TRUE(!decode2.GetStateUponManifestSwitch()); ASSERT_TRUE(decode2.GetManifestSwitched()); VersionEdit edit3, decode3; edit3.SetStateUponManifestSwitch(true); edit3.SetManifestSwitched(true); TestEncodeDecode(edit3); std::string encoded3; edit3.EncodeTo(&encoded3); ASSERT_OK(decode3.DecodeFrom(encoded3)); ASSERT_TRUE(decode3.GetStateUponManifestSwitch()); ASSERT_TRUE(decode3.GetManifestSwitched()); } TEST_F(VersionEditTest, BlobFileAdditionAndGarbage) { VersionEdit edit; const std::string checksum_method_prefix = "Hash"; const std::string checksum_value_prefix = "Value"; for (uint64_t blob_file_number = 1; blob_file_number <= 10; ++blob_file_number) { const uint64_t total_blob_count = blob_file_number << 10; const uint64_t total_blob_bytes = blob_file_number << 20; std::string checksum_method(checksum_method_prefix); AppendNumberTo(&checksum_method, blob_file_number); std::string checksum_value(checksum_value_prefix); AppendNumberTo(&checksum_value, blob_file_number); edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes, checksum_method, checksum_value); const uint64_t garbage_blob_count = total_blob_count >> 2; const uint64_t garbage_blob_bytes = total_blob_bytes >> 1; edit.AddBlobFileGarbage(blob_file_number, garbage_blob_count, garbage_blob_bytes); } TestEncodeDecode(edit); } } // namespace ROCKSDB_NAMESPACE int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }