version_edit_test.cc 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136
  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. #include "db/version_edit.h"
  10. #include "db/blob/blob_index.h"
  11. #include "rocksdb/advanced_options.h"
  12. #include "table/unique_id_impl.h"
  13. #include "test_util/sync_point.h"
  14. #include "test_util/testharness.h"
  15. #include "test_util/testutil.h"
  16. #include "util/coding.h"
  17. #include "util/string_util.h"
  18. namespace ROCKSDB_NAMESPACE {
  19. static void TestEncodeDecode(const VersionEdit& edit) {
  20. // Encoding one `VersionEdit` and decoding it again should result in the
  21. // exact same `VersionEdit`. However, a special handling is applied to file
  22. // boundaries: `FileMetaData.smallest`, `FileMetaData.largest` when
  23. // user-defined timestamps should not be persisted. In that scenario, this
  24. // invariant does not hold. We disable this scenario in this util method to
  25. // enable all other test cases continue to verify this invariant, while the
  26. // special case is separately covered in test
  27. // `EncodeDecodeNewFile4HandleFileBoundary`.
  28. std::string encoded, encoded2;
  29. edit.EncodeTo(&encoded, 0 /* ts_sz */);
  30. VersionEdit parsed;
  31. Status s = parsed.DecodeFrom(encoded);
  32. ASSERT_TRUE(s.ok()) << s.ToString();
  33. parsed.EncodeTo(&encoded2, 0 /* ts_sz */);
  34. ASSERT_EQ(encoded, encoded2);
  35. }
  36. class VersionEditTest : public testing::Test {};
  37. TEST_F(VersionEditTest, EncodeDecode) {
  38. static const uint64_t kBig = 1ull << 50;
  39. static const uint32_t kBig32Bit = 1ull << 30;
  40. VersionEdit edit;
  41. for (int i = 0; i < 4; i++) {
  42. TestEncodeDecode(edit);
  43. edit.AddFile(3, kBig + 300 + i, kBig32Bit + 400 + i, 0,
  44. InternalKey("foo", kBig + 500 + i, kTypeValue),
  45. InternalKey("zoo", kBig + 600 + i, kTypeDeletion),
  46. kBig + 500 + i, kBig + 600 + i, false, Temperature::kUnknown,
  47. kInvalidBlobFileNumber, 888, 678,
  48. kBig + 300 + i /* epoch_number */, "234", "crc32c",
  49. kNullUniqueId64x2, 0, 0, true);
  50. edit.DeleteFile(4, kBig + 700 + i);
  51. }
  52. edit.SetComparatorName("foo");
  53. edit.SetPersistUserDefinedTimestamps(true);
  54. edit.SetLogNumber(kBig + 100);
  55. edit.SetNextFile(kBig + 200);
  56. edit.SetLastSequence(kBig + 1000);
  57. TestEncodeDecode(edit);
  58. }
  59. TEST_F(VersionEditTest, EncodeDecodeNewFile4) {
  60. static const uint64_t kBig = 1ull << 50;
  61. VersionEdit edit;
  62. edit.AddFile(3, 300, 3, 100, InternalKey("foo", kBig + 500, kTypeValue),
  63. InternalKey("zoo", kBig + 600, kTypeDeletion), kBig + 500,
  64. kBig + 600, true, Temperature::kUnknown, kInvalidBlobFileNumber,
  65. kUnknownOldestAncesterTime, kUnknownFileCreationTime,
  66. 300 /* epoch_number */, kUnknownFileChecksum,
  67. kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, true);
  68. edit.AddFile(4, 301, 3, 100, InternalKey("foo", kBig + 501, kTypeValue),
  69. InternalKey("zoo", kBig + 601, kTypeDeletion), kBig + 501,
  70. kBig + 601, false, Temperature::kUnknown, kInvalidBlobFileNumber,
  71. kUnknownOldestAncesterTime, kUnknownFileCreationTime,
  72. 301 /* epoch_number */, kUnknownFileChecksum,
  73. kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, false);
  74. edit.AddFile(5, 302, 0, 100, InternalKey("foo", kBig + 502, kTypeValue),
  75. InternalKey("zoo", kBig + 602, kTypeDeletion), kBig + 502,
  76. kBig + 602, true, Temperature::kUnknown, kInvalidBlobFileNumber,
  77. 666, 888, 302 /* epoch_number */, kUnknownFileChecksum,
  78. kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, true);
  79. edit.AddFile(5, 303, 0, 100, InternalKey("foo", kBig + 503, kTypeBlobIndex),
  80. InternalKey("zoo", kBig + 603, kTypeBlobIndex), kBig + 503,
  81. kBig + 603, true, Temperature::kUnknown, 1001,
  82. kUnknownOldestAncesterTime, kUnknownFileCreationTime,
  83. 303 /* epoch_number */, kUnknownFileChecksum,
  84. kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, true);
  85. edit.DeleteFile(4, 700);
  86. edit.SetComparatorName("foo");
  87. edit.SetPersistUserDefinedTimestamps(false);
  88. edit.SetLogNumber(kBig + 100);
  89. edit.SetNextFile(kBig + 200);
  90. edit.SetLastSequence(kBig + 1000);
  91. TestEncodeDecode(edit);
  92. std::string encoded, encoded2;
  93. edit.EncodeTo(&encoded, 0 /* ts_sz */);
  94. VersionEdit parsed;
  95. Status s = parsed.DecodeFrom(encoded);
  96. ASSERT_TRUE(s.ok()) << s.ToString();
  97. auto& new_files = parsed.GetNewFiles();
  98. ASSERT_TRUE(new_files[0].second.marked_for_compaction);
  99. ASSERT_FALSE(new_files[1].second.marked_for_compaction);
  100. ASSERT_TRUE(new_files[2].second.marked_for_compaction);
  101. ASSERT_TRUE(new_files[3].second.marked_for_compaction);
  102. ASSERT_EQ(3u, new_files[0].second.fd.GetPathId());
  103. ASSERT_EQ(3u, new_files[1].second.fd.GetPathId());
  104. ASSERT_EQ(0u, new_files[2].second.fd.GetPathId());
  105. ASSERT_EQ(0u, new_files[3].second.fd.GetPathId());
  106. ASSERT_EQ(kInvalidBlobFileNumber,
  107. new_files[0].second.oldest_blob_file_number);
  108. ASSERT_EQ(kInvalidBlobFileNumber,
  109. new_files[1].second.oldest_blob_file_number);
  110. ASSERT_EQ(kInvalidBlobFileNumber,
  111. new_files[2].second.oldest_blob_file_number);
  112. ASSERT_EQ(1001, new_files[3].second.oldest_blob_file_number);
  113. ASSERT_TRUE(new_files[0].second.user_defined_timestamps_persisted);
  114. ASSERT_FALSE(new_files[1].second.user_defined_timestamps_persisted);
  115. ASSERT_TRUE(new_files[2].second.user_defined_timestamps_persisted);
  116. ASSERT_TRUE(new_files[3].second.user_defined_timestamps_persisted);
  117. ASSERT_FALSE(parsed.GetPersistUserDefinedTimestamps());
  118. }
  119. TEST_F(VersionEditTest, EncodeDecodeNewFile4HandleFileBoundary) {
  120. static const uint64_t kBig = 1ull << 50;
  121. size_t ts_sz = 16;
  122. static std::string min_ts(ts_sz, static_cast<unsigned char>(0));
  123. VersionEdit edit;
  124. std::string smallest = "foo";
  125. std::string largest = "zoo";
  126. // In real manifest writing scenarios, one `VersionEdit` should not contain
  127. // files with different `user_defined_timestamps_persisted` flag value.
  128. // This is just for testing file boundaries handling w.r.t persisting user
  129. // defined timestamps during `VersionEdit` encoding.
  130. edit.AddFile(
  131. 3, 300, 3, 100, InternalKey(smallest + min_ts, kBig + 500, kTypeValue),
  132. InternalKey(largest + min_ts, kBig + 600, kTypeDeletion), kBig + 500,
  133. kBig + 600, true, Temperature::kUnknown, kInvalidBlobFileNumber,
  134. kUnknownOldestAncesterTime, kUnknownFileCreationTime,
  135. 300 /* epoch_number */, kUnknownFileChecksum,
  136. kUnknownFileChecksumFuncName, kNullUniqueId64x2,
  137. 0 /* compensated_range_deletion_size */, 0 /* tail_size */,
  138. false /* user_defined_timestamps_persisted */);
  139. edit.AddFile(3, 300, 3, 100,
  140. InternalKey(smallest + min_ts, kBig + 500, kTypeValue),
  141. InternalKey(largest + min_ts, kBig + 600, kTypeDeletion),
  142. kBig + 500, kBig + 600, true, Temperature::kUnknown,
  143. kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
  144. kUnknownFileCreationTime, 300 /* epoch_number */,
  145. kUnknownFileChecksum, kUnknownFileChecksumFuncName,
  146. kNullUniqueId64x2, 0 /* compensated_range_deletion_size */,
  147. 0 /* tail_size */, true /* user_defined_timestamps_persisted */);
  148. std::string encoded;
  149. edit.EncodeTo(&encoded, ts_sz);
  150. VersionEdit parsed;
  151. Status s = parsed.DecodeFrom(encoded);
  152. ASSERT_TRUE(s.ok()) << s.ToString();
  153. auto& new_files = parsed.GetNewFiles();
  154. ASSERT_TRUE(new_files.size() == 2);
  155. ASSERT_FALSE(new_files[0].second.user_defined_timestamps_persisted);
  156. // First file's boundaries do not contain user-defined timestamps.
  157. ASSERT_EQ(InternalKey(smallest, kBig + 500, kTypeValue).Encode(),
  158. new_files[0].second.smallest.Encode());
  159. ASSERT_EQ(InternalKey(largest, kBig + 600, kTypeDeletion).Encode(),
  160. new_files[0].second.largest.Encode());
  161. ASSERT_TRUE(new_files[1].second.user_defined_timestamps_persisted);
  162. // Second file's boundaries contain user-defined timestamps.
  163. ASSERT_EQ(InternalKey(smallest + min_ts, kBig + 500, kTypeValue).Encode(),
  164. new_files[1].second.smallest.Encode());
  165. ASSERT_EQ(InternalKey(largest + min_ts, kBig + 600, kTypeDeletion).Encode(),
  166. new_files[1].second.largest.Encode());
  167. }
  168. TEST_F(VersionEditTest, ForwardCompatibleNewFile4) {
  169. static const uint64_t kBig = 1ull << 50;
  170. VersionEdit edit;
  171. edit.AddFile(3, 300, 3, 100, InternalKey("foo", kBig + 500, kTypeValue),
  172. InternalKey("zoo", kBig + 600, kTypeDeletion), kBig + 500,
  173. kBig + 600, true, Temperature::kUnknown, kInvalidBlobFileNumber,
  174. kUnknownOldestAncesterTime, kUnknownFileCreationTime,
  175. 300 /* epoch_number */, kUnknownFileChecksum,
  176. kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, true);
  177. edit.AddFile(4, 301, 3, 100, InternalKey("foo", kBig + 501, kTypeValue),
  178. InternalKey("zoo", kBig + 601, kTypeDeletion), kBig + 501,
  179. kBig + 601, false, Temperature::kUnknown, kInvalidBlobFileNumber,
  180. 686, 868, 301 /* epoch_number */, "234", "crc32c",
  181. kNullUniqueId64x2, 0, 0, true);
  182. edit.DeleteFile(4, 700);
  183. edit.SetComparatorName("foo");
  184. edit.SetPersistUserDefinedTimestamps(true);
  185. edit.SetLogNumber(kBig + 100);
  186. edit.SetNextFile(kBig + 200);
  187. edit.SetLastSequence(kBig + 1000);
  188. std::string encoded;
  189. // Call back function to add extra customized builds.
  190. bool first = true;
  191. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  192. "VersionEdit::EncodeTo:NewFile4:CustomizeFields", [&](void* arg) {
  193. std::string* str = reinterpret_cast<std::string*>(arg);
  194. PutVarint32(str, 33);
  195. const std::string str1 = "random_string";
  196. PutLengthPrefixedSlice(str, str1);
  197. if (first) {
  198. first = false;
  199. PutVarint32(str, 22);
  200. const std::string str2 = "s";
  201. PutLengthPrefixedSlice(str, str2);
  202. }
  203. });
  204. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  205. edit.EncodeTo(&encoded, 0 /* ts_sz */);
  206. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  207. VersionEdit parsed;
  208. Status s = parsed.DecodeFrom(encoded);
  209. ASSERT_TRUE(s.ok()) << s.ToString();
  210. ASSERT_TRUE(!first);
  211. auto& new_files = parsed.GetNewFiles();
  212. ASSERT_TRUE(new_files[0].second.marked_for_compaction);
  213. ASSERT_TRUE(!new_files[1].second.marked_for_compaction);
  214. ASSERT_EQ(3u, new_files[0].second.fd.GetPathId());
  215. ASSERT_EQ(3u, new_files[1].second.fd.GetPathId());
  216. ASSERT_EQ(1u, parsed.GetDeletedFiles().size());
  217. ASSERT_TRUE(parsed.GetPersistUserDefinedTimestamps());
  218. }
  219. TEST_F(VersionEditTest, NewFile4NotSupportedField) {
  220. static const uint64_t kBig = 1ull << 50;
  221. VersionEdit edit;
  222. edit.AddFile(3, 300, 3, 100, InternalKey("foo", kBig + 500, kTypeValue),
  223. InternalKey("zoo", kBig + 600, kTypeDeletion), kBig + 500,
  224. kBig + 600, true, Temperature::kUnknown, kInvalidBlobFileNumber,
  225. kUnknownOldestAncesterTime, kUnknownFileCreationTime,
  226. 300 /* epoch_number */, kUnknownFileChecksum,
  227. kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, false);
  228. edit.SetComparatorName("foo");
  229. edit.SetPersistUserDefinedTimestamps(false);
  230. edit.SetLogNumber(kBig + 100);
  231. edit.SetNextFile(kBig + 200);
  232. edit.SetLastSequence(kBig + 1000);
  233. std::string encoded;
  234. // Call back function to add extra customized builds.
  235. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  236. "VersionEdit::EncodeTo:NewFile4:CustomizeFields", [&](void* arg) {
  237. std::string* str = reinterpret_cast<std::string*>(arg);
  238. const std::string str1 = "s";
  239. PutLengthPrefixedSlice(str, str1);
  240. });
  241. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  242. edit.EncodeTo(&encoded, 0 /* ts_sz */);
  243. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  244. VersionEdit parsed;
  245. Status s = parsed.DecodeFrom(encoded);
  246. ASSERT_NOK(s);
  247. }
  248. TEST_F(VersionEditTest, EncodeEmptyFile) {
  249. VersionEdit edit;
  250. edit.AddFile(0, 0, 0, 0, InternalKey(), InternalKey(), 0, 0, false,
  251. Temperature::kUnknown, kInvalidBlobFileNumber,
  252. kUnknownOldestAncesterTime, kUnknownFileCreationTime,
  253. 1 /*epoch_number*/, kUnknownFileChecksum,
  254. kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, true);
  255. std::string buffer;
  256. ASSERT_TRUE(!edit.EncodeTo(&buffer, 0 /* ts_sz */));
  257. }
  258. TEST_F(VersionEditTest, ColumnFamilyTest) {
  259. VersionEdit edit;
  260. edit.SetColumnFamily(2);
  261. edit.AddColumnFamily("column_family");
  262. edit.SetMaxColumnFamily(5);
  263. TestEncodeDecode(edit);
  264. edit.Clear();
  265. edit.SetColumnFamily(3);
  266. edit.DropColumnFamily();
  267. TestEncodeDecode(edit);
  268. }
  269. TEST_F(VersionEditTest, MinLogNumberToKeep) {
  270. VersionEdit edit;
  271. edit.SetMinLogNumberToKeep(13);
  272. TestEncodeDecode(edit);
  273. edit.Clear();
  274. edit.SetMinLogNumberToKeep(23);
  275. TestEncodeDecode(edit);
  276. }
  277. TEST_F(VersionEditTest, AtomicGroupTest) {
  278. VersionEdit edit;
  279. edit.MarkAtomicGroup(1);
  280. TestEncodeDecode(edit);
  281. }
  282. TEST_F(VersionEditTest, IgnorableField) {
  283. VersionEdit ve;
  284. std::string encoded;
  285. // Size of ignorable field is too large
  286. PutVarint32Varint64(&encoded, 2 /* kLogNumber */, 66);
  287. // This is a customized ignorable tag
  288. PutVarint32Varint64(&encoded,
  289. 0x2710 /* A field with kTagSafeIgnoreMask set */,
  290. 5 /* fieldlength 5 */);
  291. encoded += "abc"; // Only fills 3 bytes,
  292. ASSERT_NOK(ve.DecodeFrom(encoded));
  293. encoded.clear();
  294. // Error when seeing unidentified tag that is not ignorable
  295. PutVarint32Varint64(&encoded, 2 /* kLogNumber */, 66);
  296. // This is a customized ignorable tag
  297. PutVarint32Varint64(&encoded, 666 /* A field with kTagSafeIgnoreMask unset */,
  298. 3 /* fieldlength 3 */);
  299. encoded += "abc"; // Fill 3 bytes
  300. PutVarint32Varint64(&encoded, 3 /* next file number */, 88);
  301. ASSERT_NOK(ve.DecodeFrom(encoded));
  302. // Safely ignore an identified but safely ignorable entry
  303. encoded.clear();
  304. PutVarint32Varint64(&encoded, 2 /* kLogNumber */, 66);
  305. // This is a customized ignorable tag
  306. PutVarint32Varint64(&encoded,
  307. 0x2710 /* A field with kTagSafeIgnoreMask set */,
  308. 3 /* fieldlength 3 */);
  309. encoded += "abc"; // Fill 3 bytes
  310. PutVarint32Varint64(&encoded, 3 /* kNextFileNumber */, 88);
  311. ASSERT_OK(ve.DecodeFrom(encoded));
  312. ASSERT_TRUE(ve.HasLogNumber());
  313. ASSERT_TRUE(ve.HasNextFile());
  314. ASSERT_EQ(66, ve.GetLogNumber());
  315. ASSERT_EQ(88, ve.GetNextFile());
  316. }
  317. TEST_F(VersionEditTest, DbId) {
  318. VersionEdit edit;
  319. edit.SetDBId("ab34-cd12-435f-er00");
  320. TestEncodeDecode(edit);
  321. edit.Clear();
  322. edit.SetDBId("34ba-cd12-435f-er01");
  323. TestEncodeDecode(edit);
  324. }
  325. TEST_F(VersionEditTest, BlobFileAdditionAndGarbage) {
  326. VersionEdit edit;
  327. const std::string checksum_method_prefix = "Hash";
  328. const std::string checksum_value_prefix = "Value";
  329. for (uint64_t blob_file_number = 1; blob_file_number <= 10;
  330. ++blob_file_number) {
  331. const uint64_t total_blob_count = blob_file_number << 10;
  332. const uint64_t total_blob_bytes = blob_file_number << 20;
  333. std::string checksum_method(checksum_method_prefix);
  334. AppendNumberTo(&checksum_method, blob_file_number);
  335. std::string checksum_value(checksum_value_prefix);
  336. AppendNumberTo(&checksum_value, blob_file_number);
  337. edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
  338. checksum_method, checksum_value);
  339. const uint64_t garbage_blob_count = total_blob_count >> 2;
  340. const uint64_t garbage_blob_bytes = total_blob_bytes >> 1;
  341. edit.AddBlobFileGarbage(blob_file_number, garbage_blob_count,
  342. garbage_blob_bytes);
  343. }
  344. TestEncodeDecode(edit);
  345. }
  346. TEST_F(VersionEditTest, AddWalEncodeDecode) {
  347. VersionEdit edit;
  348. for (uint64_t log_number = 1; log_number <= 20; log_number++) {
  349. WalMetadata meta;
  350. bool has_size = rand() % 2 == 0;
  351. if (has_size) {
  352. meta.SetSyncedSizeInBytes(rand() % 1000);
  353. }
  354. edit.AddWal(log_number, meta);
  355. }
  356. TestEncodeDecode(edit);
  357. }
  358. static std::string PrefixEncodedWalAdditionWithLength(
  359. const std::string& encoded) {
  360. std::string ret;
  361. PutVarint32(&ret, Tag::kWalAddition2);
  362. PutLengthPrefixedSlice(&ret, encoded);
  363. return ret;
  364. }
  365. TEST_F(VersionEditTest, AddWalDecodeBadLogNumber) {
  366. std::string encoded;
  367. {
  368. // No log number.
  369. std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
  370. VersionEdit edit;
  371. Status s = edit.DecodeFrom(encoded_edit);
  372. ASSERT_TRUE(s.IsCorruption());
  373. ASSERT_TRUE(s.ToString().find("Error decoding WAL log number") !=
  374. std::string::npos)
  375. << s.ToString();
  376. }
  377. {
  378. // log number should be varint64,
  379. // but we only encode 128 which is not a valid representation of varint64.
  380. char c = 0;
  381. unsigned char* ptr = reinterpret_cast<unsigned char*>(&c);
  382. *ptr = 128;
  383. encoded.append(1, c);
  384. std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
  385. VersionEdit edit;
  386. Status s = edit.DecodeFrom(encoded_edit);
  387. ASSERT_TRUE(s.IsCorruption());
  388. ASSERT_TRUE(s.ToString().find("Error decoding WAL log number") !=
  389. std::string::npos)
  390. << s.ToString();
  391. }
  392. }
  393. TEST_F(VersionEditTest, AddWalDecodeBadTag) {
  394. constexpr WalNumber kLogNumber = 100;
  395. constexpr uint64_t kSizeInBytes = 100;
  396. std::string encoded;
  397. PutVarint64(&encoded, kLogNumber);
  398. {
  399. // No tag.
  400. std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
  401. VersionEdit edit;
  402. Status s = edit.DecodeFrom(encoded_edit);
  403. ASSERT_TRUE(s.IsCorruption());
  404. ASSERT_TRUE(s.ToString().find("Error decoding tag") != std::string::npos)
  405. << s.ToString();
  406. }
  407. {
  408. // Only has size tag, no terminate tag.
  409. std::string encoded_with_size = encoded;
  410. PutVarint32(&encoded_with_size,
  411. static_cast<uint32_t>(WalAdditionTag::kSyncedSize));
  412. PutVarint64(&encoded_with_size, kSizeInBytes);
  413. std::string encoded_edit =
  414. PrefixEncodedWalAdditionWithLength(encoded_with_size);
  415. VersionEdit edit;
  416. Status s = edit.DecodeFrom(encoded_edit);
  417. ASSERT_TRUE(s.IsCorruption());
  418. ASSERT_TRUE(s.ToString().find("Error decoding tag") != std::string::npos)
  419. << s.ToString();
  420. }
  421. {
  422. // Only has terminate tag.
  423. std::string encoded_with_terminate = encoded;
  424. PutVarint32(&encoded_with_terminate,
  425. static_cast<uint32_t>(WalAdditionTag::kTerminate));
  426. std::string encoded_edit =
  427. PrefixEncodedWalAdditionWithLength(encoded_with_terminate);
  428. VersionEdit edit;
  429. ASSERT_OK(edit.DecodeFrom(encoded_edit));
  430. auto& wal_addition = edit.GetWalAdditions()[0];
  431. ASSERT_EQ(wal_addition.GetLogNumber(), kLogNumber);
  432. ASSERT_FALSE(wal_addition.GetMetadata().HasSyncedSize());
  433. }
  434. }
  435. TEST_F(VersionEditTest, AddWalDecodeNoSize) {
  436. constexpr WalNumber kLogNumber = 100;
  437. std::string encoded;
  438. PutVarint64(&encoded, kLogNumber);
  439. PutVarint32(&encoded, static_cast<uint32_t>(WalAdditionTag::kSyncedSize));
  440. // No real size after the size tag.
  441. {
  442. // Without terminate tag.
  443. std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
  444. VersionEdit edit;
  445. Status s = edit.DecodeFrom(encoded_edit);
  446. ASSERT_TRUE(s.IsCorruption());
  447. ASSERT_TRUE(s.ToString().find("Error decoding WAL file size") !=
  448. std::string::npos)
  449. << s.ToString();
  450. }
  451. {
  452. // With terminate tag.
  453. PutVarint32(&encoded, static_cast<uint32_t>(WalAdditionTag::kTerminate));
  454. std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
  455. VersionEdit edit;
  456. Status s = edit.DecodeFrom(encoded_edit);
  457. ASSERT_TRUE(s.IsCorruption());
  458. // The terminate tag is misunderstood as the size.
  459. ASSERT_TRUE(s.ToString().find("Error decoding tag") != std::string::npos)
  460. << s.ToString();
  461. }
  462. }
  463. TEST_F(VersionEditTest, AddWalDebug) {
  464. constexpr int n = 2;
  465. constexpr std::array<uint64_t, n> kLogNumbers{{10, 20}};
  466. constexpr std::array<uint64_t, n> kSizeInBytes{{100, 200}};
  467. VersionEdit edit;
  468. for (int i = 0; i < n; i++) {
  469. edit.AddWal(kLogNumbers[i], WalMetadata(kSizeInBytes[i]));
  470. }
  471. const WalAdditions& wals = edit.GetWalAdditions();
  472. ASSERT_TRUE(edit.IsWalAddition());
  473. ASSERT_EQ(wals.size(), n);
  474. for (int i = 0; i < n; i++) {
  475. const WalAddition& wal = wals[i];
  476. ASSERT_EQ(wal.GetLogNumber(), kLogNumbers[i]);
  477. ASSERT_EQ(wal.GetMetadata().GetSyncedSizeInBytes(), kSizeInBytes[i]);
  478. }
  479. std::string expected_str = "VersionEdit {\n";
  480. for (int i = 0; i < n; i++) {
  481. std::stringstream ss;
  482. ss << " WalAddition: log_number: " << kLogNumbers[i]
  483. << " synced_size_in_bytes: " << kSizeInBytes[i] << "\n";
  484. expected_str += ss.str();
  485. }
  486. expected_str += " ColumnFamily: 0\n}\n";
  487. ASSERT_EQ(edit.DebugString(true), expected_str);
  488. std::string expected_json = "{\"EditNumber\": 4, \"WalAdditions\": [";
  489. for (int i = 0; i < n; i++) {
  490. std::stringstream ss;
  491. ss << "{\"LogNumber\": " << kLogNumbers[i] << ", "
  492. << "\"SyncedSizeInBytes\": " << kSizeInBytes[i] << "}";
  493. if (i < n - 1) {
  494. ss << ", ";
  495. }
  496. expected_json += ss.str();
  497. }
  498. expected_json += "], \"ColumnFamily\": 0}";
  499. ASSERT_EQ(edit.DebugJSON(4, true), expected_json);
  500. }
  501. TEST_F(VersionEditTest, DeleteWalEncodeDecode) {
  502. VersionEdit edit;
  503. edit.DeleteWalsBefore(rand() % 100);
  504. TestEncodeDecode(edit);
  505. }
  506. TEST_F(VersionEditTest, DeleteWalDebug) {
  507. constexpr int n = 2;
  508. constexpr std::array<uint64_t, n> kLogNumbers{{10, 20}};
  509. VersionEdit edit;
  510. edit.DeleteWalsBefore(kLogNumbers[n - 1]);
  511. const WalDeletion& wal = edit.GetWalDeletion();
  512. ASSERT_TRUE(edit.IsWalDeletion());
  513. ASSERT_EQ(wal.GetLogNumber(), kLogNumbers[n - 1]);
  514. std::string expected_str = "VersionEdit {\n";
  515. {
  516. std::stringstream ss;
  517. ss << " WalDeletion: log_number: " << kLogNumbers[n - 1] << "\n";
  518. expected_str += ss.str();
  519. }
  520. expected_str += " ColumnFamily: 0\n}\n";
  521. ASSERT_EQ(edit.DebugString(true), expected_str);
  522. std::string expected_json = "{\"EditNumber\": 4, \"WalDeletion\": ";
  523. {
  524. std::stringstream ss;
  525. ss << "{\"LogNumber\": " << kLogNumbers[n - 1] << "}";
  526. expected_json += ss.str();
  527. }
  528. expected_json += ", \"ColumnFamily\": 0}";
  529. ASSERT_EQ(edit.DebugJSON(4, true), expected_json);
  530. }
  531. TEST_F(VersionEditTest, FullHistoryTsLow) {
  532. VersionEdit edit;
  533. ASSERT_FALSE(edit.HasFullHistoryTsLow());
  534. std::string ts = test::EncodeInt(0);
  535. edit.SetFullHistoryTsLow(ts);
  536. TestEncodeDecode(edit);
  537. }
  538. // Tests that if RocksDB is downgraded, the new types of VersionEdits
  539. // that have a tag larger than kTagSafeIgnoreMask can be safely ignored.
  540. TEST_F(VersionEditTest, IgnorableTags) {
  541. SyncPoint::GetInstance()->SetCallBack(
  542. "VersionEdit::EncodeTo:IgnoreIgnorableTags", [&](void* arg) {
  543. bool* ignore = static_cast<bool*>(arg);
  544. *ignore = true;
  545. });
  546. SyncPoint::GetInstance()->EnableProcessing();
  547. constexpr uint64_t kPrevLogNumber = 100;
  548. constexpr uint64_t kLogNumber = 200;
  549. constexpr uint64_t kNextFileNumber = 300;
  550. constexpr uint64_t kColumnFamilyId = 400;
  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. std::string encoded;
  569. ASSERT_TRUE(edit.EncodeTo(&encoded, 0 /* ts_sz */));
  570. VersionEdit decoded;
  571. ASSERT_OK(decoded.DecodeFrom(encoded));
  572. // Check that all ignorable entries are ignored.
  573. ASSERT_FALSE(decoded.HasDbId());
  574. ASSERT_FALSE(decoded.HasFullHistoryTsLow());
  575. ASSERT_FALSE(decoded.IsWalAddition());
  576. ASSERT_FALSE(decoded.IsWalDeletion());
  577. ASSERT_TRUE(decoded.GetWalAdditions().empty());
  578. ASSERT_TRUE(decoded.GetWalDeletion().IsEmpty());
  579. // Check that unignorable entries are still present.
  580. ASSERT_EQ(edit.GetPrevLogNumber(), kPrevLogNumber);
  581. ASSERT_EQ(edit.GetLogNumber(), kLogNumber);
  582. ASSERT_EQ(edit.GetNextFile(), kNextFileNumber);
  583. ASSERT_EQ(edit.GetColumnFamily(), kColumnFamilyId);
  584. SyncPoint::GetInstance()->DisableProcessing();
  585. }
  586. TEST(FileMetaDataTest, UpdateBoundariesBlobIndex) {
  587. FileMetaData meta;
  588. {
  589. constexpr uint64_t file_number = 10;
  590. constexpr uint32_t path_id = 0;
  591. constexpr uint64_t file_size = 0;
  592. meta.fd = FileDescriptor(file_number, path_id, file_size);
  593. }
  594. constexpr char key[] = "foo";
  595. constexpr uint64_t expected_oldest_blob_file_number = 20;
  596. // Plain old value (does not affect oldest_blob_file_number)
  597. {
  598. constexpr char value[] = "value";
  599. constexpr SequenceNumber seq = 200;
  600. ASSERT_OK(meta.UpdateBoundaries(key, value, seq, kTypeValue));
  601. ASSERT_EQ(meta.oldest_blob_file_number, kInvalidBlobFileNumber);
  602. }
  603. // Non-inlined, non-TTL blob index (sets oldest_blob_file_number)
  604. {
  605. constexpr uint64_t blob_file_number = 25;
  606. static_assert(blob_file_number > expected_oldest_blob_file_number,
  607. "unexpected");
  608. constexpr uint64_t offset = 1000;
  609. constexpr uint64_t size = 100;
  610. std::string blob_index;
  611. BlobIndex::EncodeBlob(&blob_index, blob_file_number, offset, size,
  612. kNoCompression);
  613. constexpr SequenceNumber seq = 201;
  614. ASSERT_OK(meta.UpdateBoundaries(key, blob_index, seq, kTypeBlobIndex));
  615. ASSERT_EQ(meta.oldest_blob_file_number, blob_file_number);
  616. }
  617. // Another one, with the oldest blob file number (updates
  618. // oldest_blob_file_number)
  619. {
  620. constexpr uint64_t offset = 2000;
  621. constexpr uint64_t size = 300;
  622. std::string blob_index;
  623. BlobIndex::EncodeBlob(&blob_index, expected_oldest_blob_file_number, offset,
  624. size, kNoCompression);
  625. constexpr SequenceNumber seq = 202;
  626. ASSERT_OK(meta.UpdateBoundaries(key, blob_index, seq, kTypeBlobIndex));
  627. ASSERT_EQ(meta.oldest_blob_file_number, expected_oldest_blob_file_number);
  628. }
  629. // Inlined TTL blob index (does not affect oldest_blob_file_number)
  630. {
  631. constexpr uint64_t expiration = 9876543210;
  632. constexpr char value[] = "value";
  633. std::string blob_index;
  634. BlobIndex::EncodeInlinedTTL(&blob_index, expiration, value);
  635. constexpr SequenceNumber seq = 203;
  636. ASSERT_OK(meta.UpdateBoundaries(key, blob_index, seq, kTypeBlobIndex));
  637. ASSERT_EQ(meta.oldest_blob_file_number, expected_oldest_blob_file_number);
  638. }
  639. // Non-inlined TTL blob index (does not affect oldest_blob_file_number, even
  640. // though file number is smaller)
  641. {
  642. constexpr uint64_t expiration = 9876543210;
  643. constexpr uint64_t blob_file_number = 15;
  644. static_assert(blob_file_number < expected_oldest_blob_file_number,
  645. "unexpected");
  646. constexpr uint64_t offset = 2000;
  647. constexpr uint64_t size = 500;
  648. std::string blob_index;
  649. BlobIndex::EncodeBlobTTL(&blob_index, expiration, blob_file_number, offset,
  650. size, kNoCompression);
  651. constexpr SequenceNumber seq = 204;
  652. ASSERT_OK(meta.UpdateBoundaries(key, blob_index, seq, kTypeBlobIndex));
  653. ASSERT_EQ(meta.oldest_blob_file_number, expected_oldest_blob_file_number);
  654. }
  655. // Corrupt blob index
  656. {
  657. constexpr char corrupt_blob_index[] = "!corrupt!";
  658. constexpr SequenceNumber seq = 205;
  659. ASSERT_TRUE(
  660. meta.UpdateBoundaries(key, corrupt_blob_index, seq, kTypeBlobIndex)
  661. .IsCorruption());
  662. ASSERT_EQ(meta.oldest_blob_file_number, expected_oldest_blob_file_number);
  663. }
  664. // Invalid blob file number
  665. {
  666. constexpr uint64_t offset = 10000;
  667. constexpr uint64_t size = 1000;
  668. std::string blob_index;
  669. BlobIndex::EncodeBlob(&blob_index, kInvalidBlobFileNumber, offset, size,
  670. kNoCompression);
  671. constexpr SequenceNumber seq = 206;
  672. ASSERT_TRUE(meta.UpdateBoundaries(key, blob_index, seq, kTypeBlobIndex)
  673. .IsCorruption());
  674. ASSERT_EQ(meta.oldest_blob_file_number, expected_oldest_blob_file_number);
  675. }
  676. }
  677. class SubcompactionProgressTest : public VersionEditTest {
  678. protected:
  679. static constexpr uint64_t kTestFileSize = 1024;
  680. static constexpr SequenceNumber kTestSmallestSeq = 50;
  681. static constexpr SequenceNumber kTestLargestSeq = 150;
  682. static constexpr uint64_t kTestOldestAncesterTime = 12345;
  683. static constexpr uint64_t kTestFileCreationTime = 67890;
  684. static constexpr uint64_t kTestEpochNumber = 10;
  685. static const std::string kTestChecksumFuncName;
  686. FileMetaData CreateTestFile(uint64_t file_number, const std::string& prefix) {
  687. FileMetaData file;
  688. file.fd = FileDescriptor(file_number, 0, kTestFileSize, kTestSmallestSeq,
  689. kTestLargestSeq);
  690. file.smallest = InternalKey(prefix + "a", kTestSmallestSeq, kTypeValue);
  691. file.largest = InternalKey(prefix + "z", kTestLargestSeq, kTypeValue);
  692. file.oldest_ancester_time = kTestOldestAncesterTime;
  693. file.file_creation_time = kTestFileCreationTime;
  694. file.epoch_number = kTestEpochNumber;
  695. file.file_checksum = "checksum_" + std::to_string(file_number);
  696. file.file_checksum_func_name = kTestChecksumFuncName;
  697. file.marked_for_compaction = false;
  698. file.temperature = Temperature::kUnknown;
  699. return file;
  700. }
  701. // Store external file metadata objects for testing
  702. // These simulate files owned by CompactionOutputs
  703. std::vector<FileMetaData> compaction_output_files_;
  704. std::vector<FileMetaData> proximal_level_compaction_output_files_;
  705. SubcompactionProgress CreateSubcompactionProgress(
  706. const std::string& next_key, uint64_t num_processed_input_records,
  707. uint64_t num_processed_output_records,
  708. uint64_t num_processed_proximal_level_output_records,
  709. const std::vector<uint64_t>& output_file_numbers = {},
  710. const std::vector<uint64_t>& proximal_file_numbers = {},
  711. const std::string& file_prefix = "file_") {
  712. SubcompactionProgress progress;
  713. progress.next_internal_key_to_compact = next_key;
  714. progress.num_processed_input_records = num_processed_input_records;
  715. progress.output_level_progress.SetNumProcessedOutputRecords(
  716. num_processed_output_records);
  717. progress.proximal_output_level_progress.SetNumProcessedOutputRecords(
  718. num_processed_proximal_level_output_records);
  719. for (uint64_t file_num : output_file_numbers) {
  720. FileMetaData file = CreateTestFile(file_num, file_prefix + "output_");
  721. progress.output_level_progress.AddToOutputFiles(file);
  722. }
  723. for (uint64_t file_num : proximal_file_numbers) {
  724. FileMetaData file = CreateTestFile(file_num, file_prefix + "proximal_");
  725. progress.proximal_output_level_progress.AddToOutputFiles(file);
  726. }
  727. return progress;
  728. }
  729. std::pair<const VersionEdit, const SubcompactionProgress>
  730. EncodeDecodeProgress(const SubcompactionProgress& progress) {
  731. VersionEdit edit;
  732. edit.SetSubcompactionProgress(progress);
  733. std::string encoded;
  734. EXPECT_TRUE(edit.EncodeTo(&encoded, 0 /* ts_sz */));
  735. VersionEdit decoded_edit;
  736. EXPECT_OK(decoded_edit.DecodeFrom(encoded));
  737. EXPECT_TRUE(decoded_edit.HasSubcompactionProgress());
  738. SubcompactionProgress decoded_progress =
  739. decoded_edit.GetSubcompactionProgress();
  740. return {std::move(decoded_edit), std::move(decoded_progress)};
  741. }
  742. void VerifyFileMetaDataEquality(const FileMetaData& expected,
  743. const FileMetaData& actual) {
  744. // Verify the major fields only
  745. ASSERT_EQ(actual.fd.GetNumber(), expected.fd.GetNumber());
  746. ASSERT_EQ(actual.fd.GetFileSize(), expected.fd.GetFileSize());
  747. ASSERT_EQ(actual.smallest.Encode(), expected.smallest.Encode());
  748. ASSERT_EQ(actual.largest.Encode(), expected.largest.Encode());
  749. ASSERT_EQ(actual.oldest_ancester_time, expected.oldest_ancester_time);
  750. ASSERT_EQ(actual.file_creation_time, expected.file_creation_time);
  751. ASSERT_EQ(actual.epoch_number, expected.epoch_number);
  752. ASSERT_EQ(actual.file_checksum, expected.file_checksum);
  753. ASSERT_EQ(actual.file_checksum_func_name, expected.file_checksum_func_name);
  754. ASSERT_EQ(actual.marked_for_compaction, expected.marked_for_compaction);
  755. ASSERT_EQ(actual.temperature, expected.temperature);
  756. }
  757. void VerifyProgressEquality(const SubcompactionProgress& expected,
  758. const SubcompactionProgress& actual) {
  759. ASSERT_EQ(actual.next_internal_key_to_compact,
  760. expected.next_internal_key_to_compact);
  761. ASSERT_EQ(actual.num_processed_input_records,
  762. expected.num_processed_input_records);
  763. for (const bool is_proximal_level : {false, true}) {
  764. const SubcompactionProgressPerLevel&
  765. actual_subcompaction_progress_by_level =
  766. is_proximal_level ? actual.proximal_output_level_progress
  767. : actual.output_level_progress;
  768. const SubcompactionProgressPerLevel&
  769. expected_subcompaction_progress_by_level =
  770. is_proximal_level ? expected.proximal_output_level_progress
  771. : expected.output_level_progress;
  772. ASSERT_EQ(
  773. actual_subcompaction_progress_by_level.GetNumProcessedOutputRecords(),
  774. expected_subcompaction_progress_by_level
  775. .GetNumProcessedOutputRecords());
  776. ASSERT_EQ(
  777. actual_subcompaction_progress_by_level.GetOutputFiles().size(),
  778. expected_subcompaction_progress_by_level.GetOutputFiles().size());
  779. for (size_t i = 0;
  780. i < expected_subcompaction_progress_by_level.GetOutputFiles().size();
  781. ++i) {
  782. VerifyFileMetaDataEquality(
  783. expected_subcompaction_progress_by_level.GetOutputFiles()[i],
  784. actual_subcompaction_progress_by_level.GetOutputFiles()[i]);
  785. }
  786. }
  787. }
  788. };
  789. const std::string SubcompactionProgressTest::kTestChecksumFuncName = "crc32c";
  790. TEST_F(SubcompactionProgressTest, BasicEncodeDecode) {
  791. // Create progress with files for both levels
  792. SubcompactionProgress progress = CreateSubcompactionProgress(
  793. "key_100", // next_internal_key_to_compact
  794. 500, // num_processed_input_records
  795. 400, // num_processed_output_records
  796. 100, // num_processed_proximal_level_output_records
  797. {1}, // output_file_numbers
  798. {2}, // proximal_file_numbers
  799. "test_" // file_prefix
  800. );
  801. auto [ignored, decoded_progress] = EncodeDecodeProgress(progress);
  802. VerifyProgressEquality(progress, decoded_progress);
  803. }
  804. TEST_F(SubcompactionProgressTest, OutputFilesDeltaEncodeDecode) {
  805. // Test Delta Encoding/Decoding
  806. SubcompactionProgress initial_progress = CreateSubcompactionProgress(
  807. "key_100", // next_internal_key_to_compact
  808. 100, // num_processed_input_records
  809. 40, // num_processed_output_records
  810. 60, // num_processed_proximal_level_output_records
  811. {1}, // output_file_numbers
  812. {2}, // proximal_file_numbers
  813. "initial_" // file_prefix
  814. );
  815. auto [initial_decoded_edit, ignored_1] =
  816. EncodeDecodeProgress(initial_progress);
  817. initial_progress.output_level_progress.UpdateLastPersistedOutputFilesCount();
  818. initial_progress.proximal_output_level_progress
  819. .UpdateLastPersistedOutputFilesCount();
  820. // Add one new output file to output and proximal level
  821. SubcompactionProgress updated_progress = initial_progress;
  822. updated_progress.next_internal_key_to_compact = "key_300";
  823. updated_progress.num_processed_input_records = 1000;
  824. updated_progress.output_level_progress.SetNumProcessedOutputRecords(400);
  825. updated_progress.proximal_output_level_progress.SetNumProcessedOutputRecords(
  826. 600);
  827. FileMetaData new_file = CreateTestFile(3, "new_");
  828. updated_progress.output_level_progress.AddToOutputFiles(new_file);
  829. FileMetaData new_file_proximal = CreateTestFile(4, "new_");
  830. updated_progress.proximal_output_level_progress.AddToOutputFiles(
  831. new_file_proximal);
  832. auto [delta_decoded_edit, delta_decoded_progress] =
  833. EncodeDecodeProgress(updated_progress);
  834. ASSERT_EQ(delta_decoded_progress.next_internal_key_to_compact,
  835. updated_progress.next_internal_key_to_compact);
  836. ASSERT_EQ(delta_decoded_progress.num_processed_input_records,
  837. updated_progress.num_processed_input_records);
  838. for (const bool& is_proximal_level : {false, true}) {
  839. const SubcompactionProgressPerLevel& delta_progress_per_level =
  840. is_proximal_level
  841. ? delta_decoded_progress.proximal_output_level_progress
  842. : delta_decoded_progress.output_level_progress;
  843. const SubcompactionProgressPerLevel& updated_progress_per_level =
  844. is_proximal_level ? updated_progress.proximal_output_level_progress
  845. : updated_progress.output_level_progress;
  846. ASSERT_EQ(delta_progress_per_level.GetNumProcessedOutputRecords(),
  847. updated_progress_per_level.GetNumProcessedOutputRecords());
  848. // Only the newly added file since last persistence should be present
  849. ASSERT_EQ(delta_progress_per_level.GetOutputFiles().size(), 1);
  850. ASSERT_EQ(delta_progress_per_level.GetOutputFiles()[0].fd.GetNumber(),
  851. is_proximal_level ? new_file_proximal.fd.GetNumber()
  852. : new_file.fd.GetNumber());
  853. }
  854. // Test SubcompactionProgressBuilder
  855. SubcompactionProgressBuilder builder;
  856. ASSERT_FALSE(builder.HasAccumulatedSubcompactionProgress());
  857. ASSERT_TRUE(builder.ProcessVersionEdit(initial_decoded_edit));
  858. ASSERT_TRUE(builder.HasAccumulatedSubcompactionProgress());
  859. ASSERT_TRUE(builder.ProcessVersionEdit(delta_decoded_edit));
  860. const auto& accumulated_progress =
  861. builder.GetAccumulatedSubcompactionProgress();
  862. ASSERT_EQ(accumulated_progress.next_internal_key_to_compact,
  863. updated_progress.next_internal_key_to_compact);
  864. ASSERT_EQ(accumulated_progress.num_processed_input_records,
  865. updated_progress.num_processed_input_records);
  866. for (const bool& is_proximal_level : {false, true}) {
  867. const SubcompactionProgressPerLevel& accumulated_progress_per_level =
  868. is_proximal_level ? accumulated_progress.proximal_output_level_progress
  869. : accumulated_progress.output_level_progress;
  870. const SubcompactionProgressPerLevel& updated_progress_per_level =
  871. is_proximal_level ? updated_progress.proximal_output_level_progress
  872. : updated_progress.output_level_progress;
  873. ASSERT_EQ(accumulated_progress_per_level.GetNumProcessedOutputRecords(),
  874. updated_progress_per_level.GetNumProcessedOutputRecords());
  875. ASSERT_EQ(accumulated_progress_per_level.GetOutputFiles().size(),
  876. updated_progress_per_level.GetOutputFiles().size());
  877. std::set<uint64_t> accumulated_file_numbers;
  878. for (const auto& file : accumulated_progress_per_level.GetOutputFiles()) {
  879. accumulated_file_numbers.insert(file.fd.GetNumber());
  880. }
  881. std::set<uint64_t> expected_file_numbers;
  882. for (const auto& file : updated_progress_per_level.GetOutputFiles()) {
  883. expected_file_numbers.insert(file.fd.GetNumber());
  884. }
  885. ASSERT_EQ(accumulated_file_numbers, expected_file_numbers);
  886. }
  887. // ===== PART 3: Test Builder Reset =====
  888. builder.Clear();
  889. ASSERT_FALSE(builder.HasAccumulatedSubcompactionProgress());
  890. }
  891. TEST_F(SubcompactionProgressTest, UnknownTags) {
  892. SubcompactionProgress progress;
  893. std::string encoded;
  894. // 1. Test unknown ignorable tag
  895. progress.next_internal_key_to_compact = "test_key";
  896. progress.num_processed_input_records = 100;
  897. PutVarint32(&encoded,
  898. SubcompactionProgressCustomTag::kNextInternalKeyToCompact);
  899. PutLengthPrefixedSlice(&encoded, progress.next_internal_key_to_compact);
  900. PutVarint32(&encoded,
  901. SubcompactionProgressCustomTag::kNumProcessedInputRecords);
  902. std::string varint_records;
  903. PutVarint64(&varint_records, progress.num_processed_input_records);
  904. PutLengthPrefixedSlice(&encoded, varint_records);
  905. // Manually encode with unknown ignorable tag (has
  906. // SubcompactionProgressCustomTag::kSubcompactionProgressCustomTagSafeIgnoreMask
  907. // bit set)
  908. uint32_t unknown_ignorable_tag =
  909. SubcompactionProgressCustomTag::
  910. kSubcompactionProgressCustomTagSafeIgnoreMask +
  911. 1;
  912. PutVarint32(&encoded, unknown_ignorable_tag);
  913. PutLengthPrefixedSlice(&encoded, "future_data");
  914. PutVarint32(&encoded,
  915. SubcompactionProgressCustomTag::kSubcompactionProgressTerminate);
  916. // Test decoding - should succeed and ignore unknown tag
  917. Slice input(encoded);
  918. SubcompactionProgress decoded_progress;
  919. Status s = decoded_progress.DecodeFrom(&input);
  920. ASSERT_OK(s);
  921. // Verify known fields are preserved
  922. ASSERT_EQ(decoded_progress.next_internal_key_to_compact,
  923. progress.next_internal_key_to_compact);
  924. ASSERT_EQ(decoded_progress.num_processed_input_records,
  925. progress.num_processed_input_records);
  926. // 2. Test unknown non-ignorable tag
  927. encoded.clear();
  928. PutVarint32(&encoded,
  929. SubcompactionProgressCustomTag::kNextInternalKeyToCompact);
  930. PutLengthPrefixedSlice(&encoded, "test_key");
  931. // Manually encode with unknown non-ignorable tag (do not have
  932. // SubcompactionProgressCustomTag::kSubcompactionProgressCustomTagSafeIgnoreMask
  933. // bit set)
  934. uint32_t unknown_critical_tag =
  935. SubcompactionProgressCustomTag::
  936. kSubcompactionProgressCustomTagSafeIgnoreMask -
  937. 1;
  938. PutVarint32(&encoded, unknown_critical_tag);
  939. PutLengthPrefixedSlice(&encoded, "critical_future_data");
  940. PutVarint32(&encoded,
  941. SubcompactionProgressCustomTag::kSubcompactionProgressTerminate);
  942. // Test decoding - should fail on critical unknown tag
  943. Slice critical_input(encoded);
  944. SubcompactionProgress critical_progress;
  945. Status critical_status = critical_progress.DecodeFrom(&critical_input);
  946. ASSERT_NOK(critical_status);
  947. ASSERT_TRUE(critical_status.IsNotSupported());
  948. }
  949. } // namespace ROCKSDB_NAMESPACE
  950. int main(int argc, char** argv) {
  951. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  952. ::testing::InitGoogleTest(&argc, argv);
  953. return RUN_ALL_TESTS();
  954. }