1 //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 //
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
9 
10 #include "db/version_edit.h"
11 
12 #include "test_util/sync_point.h"
13 #include "test_util/testharness.h"
14 #include "test_util/testutil.h"
15 #include "util/coding.h"
16 #include "util/string_util.h"
17 
18 namespace ROCKSDB_NAMESPACE {
19 
TestEncodeDecode(const VersionEdit & edit)20 static void TestEncodeDecode(const VersionEdit& edit) {
21   std::string encoded, encoded2;
22   edit.EncodeTo(&encoded);
23   VersionEdit parsed;
24   Status s = parsed.DecodeFrom(encoded);
25   ASSERT_TRUE(s.ok()) << s.ToString();
26   parsed.EncodeTo(&encoded2);
27   ASSERT_EQ(encoded, encoded2);
28 }
29 
30 class VersionEditTest : public testing::Test {};
31 
TEST_F(VersionEditTest,EncodeDecode)32 TEST_F(VersionEditTest, EncodeDecode) {
33   static const uint64_t kBig = 1ull << 50;
34   static const uint32_t kBig32Bit = 1ull << 30;
35 
36   VersionEdit edit;
37   for (int i = 0; i < 4; i++) {
38     TestEncodeDecode(edit);
39     edit.AddFile(3, kBig + 300 + i, kBig32Bit + 400 + i, 0,
40                  InternalKey("foo", kBig + 500 + i, kTypeValue),
41                  InternalKey("zoo", kBig + 600 + i, kTypeDeletion),
42                  kBig + 500 + i, kBig + 600 + i, false, kInvalidBlobFileNumber,
43                  888, 678, "234", "crc32c");
44     edit.DeleteFile(4, kBig + 700 + i);
45   }
46 
47   edit.SetComparatorName("foo");
48   edit.SetLogNumber(kBig + 100);
49   edit.SetNextFile(kBig + 200);
50   edit.SetLastSequence(kBig + 1000);
51   TestEncodeDecode(edit);
52 }
53 
TEST_F(VersionEditTest,EncodeDecodeNewFile4)54 TEST_F(VersionEditTest, EncodeDecodeNewFile4) {
55   static const uint64_t kBig = 1ull << 50;
56 
57   VersionEdit edit;
58   edit.AddFile(3, 300, 3, 100, InternalKey("foo", kBig + 500, kTypeValue),
59                InternalKey("zoo", kBig + 600, kTypeDeletion), kBig + 500,
60                kBig + 600, true, kInvalidBlobFileNumber,
61                kUnknownOldestAncesterTime, kUnknownFileCreationTime,
62                kUnknownFileChecksum, kUnknownFileChecksumFuncName);
63   edit.AddFile(4, 301, 3, 100, InternalKey("foo", kBig + 501, kTypeValue),
64                InternalKey("zoo", kBig + 601, kTypeDeletion), kBig + 501,
65                kBig + 601, false, kInvalidBlobFileNumber,
66                kUnknownOldestAncesterTime, kUnknownFileCreationTime,
67                kUnknownFileChecksum, kUnknownFileChecksumFuncName);
68   edit.AddFile(5, 302, 0, 100, InternalKey("foo", kBig + 502, kTypeValue),
69                InternalKey("zoo", kBig + 602, kTypeDeletion), kBig + 502,
70                kBig + 602, true, kInvalidBlobFileNumber, 666, 888,
71                kUnknownFileChecksum, kUnknownFileChecksumFuncName);
72   edit.AddFile(5, 303, 0, 100, InternalKey("foo", kBig + 503, kTypeBlobIndex),
73                InternalKey("zoo", kBig + 603, kTypeBlobIndex), kBig + 503,
74                kBig + 603, true, 1001, kUnknownOldestAncesterTime,
75                kUnknownFileCreationTime, kUnknownFileChecksum,
76                kUnknownFileChecksumFuncName);
77   ;
78 
79   edit.DeleteFile(4, 700);
80 
81   edit.SetComparatorName("foo");
82   edit.SetLogNumber(kBig + 100);
83   edit.SetNextFile(kBig + 200);
84   edit.SetLastSequence(kBig + 1000);
85   TestEncodeDecode(edit);
86 
87   std::string encoded, encoded2;
88   edit.EncodeTo(&encoded);
89   VersionEdit parsed;
90   Status s = parsed.DecodeFrom(encoded);
91   ASSERT_TRUE(s.ok()) << s.ToString();
92   auto& new_files = parsed.GetNewFiles();
93   ASSERT_TRUE(new_files[0].second.marked_for_compaction);
94   ASSERT_TRUE(!new_files[1].second.marked_for_compaction);
95   ASSERT_TRUE(new_files[2].second.marked_for_compaction);
96   ASSERT_TRUE(new_files[3].second.marked_for_compaction);
97   ASSERT_EQ(3u, new_files[0].second.fd.GetPathId());
98   ASSERT_EQ(3u, new_files[1].second.fd.GetPathId());
99   ASSERT_EQ(0u, new_files[2].second.fd.GetPathId());
100   ASSERT_EQ(0u, new_files[3].second.fd.GetPathId());
101   ASSERT_EQ(kInvalidBlobFileNumber,
102             new_files[0].second.oldest_blob_file_number);
103   ASSERT_EQ(kInvalidBlobFileNumber,
104             new_files[1].second.oldest_blob_file_number);
105   ASSERT_EQ(kInvalidBlobFileNumber,
106             new_files[2].second.oldest_blob_file_number);
107   ASSERT_EQ(1001, new_files[3].second.oldest_blob_file_number);
108 }
109 
TEST_F(VersionEditTest,ForwardCompatibleNewFile4)110 TEST_F(VersionEditTest, ForwardCompatibleNewFile4) {
111   static const uint64_t kBig = 1ull << 50;
112   VersionEdit edit;
113   edit.AddFile(3, 300, 3, 100, InternalKey("foo", kBig + 500, kTypeValue),
114                InternalKey("zoo", kBig + 600, kTypeDeletion), kBig + 500,
115                kBig + 600, true, kInvalidBlobFileNumber,
116                kUnknownOldestAncesterTime, kUnknownFileCreationTime,
117                kUnknownFileChecksum, kUnknownFileChecksumFuncName);
118   edit.AddFile(4, 301, 3, 100, InternalKey("foo", kBig + 501, kTypeValue),
119                InternalKey("zoo", kBig + 601, kTypeDeletion), kBig + 501,
120                kBig + 601, false, kInvalidBlobFileNumber, 686, 868, "234",
121                "crc32c");
122   edit.DeleteFile(4, 700);
123 
124   edit.SetComparatorName("foo");
125   edit.SetLogNumber(kBig + 100);
126   edit.SetNextFile(kBig + 200);
127   edit.SetLastSequence(kBig + 1000);
128 
129   std::string encoded;
130 
131   // Call back function to add extra customized builds.
132   bool first = true;
133   ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
134       "VersionEdit::EncodeTo:NewFile4:CustomizeFields", [&](void* arg) {
135         std::string* str = reinterpret_cast<std::string*>(arg);
136         PutVarint32(str, 33);
137         const std::string str1 = "random_string";
138         PutLengthPrefixedSlice(str, str1);
139         if (first) {
140           first = false;
141           PutVarint32(str, 22);
142           const std::string str2 = "s";
143           PutLengthPrefixedSlice(str, str2);
144         }
145       });
146   ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
147   edit.EncodeTo(&encoded);
148   ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
149 
150   VersionEdit parsed;
151   Status s = parsed.DecodeFrom(encoded);
152   ASSERT_TRUE(s.ok()) << s.ToString();
153   ASSERT_TRUE(!first);
154   auto& new_files = parsed.GetNewFiles();
155   ASSERT_TRUE(new_files[0].second.marked_for_compaction);
156   ASSERT_TRUE(!new_files[1].second.marked_for_compaction);
157   ASSERT_EQ(3u, new_files[0].second.fd.GetPathId());
158   ASSERT_EQ(3u, new_files[1].second.fd.GetPathId());
159   ASSERT_EQ(1u, parsed.GetDeletedFiles().size());
160 }
161 
TEST_F(VersionEditTest,NewFile4NotSupportedField)162 TEST_F(VersionEditTest, NewFile4NotSupportedField) {
163   static const uint64_t kBig = 1ull << 50;
164   VersionEdit edit;
165   edit.AddFile(3, 300, 3, 100, InternalKey("foo", kBig + 500, kTypeValue),
166                InternalKey("zoo", kBig + 600, kTypeDeletion), kBig + 500,
167                kBig + 600, true, kInvalidBlobFileNumber,
168                kUnknownOldestAncesterTime, kUnknownFileCreationTime,
169                kUnknownFileChecksum, kUnknownFileChecksumFuncName);
170 
171   edit.SetComparatorName("foo");
172   edit.SetLogNumber(kBig + 100);
173   edit.SetNextFile(kBig + 200);
174   edit.SetLastSequence(kBig + 1000);
175 
176   std::string encoded;
177 
178   // Call back function to add extra customized builds.
179   ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
180       "VersionEdit::EncodeTo:NewFile4:CustomizeFields", [&](void* arg) {
181         std::string* str = reinterpret_cast<std::string*>(arg);
182         const std::string str1 = "s";
183         PutLengthPrefixedSlice(str, str1);
184       });
185   ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
186   edit.EncodeTo(&encoded);
187   ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
188 
189   VersionEdit parsed;
190   Status s = parsed.DecodeFrom(encoded);
191   ASSERT_NOK(s);
192 }
193 
TEST_F(VersionEditTest,EncodeEmptyFile)194 TEST_F(VersionEditTest, EncodeEmptyFile) {
195   VersionEdit edit;
196   edit.AddFile(0, 0, 0, 0, InternalKey(), InternalKey(), 0, 0, false,
197                kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
198                kUnknownFileCreationTime, kUnknownFileChecksum,
199                kUnknownFileChecksumFuncName);
200   std::string buffer;
201   ASSERT_TRUE(!edit.EncodeTo(&buffer));
202 }
203 
TEST_F(VersionEditTest,ColumnFamilyTest)204 TEST_F(VersionEditTest, ColumnFamilyTest) {
205   VersionEdit edit;
206   edit.SetColumnFamily(2);
207   edit.AddColumnFamily("column_family");
208   edit.SetMaxColumnFamily(5);
209   TestEncodeDecode(edit);
210 
211   edit.Clear();
212   edit.SetColumnFamily(3);
213   edit.DropColumnFamily();
214   TestEncodeDecode(edit);
215 }
216 
TEST_F(VersionEditTest,MinLogNumberToKeep)217 TEST_F(VersionEditTest, MinLogNumberToKeep) {
218   VersionEdit edit;
219   edit.SetMinLogNumberToKeep(13);
220   TestEncodeDecode(edit);
221 
222   edit.Clear();
223   edit.SetMinLogNumberToKeep(23);
224   TestEncodeDecode(edit);
225 }
226 
TEST_F(VersionEditTest,AtomicGroupTest)227 TEST_F(VersionEditTest, AtomicGroupTest) {
228   VersionEdit edit;
229   edit.MarkAtomicGroup(1);
230   TestEncodeDecode(edit);
231 }
232 
TEST_F(VersionEditTest,IgnorableField)233 TEST_F(VersionEditTest, IgnorableField) {
234   VersionEdit ve;
235   std::string encoded;
236 
237   // Size of ignorable field is too large
238   PutVarint32Varint64(&encoded, 2 /* kLogNumber */, 66);
239   // This is a customized ignorable tag
240   PutVarint32Varint64(&encoded,
241                       0x2710 /* A field with kTagSafeIgnoreMask set */,
242                       5 /* fieldlength 5 */);
243   encoded += "abc";  // Only fills 3 bytes,
244   ASSERT_NOK(ve.DecodeFrom(encoded));
245 
246   encoded.clear();
247   // Error when seeing unidentified tag that is not ignorable
248   PutVarint32Varint64(&encoded, 2 /* kLogNumber */, 66);
249   // This is a customized ignorable tag
250   PutVarint32Varint64(&encoded, 666 /* A field with kTagSafeIgnoreMask unset */,
251                       3 /* fieldlength 3 */);
252   encoded += "abc";  //  Fill 3 bytes
253   PutVarint32Varint64(&encoded, 3 /* next file number */, 88);
254   ASSERT_NOK(ve.DecodeFrom(encoded));
255 
256   // Safely ignore an identified but safely ignorable entry
257   encoded.clear();
258   PutVarint32Varint64(&encoded, 2 /* kLogNumber */, 66);
259   // This is a customized ignorable tag
260   PutVarint32Varint64(&encoded,
261                       0x2710 /* A field with kTagSafeIgnoreMask set */,
262                       3 /* fieldlength 3 */);
263   encoded += "abc";  //  Fill 3 bytes
264   PutVarint32Varint64(&encoded, 3 /* kNextFileNumber */, 88);
265 
266   ASSERT_OK(ve.DecodeFrom(encoded));
267 
268   ASSERT_TRUE(ve.HasLogNumber());
269   ASSERT_TRUE(ve.HasNextFile());
270   ASSERT_EQ(66, ve.GetLogNumber());
271   ASSERT_EQ(88, ve.GetNextFile());
272 }
273 
TEST_F(VersionEditTest,DbId)274 TEST_F(VersionEditTest, DbId) {
275   VersionEdit edit;
276   edit.SetDBId("ab34-cd12-435f-er00");
277   TestEncodeDecode(edit);
278 
279   edit.Clear();
280   edit.SetDBId("34ba-cd12-435f-er01");
281   TestEncodeDecode(edit);
282 }
283 
TEST_F(VersionEditTest,BlobFileAdditionAndGarbage)284 TEST_F(VersionEditTest, BlobFileAdditionAndGarbage) {
285   VersionEdit edit;
286 
287   const std::string checksum_method_prefix = "Hash";
288   const std::string checksum_value_prefix = "Value";
289 
290   for (uint64_t blob_file_number = 1; blob_file_number <= 10;
291        ++blob_file_number) {
292     const uint64_t total_blob_count = blob_file_number << 10;
293     const uint64_t total_blob_bytes = blob_file_number << 20;
294 
295     std::string checksum_method(checksum_method_prefix);
296     AppendNumberTo(&checksum_method, blob_file_number);
297 
298     std::string checksum_value(checksum_value_prefix);
299     AppendNumberTo(&checksum_value, blob_file_number);
300 
301     edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
302                      checksum_method, checksum_value);
303 
304     const uint64_t garbage_blob_count = total_blob_count >> 2;
305     const uint64_t garbage_blob_bytes = total_blob_bytes >> 1;
306 
307     edit.AddBlobFileGarbage(blob_file_number, garbage_blob_count,
308                             garbage_blob_bytes);
309   }
310 
311   TestEncodeDecode(edit);
312 }
313 
TEST_F(VersionEditTest,AddWalEncodeDecode)314 TEST_F(VersionEditTest, AddWalEncodeDecode) {
315   VersionEdit edit;
316   for (uint64_t log_number = 1; log_number <= 20; log_number++) {
317     WalMetadata meta;
318     bool has_size = rand() % 2 == 0;
319     if (has_size) {
320       meta.SetSyncedSizeInBytes(rand() % 1000);
321     }
322     edit.AddWal(log_number, meta);
323   }
324   TestEncodeDecode(edit);
325 }
326 
PrefixEncodedWalAdditionWithLength(const std::string & encoded)327 static std::string PrefixEncodedWalAdditionWithLength(
328     const std::string& encoded) {
329   std::string ret;
330   PutVarint32(&ret, Tag::kWalAddition2);
331   PutLengthPrefixedSlice(&ret, encoded);
332   return ret;
333 }
334 
TEST_F(VersionEditTest,AddWalDecodeBadLogNumber)335 TEST_F(VersionEditTest, AddWalDecodeBadLogNumber) {
336   std::string encoded;
337 
338   {
339     // No log number.
340     std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
341     VersionEdit edit;
342     Status s = edit.DecodeFrom(encoded_edit);
343     ASSERT_TRUE(s.IsCorruption());
344     ASSERT_TRUE(s.ToString().find("Error decoding WAL log number") !=
345                 std::string::npos)
346         << s.ToString();
347   }
348 
349   {
350     // log number should be varint64,
351     // but we only encode 128 which is not a valid representation of varint64.
352     char c = 0;
353     unsigned char* ptr = reinterpret_cast<unsigned char*>(&c);
354     *ptr = 128;
355     encoded.append(1, c);
356 
357     std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
358     VersionEdit edit;
359     Status s = edit.DecodeFrom(encoded_edit);
360     ASSERT_TRUE(s.IsCorruption());
361     ASSERT_TRUE(s.ToString().find("Error decoding WAL log number") !=
362                 std::string::npos)
363         << s.ToString();
364   }
365 }
366 
TEST_F(VersionEditTest,AddWalDecodeBadTag)367 TEST_F(VersionEditTest, AddWalDecodeBadTag) {
368   constexpr WalNumber kLogNumber = 100;
369   constexpr uint64_t kSizeInBytes = 100;
370 
371   std::string encoded;
372   PutVarint64(&encoded, kLogNumber);
373 
374   {
375     // No tag.
376     std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
377     VersionEdit edit;
378     Status s = edit.DecodeFrom(encoded_edit);
379     ASSERT_TRUE(s.IsCorruption());
380     ASSERT_TRUE(s.ToString().find("Error decoding tag") != std::string::npos)
381         << s.ToString();
382   }
383 
384   {
385     // Only has size tag, no terminate tag.
386     std::string encoded_with_size = encoded;
387     PutVarint32(&encoded_with_size,
388                 static_cast<uint32_t>(WalAdditionTag::kSyncedSize));
389     PutVarint64(&encoded_with_size, kSizeInBytes);
390 
391     std::string encoded_edit =
392         PrefixEncodedWalAdditionWithLength(encoded_with_size);
393     VersionEdit edit;
394     Status s = edit.DecodeFrom(encoded_edit);
395     ASSERT_TRUE(s.IsCorruption());
396     ASSERT_TRUE(s.ToString().find("Error decoding tag") != std::string::npos)
397         << s.ToString();
398   }
399 
400   {
401     // Only has terminate tag.
402     std::string encoded_with_terminate = encoded;
403     PutVarint32(&encoded_with_terminate,
404                 static_cast<uint32_t>(WalAdditionTag::kTerminate));
405 
406     std::string encoded_edit =
407         PrefixEncodedWalAdditionWithLength(encoded_with_terminate);
408     VersionEdit edit;
409     ASSERT_OK(edit.DecodeFrom(encoded_edit));
410     auto& wal_addition = edit.GetWalAdditions()[0];
411     ASSERT_EQ(wal_addition.GetLogNumber(), kLogNumber);
412     ASSERT_FALSE(wal_addition.GetMetadata().HasSyncedSize());
413   }
414 }
415 
TEST_F(VersionEditTest,AddWalDecodeNoSize)416 TEST_F(VersionEditTest, AddWalDecodeNoSize) {
417   constexpr WalNumber kLogNumber = 100;
418 
419   std::string encoded;
420   PutVarint64(&encoded, kLogNumber);
421   PutVarint32(&encoded, static_cast<uint32_t>(WalAdditionTag::kSyncedSize));
422   // No real size after the size tag.
423 
424   {
425     // Without terminate tag.
426     std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
427     VersionEdit edit;
428     Status s = edit.DecodeFrom(encoded_edit);
429     ASSERT_TRUE(s.IsCorruption());
430     ASSERT_TRUE(s.ToString().find("Error decoding WAL file size") !=
431                 std::string::npos)
432         << s.ToString();
433   }
434 
435   {
436     // With terminate tag.
437     PutVarint32(&encoded, static_cast<uint32_t>(WalAdditionTag::kTerminate));
438 
439     std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
440     VersionEdit edit;
441     Status s = edit.DecodeFrom(encoded_edit);
442     ASSERT_TRUE(s.IsCorruption());
443     // The terminate tag is misunderstood as the size.
444     ASSERT_TRUE(s.ToString().find("Error decoding tag") != std::string::npos)
445         << s.ToString();
446   }
447 }
448 
TEST_F(VersionEditTest,AddWalDebug)449 TEST_F(VersionEditTest, AddWalDebug) {
450   constexpr int n = 2;
451   constexpr std::array<uint64_t, n> kLogNumbers{{10, 20}};
452   constexpr std::array<uint64_t, n> kSizeInBytes{{100, 200}};
453 
454   VersionEdit edit;
455   for (int i = 0; i < n; i++) {
456     edit.AddWal(kLogNumbers[i], WalMetadata(kSizeInBytes[i]));
457   }
458 
459   const WalAdditions& wals = edit.GetWalAdditions();
460 
461   ASSERT_TRUE(edit.IsWalAddition());
462   ASSERT_EQ(wals.size(), n);
463   for (int i = 0; i < n; i++) {
464     const WalAddition& wal = wals[i];
465     ASSERT_EQ(wal.GetLogNumber(), kLogNumbers[i]);
466     ASSERT_EQ(wal.GetMetadata().GetSyncedSizeInBytes(), kSizeInBytes[i]);
467   }
468 
469   std::string expected_str = "VersionEdit {\n";
470   for (int i = 0; i < n; i++) {
471     std::stringstream ss;
472     ss << "  WalAddition: log_number: " << kLogNumbers[i]
473        << " synced_size_in_bytes: " << kSizeInBytes[i] << "\n";
474     expected_str += ss.str();
475   }
476   expected_str += "  ColumnFamily: 0\n}\n";
477   ASSERT_EQ(edit.DebugString(true), expected_str);
478 
479   std::string expected_json = "{\"EditNumber\": 4, \"WalAdditions\": [";
480   for (int i = 0; i < n; i++) {
481     std::stringstream ss;
482     ss << "{\"LogNumber\": " << kLogNumbers[i] << ", "
483        << "\"SyncedSizeInBytes\": " << kSizeInBytes[i] << "}";
484     if (i < n - 1) ss << ", ";
485     expected_json += ss.str();
486   }
487   expected_json += "], \"ColumnFamily\": 0}";
488   ASSERT_EQ(edit.DebugJSON(4, true), expected_json);
489 }
490 
TEST_F(VersionEditTest,DeleteWalEncodeDecode)491 TEST_F(VersionEditTest, DeleteWalEncodeDecode) {
492   VersionEdit edit;
493   edit.DeleteWalsBefore(rand() % 100);
494   TestEncodeDecode(edit);
495 }
496 
TEST_F(VersionEditTest,DeleteWalDebug)497 TEST_F(VersionEditTest, DeleteWalDebug) {
498   constexpr int n = 2;
499   constexpr std::array<uint64_t, n> kLogNumbers{{10, 20}};
500 
501   VersionEdit edit;
502   edit.DeleteWalsBefore(kLogNumbers[n - 1]);
503 
504   const WalDeletion& wal = edit.GetWalDeletion();
505 
506   ASSERT_TRUE(edit.IsWalDeletion());
507   ASSERT_EQ(wal.GetLogNumber(), kLogNumbers[n - 1]);
508 
509   std::string expected_str = "VersionEdit {\n";
510   {
511     std::stringstream ss;
512     ss << "  WalDeletion: log_number: " << kLogNumbers[n - 1] << "\n";
513     expected_str += ss.str();
514   }
515   expected_str += "  ColumnFamily: 0\n}\n";
516   ASSERT_EQ(edit.DebugString(true), expected_str);
517 
518   std::string expected_json = "{\"EditNumber\": 4, \"WalDeletion\": ";
519   {
520     std::stringstream ss;
521     ss << "{\"LogNumber\": " << kLogNumbers[n - 1] << "}";
522     expected_json += ss.str();
523   }
524   expected_json += ", \"ColumnFamily\": 0}";
525   ASSERT_EQ(edit.DebugJSON(4, true), expected_json);
526 }
527 
TEST_F(VersionEditTest,FullHistoryTsLow)528 TEST_F(VersionEditTest, FullHistoryTsLow) {
529   VersionEdit edit;
530   ASSERT_FALSE(edit.HasFullHistoryTsLow());
531   std::string ts = test::EncodeInt(0);
532   edit.SetFullHistoryTsLow(ts);
533   TestEncodeDecode(edit);
534 }
535 
536 // Tests that if RocksDB is downgraded, the new types of VersionEdits
537 // that have a tag larger than kTagSafeIgnoreMask can be safely ignored.
TEST_F(VersionEditTest,IgnorableTags)538 TEST_F(VersionEditTest, IgnorableTags) {
539   SyncPoint::GetInstance()->SetCallBack(
540       "VersionEdit::EncodeTo:IgnoreIgnorableTags", [&](void* arg) {
541         bool* ignore = static_cast<bool*>(arg);
542         *ignore = true;
543       });
544   SyncPoint::GetInstance()->EnableProcessing();
545 
546   constexpr uint64_t kPrevLogNumber = 100;
547   constexpr uint64_t kLogNumber = 200;
548   constexpr uint64_t kNextFileNumber = 300;
549   constexpr uint64_t kColumnFamilyId = 400;
550 
551   VersionEdit edit;
552   // Add some ignorable entries.
553   for (int i = 0; i < 2; i++) {
554     edit.AddWal(i + 1, WalMetadata(i + 2));
555   }
556   edit.SetDBId("db_id");
557   // Add unignorable entries.
558   edit.SetPrevLogNumber(kPrevLogNumber);
559   edit.SetLogNumber(kLogNumber);
560   // Add more ignorable entries.
561   edit.DeleteWalsBefore(100);
562   // Add unignorable entry.
563   edit.SetNextFile(kNextFileNumber);
564   // Add more ignorable entries.
565   edit.SetFullHistoryTsLow("ts");
566   // Add unignorable entry.
567   edit.SetColumnFamily(kColumnFamilyId);
568 
569   std::string encoded;
570   ASSERT_TRUE(edit.EncodeTo(&encoded));
571 
572   VersionEdit decoded;
573   ASSERT_OK(decoded.DecodeFrom(encoded));
574 
575   // Check that all ignorable entries are ignored.
576   ASSERT_FALSE(decoded.HasDbId());
577   ASSERT_FALSE(decoded.HasFullHistoryTsLow());
578   ASSERT_FALSE(decoded.IsWalAddition());
579   ASSERT_FALSE(decoded.IsWalDeletion());
580   ASSERT_TRUE(decoded.GetWalAdditions().empty());
581   ASSERT_TRUE(decoded.GetWalDeletion().IsEmpty());
582 
583   // Check that unignorable entries are still present.
584   ASSERT_EQ(edit.GetPrevLogNumber(), kPrevLogNumber);
585   ASSERT_EQ(edit.GetLogNumber(), kLogNumber);
586   ASSERT_EQ(edit.GetNextFile(), kNextFileNumber);
587   ASSERT_EQ(edit.GetColumnFamily(), kColumnFamilyId);
588 
589   SyncPoint::GetInstance()->DisableProcessing();
590 }
591 
592 }  // namespace ROCKSDB_NAMESPACE
593 
main(int argc,char ** argv)594 int main(int argc, char** argv) {
595   ::testing::InitGoogleTest(&argc, argv);
596   return RUN_ALL_TESTS();
597 }
598