external_sst_file_basic_test.cc 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128
  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. #include <functional>
  6. #include "db/db_test_util.h"
  7. #include "port/port.h"
  8. #include "port/stack_trace.h"
  9. #include "rocksdb/sst_file_writer.h"
  10. #include "test_util/fault_injection_test_env.h"
  11. #include "test_util/testutil.h"
  12. namespace ROCKSDB_NAMESPACE {
  13. #ifndef ROCKSDB_LITE
  14. class ExternalSSTFileBasicTest
  15. : public DBTestBase,
  16. public ::testing::WithParamInterface<std::tuple<bool, bool>> {
  17. public:
  18. ExternalSSTFileBasicTest() : DBTestBase("/external_sst_file_basic_test") {
  19. sst_files_dir_ = dbname_ + "/sst_files/";
  20. fault_injection_test_env_.reset(new FaultInjectionTestEnv(Env::Default()));
  21. DestroyAndRecreateExternalSSTFilesDir();
  22. }
  23. void DestroyAndRecreateExternalSSTFilesDir() {
  24. test::DestroyDir(env_, sst_files_dir_);
  25. env_->CreateDir(sst_files_dir_);
  26. }
  27. Status DeprecatedAddFile(const std::vector<std::string>& files,
  28. bool move_files = false,
  29. bool skip_snapshot_check = false) {
  30. IngestExternalFileOptions opts;
  31. opts.move_files = move_files;
  32. opts.snapshot_consistency = !skip_snapshot_check;
  33. opts.allow_global_seqno = false;
  34. opts.allow_blocking_flush = false;
  35. return db_->IngestExternalFile(files, opts);
  36. }
  37. Status GenerateAndAddExternalFile(
  38. const Options options, std::vector<int> keys,
  39. const std::vector<ValueType>& value_types,
  40. std::vector<std::pair<int, int>> range_deletions, int file_id,
  41. bool write_global_seqno, bool verify_checksums_before_ingest,
  42. std::map<std::string, std::string>* true_data) {
  43. assert(value_types.size() == 1 || keys.size() == value_types.size());
  44. std::string file_path = sst_files_dir_ + ToString(file_id);
  45. SstFileWriter sst_file_writer(EnvOptions(), options);
  46. Status s = sst_file_writer.Open(file_path);
  47. if (!s.ok()) {
  48. return s;
  49. }
  50. for (size_t i = 0; i < range_deletions.size(); i++) {
  51. // Account for the effect of range deletions on true_data before
  52. // all point operators, even though sst_file_writer.DeleteRange
  53. // must be called before other sst_file_writer methods. This is
  54. // because point writes take precedence over range deletions
  55. // in the same ingested sst.
  56. std::string start_key = Key(range_deletions[i].first);
  57. std::string end_key = Key(range_deletions[i].second);
  58. s = sst_file_writer.DeleteRange(start_key, end_key);
  59. if (!s.ok()) {
  60. sst_file_writer.Finish();
  61. return s;
  62. }
  63. auto start_key_it = true_data->find(start_key);
  64. if (start_key_it == true_data->end()) {
  65. start_key_it = true_data->upper_bound(start_key);
  66. }
  67. auto end_key_it = true_data->find(end_key);
  68. if (end_key_it == true_data->end()) {
  69. end_key_it = true_data->upper_bound(end_key);
  70. }
  71. true_data->erase(start_key_it, end_key_it);
  72. }
  73. for (size_t i = 0; i < keys.size(); i++) {
  74. std::string key = Key(keys[i]);
  75. std::string value = Key(keys[i]) + ToString(file_id);
  76. ValueType value_type =
  77. (value_types.size() == 1 ? value_types[0] : value_types[i]);
  78. switch (value_type) {
  79. case ValueType::kTypeValue:
  80. s = sst_file_writer.Put(key, value);
  81. (*true_data)[key] = value;
  82. break;
  83. case ValueType::kTypeMerge:
  84. s = sst_file_writer.Merge(key, value);
  85. // we only use TestPutOperator in this test
  86. (*true_data)[key] = value;
  87. break;
  88. case ValueType::kTypeDeletion:
  89. s = sst_file_writer.Delete(key);
  90. true_data->erase(key);
  91. break;
  92. default:
  93. return Status::InvalidArgument("Value type is not supported");
  94. }
  95. if (!s.ok()) {
  96. sst_file_writer.Finish();
  97. return s;
  98. }
  99. }
  100. s = sst_file_writer.Finish();
  101. if (s.ok()) {
  102. IngestExternalFileOptions ifo;
  103. ifo.allow_global_seqno = true;
  104. ifo.write_global_seqno = write_global_seqno;
  105. ifo.verify_checksums_before_ingest = verify_checksums_before_ingest;
  106. s = db_->IngestExternalFile({file_path}, ifo);
  107. }
  108. return s;
  109. }
  110. Status GenerateAndAddExternalFile(
  111. const Options options, std::vector<int> keys,
  112. const std::vector<ValueType>& value_types, int file_id,
  113. bool write_global_seqno, bool verify_checksums_before_ingest,
  114. std::map<std::string, std::string>* true_data) {
  115. return GenerateAndAddExternalFile(
  116. options, keys, value_types, {}, file_id, write_global_seqno,
  117. verify_checksums_before_ingest, true_data);
  118. }
  119. Status GenerateAndAddExternalFile(
  120. const Options options, std::vector<int> keys, const ValueType value_type,
  121. int file_id, bool write_global_seqno, bool verify_checksums_before_ingest,
  122. std::map<std::string, std::string>* true_data) {
  123. return GenerateAndAddExternalFile(
  124. options, keys, std::vector<ValueType>(1, value_type), file_id,
  125. write_global_seqno, verify_checksums_before_ingest, true_data);
  126. }
  127. ~ExternalSSTFileBasicTest() override {
  128. test::DestroyDir(env_, sst_files_dir_);
  129. }
  130. protected:
  131. std::string sst_files_dir_;
  132. std::unique_ptr<FaultInjectionTestEnv> fault_injection_test_env_;
  133. };
  134. TEST_F(ExternalSSTFileBasicTest, Basic) {
  135. Options options = CurrentOptions();
  136. SstFileWriter sst_file_writer(EnvOptions(), options);
  137. // Current file size should be 0 after sst_file_writer init and before open a
  138. // file.
  139. ASSERT_EQ(sst_file_writer.FileSize(), 0);
  140. // file1.sst (0 => 99)
  141. std::string file1 = sst_files_dir_ + "file1.sst";
  142. ASSERT_OK(sst_file_writer.Open(file1));
  143. for (int k = 0; k < 100; k++) {
  144. ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
  145. }
  146. ExternalSstFileInfo file1_info;
  147. Status s = sst_file_writer.Finish(&file1_info);
  148. ASSERT_TRUE(s.ok()) << s.ToString();
  149. // Current file size should be non-zero after success write.
  150. ASSERT_GT(sst_file_writer.FileSize(), 0);
  151. ASSERT_EQ(file1_info.file_path, file1);
  152. ASSERT_EQ(file1_info.num_entries, 100);
  153. ASSERT_EQ(file1_info.smallest_key, Key(0));
  154. ASSERT_EQ(file1_info.largest_key, Key(99));
  155. ASSERT_EQ(file1_info.num_range_del_entries, 0);
  156. ASSERT_EQ(file1_info.smallest_range_del_key, "");
  157. ASSERT_EQ(file1_info.largest_range_del_key, "");
  158. // sst_file_writer already finished, cannot add this value
  159. s = sst_file_writer.Put(Key(100), "bad_val");
  160. ASSERT_FALSE(s.ok()) << s.ToString();
  161. s = sst_file_writer.DeleteRange(Key(100), Key(200));
  162. ASSERT_FALSE(s.ok()) << s.ToString();
  163. DestroyAndReopen(options);
  164. // Add file using file path
  165. s = DeprecatedAddFile({file1});
  166. ASSERT_TRUE(s.ok()) << s.ToString();
  167. ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
  168. for (int k = 0; k < 100; k++) {
  169. ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
  170. }
  171. DestroyAndRecreateExternalSSTFilesDir();
  172. }
  173. TEST_F(ExternalSSTFileBasicTest, NoCopy) {
  174. Options options = CurrentOptions();
  175. const ImmutableCFOptions ioptions(options);
  176. SstFileWriter sst_file_writer(EnvOptions(), options);
  177. // file1.sst (0 => 99)
  178. std::string file1 = sst_files_dir_ + "file1.sst";
  179. ASSERT_OK(sst_file_writer.Open(file1));
  180. for (int k = 0; k < 100; k++) {
  181. ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
  182. }
  183. ExternalSstFileInfo file1_info;
  184. Status s = sst_file_writer.Finish(&file1_info);
  185. ASSERT_TRUE(s.ok()) << s.ToString();
  186. ASSERT_EQ(file1_info.file_path, file1);
  187. ASSERT_EQ(file1_info.num_entries, 100);
  188. ASSERT_EQ(file1_info.smallest_key, Key(0));
  189. ASSERT_EQ(file1_info.largest_key, Key(99));
  190. // file2.sst (100 => 299)
  191. std::string file2 = sst_files_dir_ + "file2.sst";
  192. ASSERT_OK(sst_file_writer.Open(file2));
  193. for (int k = 100; k < 300; k++) {
  194. ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
  195. }
  196. ExternalSstFileInfo file2_info;
  197. s = sst_file_writer.Finish(&file2_info);
  198. ASSERT_TRUE(s.ok()) << s.ToString();
  199. ASSERT_EQ(file2_info.file_path, file2);
  200. ASSERT_EQ(file2_info.num_entries, 200);
  201. ASSERT_EQ(file2_info.smallest_key, Key(100));
  202. ASSERT_EQ(file2_info.largest_key, Key(299));
  203. // file3.sst (110 => 124) .. overlap with file2.sst
  204. std::string file3 = sst_files_dir_ + "file3.sst";
  205. ASSERT_OK(sst_file_writer.Open(file3));
  206. for (int k = 110; k < 125; k++) {
  207. ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val_overlap"));
  208. }
  209. ExternalSstFileInfo file3_info;
  210. s = sst_file_writer.Finish(&file3_info);
  211. ASSERT_TRUE(s.ok()) << s.ToString();
  212. ASSERT_EQ(file3_info.file_path, file3);
  213. ASSERT_EQ(file3_info.num_entries, 15);
  214. ASSERT_EQ(file3_info.smallest_key, Key(110));
  215. ASSERT_EQ(file3_info.largest_key, Key(124));
  216. s = DeprecatedAddFile({file1}, true /* move file */);
  217. ASSERT_TRUE(s.ok()) << s.ToString();
  218. ASSERT_EQ(Status::NotFound(), env_->FileExists(file1));
  219. s = DeprecatedAddFile({file2}, false /* copy file */);
  220. ASSERT_TRUE(s.ok()) << s.ToString();
  221. ASSERT_OK(env_->FileExists(file2));
  222. // This file has overlapping values with the existing data
  223. s = DeprecatedAddFile({file3}, true /* move file */);
  224. ASSERT_FALSE(s.ok()) << s.ToString();
  225. ASSERT_OK(env_->FileExists(file3));
  226. for (int k = 0; k < 300; k++) {
  227. ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
  228. }
  229. }
  230. TEST_P(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
  231. bool write_global_seqno = std::get<0>(GetParam());
  232. bool verify_checksums_before_ingest = std::get<1>(GetParam());
  233. do {
  234. Options options = CurrentOptions();
  235. DestroyAndReopen(options);
  236. std::map<std::string, std::string> true_data;
  237. int file_id = 1;
  238. ASSERT_OK(GenerateAndAddExternalFile(
  239. options, {1, 2, 3, 4, 5, 6}, ValueType::kTypeValue, file_id++,
  240. write_global_seqno, verify_checksums_before_ingest, &true_data));
  241. // File doesn't overwrite any keys, no seqno needed
  242. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
  243. ASSERT_OK(GenerateAndAddExternalFile(
  244. options, {10, 11, 12, 13}, ValueType::kTypeValue, file_id++,
  245. write_global_seqno, verify_checksums_before_ingest, &true_data));
  246. // File doesn't overwrite any keys, no seqno needed
  247. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
  248. ASSERT_OK(GenerateAndAddExternalFile(
  249. options, {1, 4, 6}, ValueType::kTypeValue, file_id++,
  250. write_global_seqno, verify_checksums_before_ingest, &true_data));
  251. // File overwrites some keys, a seqno will be assigned
  252. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
  253. ASSERT_OK(GenerateAndAddExternalFile(
  254. options, {11, 15, 19}, ValueType::kTypeValue, file_id++,
  255. write_global_seqno, verify_checksums_before_ingest, &true_data));
  256. // File overwrites some keys, a seqno will be assigned
  257. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
  258. ASSERT_OK(GenerateAndAddExternalFile(
  259. options, {120, 130}, ValueType::kTypeValue, file_id++,
  260. write_global_seqno, verify_checksums_before_ingest, &true_data));
  261. // File doesn't overwrite any keys, no seqno needed
  262. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
  263. ASSERT_OK(GenerateAndAddExternalFile(
  264. options, {1, 130}, ValueType::kTypeValue, file_id++, write_global_seqno,
  265. verify_checksums_before_ingest, &true_data));
  266. // File overwrites some keys, a seqno will be assigned
  267. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
  268. // Write some keys through normal write path
  269. for (int i = 0; i < 50; i++) {
  270. ASSERT_OK(Put(Key(i), "memtable"));
  271. true_data[Key(i)] = "memtable";
  272. }
  273. SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
  274. ASSERT_OK(GenerateAndAddExternalFile(
  275. options, {60, 61, 62}, ValueType::kTypeValue, file_id++,
  276. write_global_seqno, verify_checksums_before_ingest, &true_data));
  277. // File doesn't overwrite any keys, no seqno needed
  278. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
  279. ASSERT_OK(GenerateAndAddExternalFile(
  280. options, {40, 41, 42}, ValueType::kTypeValue, file_id++,
  281. write_global_seqno, verify_checksums_before_ingest, &true_data));
  282. // File overwrites some keys, a seqno will be assigned
  283. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
  284. ASSERT_OK(GenerateAndAddExternalFile(
  285. options, {20, 30, 40}, ValueType::kTypeValue, file_id++,
  286. write_global_seqno, verify_checksums_before_ingest, &true_data));
  287. // File overwrites some keys, a seqno will be assigned
  288. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
  289. const Snapshot* snapshot = db_->GetSnapshot();
  290. // We will need a seqno for the file regardless if the file overwrite
  291. // keys in the DB or not because we have a snapshot
  292. ASSERT_OK(GenerateAndAddExternalFile(
  293. options, {1000, 1002}, ValueType::kTypeValue, file_id++,
  294. write_global_seqno, verify_checksums_before_ingest, &true_data));
  295. // A global seqno will be assigned anyway because of the snapshot
  296. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
  297. ASSERT_OK(GenerateAndAddExternalFile(
  298. options, {2000, 3002}, ValueType::kTypeValue, file_id++,
  299. write_global_seqno, verify_checksums_before_ingest, &true_data));
  300. // A global seqno will be assigned anyway because of the snapshot
  301. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
  302. ASSERT_OK(GenerateAndAddExternalFile(
  303. options, {1, 20, 40, 100, 150}, ValueType::kTypeValue, file_id++,
  304. write_global_seqno, verify_checksums_before_ingest, &true_data));
  305. // A global seqno will be assigned anyway because of the snapshot
  306. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
  307. db_->ReleaseSnapshot(snapshot);
  308. ASSERT_OK(GenerateAndAddExternalFile(
  309. options, {5000, 5001}, ValueType::kTypeValue, file_id++,
  310. write_global_seqno, verify_checksums_before_ingest, &true_data));
  311. // No snapshot anymore, no need to assign a seqno
  312. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
  313. size_t kcnt = 0;
  314. VerifyDBFromMap(true_data, &kcnt, false);
  315. } while (ChangeOptionsForFileIngestionTest());
  316. }
  317. TEST_P(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
  318. bool write_global_seqno = std::get<0>(GetParam());
  319. bool verify_checksums_before_ingest = std::get<1>(GetParam());
  320. do {
  321. Options options = CurrentOptions();
  322. options.merge_operator.reset(new TestPutOperator());
  323. DestroyAndReopen(options);
  324. std::map<std::string, std::string> true_data;
  325. int file_id = 1;
  326. ASSERT_OK(GenerateAndAddExternalFile(
  327. options, {1, 2, 3, 4, 5, 6}, ValueType::kTypeValue, file_id++,
  328. write_global_seqno, verify_checksums_before_ingest, &true_data));
  329. // File doesn't overwrite any keys, no seqno needed
  330. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
  331. ASSERT_OK(GenerateAndAddExternalFile(
  332. options, {10, 11, 12, 13}, ValueType::kTypeValue, file_id++,
  333. write_global_seqno, verify_checksums_before_ingest, &true_data));
  334. // File doesn't overwrite any keys, no seqno needed
  335. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
  336. ASSERT_OK(GenerateAndAddExternalFile(
  337. options, {1, 4, 6}, ValueType::kTypeMerge, file_id++,
  338. write_global_seqno, verify_checksums_before_ingest, &true_data));
  339. // File overwrites some keys, a seqno will be assigned
  340. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
  341. ASSERT_OK(GenerateAndAddExternalFile(
  342. options, {11, 15, 19}, ValueType::kTypeDeletion, file_id++,
  343. write_global_seqno, verify_checksums_before_ingest, &true_data));
  344. // File overwrites some keys, a seqno will be assigned
  345. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
  346. ASSERT_OK(GenerateAndAddExternalFile(
  347. options, {120, 130}, ValueType::kTypeMerge, file_id++,
  348. write_global_seqno, verify_checksums_before_ingest, &true_data));
  349. // File doesn't overwrite any keys, no seqno needed
  350. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
  351. ASSERT_OK(GenerateAndAddExternalFile(
  352. options, {1, 130}, ValueType::kTypeDeletion, file_id++,
  353. write_global_seqno, verify_checksums_before_ingest, &true_data));
  354. // File overwrites some keys, a seqno will be assigned
  355. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
  356. ASSERT_OK(GenerateAndAddExternalFile(
  357. options, {120}, {ValueType::kTypeValue}, {{120, 135}}, file_id++,
  358. write_global_seqno, verify_checksums_before_ingest, &true_data));
  359. // File overwrites some keys, a seqno will be assigned
  360. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 4);
  361. ASSERT_OK(GenerateAndAddExternalFile(
  362. options, {}, {}, {{110, 120}}, file_id++, write_global_seqno,
  363. verify_checksums_before_ingest, &true_data));
  364. // The range deletion ends on a key, but it doesn't actually delete
  365. // this key because the largest key in the range is exclusive. Still,
  366. // it counts as an overlap so a new seqno will be assigned.
  367. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
  368. ASSERT_OK(GenerateAndAddExternalFile(
  369. options, {}, {}, {{100, 109}}, file_id++, write_global_seqno,
  370. verify_checksums_before_ingest, &true_data));
  371. // File doesn't overwrite any keys, no seqno needed
  372. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
  373. // Write some keys through normal write path
  374. for (int i = 0; i < 50; i++) {
  375. ASSERT_OK(Put(Key(i), "memtable"));
  376. true_data[Key(i)] = "memtable";
  377. }
  378. SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
  379. ASSERT_OK(GenerateAndAddExternalFile(
  380. options, {60, 61, 62}, ValueType::kTypeValue, file_id++,
  381. write_global_seqno, verify_checksums_before_ingest, &true_data));
  382. // File doesn't overwrite any keys, no seqno needed
  383. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
  384. ASSERT_OK(GenerateAndAddExternalFile(
  385. options, {40, 41, 42}, ValueType::kTypeMerge, file_id++,
  386. write_global_seqno, verify_checksums_before_ingest, &true_data));
  387. // File overwrites some keys, a seqno will be assigned
  388. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
  389. ASSERT_OK(GenerateAndAddExternalFile(
  390. options, {20, 30, 40}, ValueType::kTypeDeletion, file_id++,
  391. write_global_seqno, verify_checksums_before_ingest, &true_data));
  392. // File overwrites some keys, a seqno will be assigned
  393. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
  394. const Snapshot* snapshot = db_->GetSnapshot();
  395. // We will need a seqno for the file regardless if the file overwrite
  396. // keys in the DB or not because we have a snapshot
  397. ASSERT_OK(GenerateAndAddExternalFile(
  398. options, {1000, 1002}, ValueType::kTypeMerge, file_id++,
  399. write_global_seqno, verify_checksums_before_ingest, &true_data));
  400. // A global seqno will be assigned anyway because of the snapshot
  401. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
  402. ASSERT_OK(GenerateAndAddExternalFile(
  403. options, {2000, 3002}, ValueType::kTypeMerge, file_id++,
  404. write_global_seqno, verify_checksums_before_ingest, &true_data));
  405. // A global seqno will be assigned anyway because of the snapshot
  406. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
  407. ASSERT_OK(GenerateAndAddExternalFile(
  408. options, {1, 20, 40, 100, 150}, ValueType::kTypeMerge, file_id++,
  409. write_global_seqno, verify_checksums_before_ingest, &true_data));
  410. // A global seqno will be assigned anyway because of the snapshot
  411. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
  412. db_->ReleaseSnapshot(snapshot);
  413. ASSERT_OK(GenerateAndAddExternalFile(
  414. options, {5000, 5001}, ValueType::kTypeValue, file_id++,
  415. write_global_seqno, verify_checksums_before_ingest, &true_data));
  416. // No snapshot anymore, no need to assign a seqno
  417. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
  418. size_t kcnt = 0;
  419. VerifyDBFromMap(true_data, &kcnt, false);
  420. } while (ChangeOptionsForFileIngestionTest());
  421. }
  422. TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
  423. bool write_global_seqno = std::get<0>(GetParam());
  424. bool verify_checksums_before_ingest = std::get<1>(GetParam());
  425. do {
  426. Options options = CurrentOptions();
  427. options.merge_operator.reset(new TestPutOperator());
  428. DestroyAndReopen(options);
  429. std::map<std::string, std::string> true_data;
  430. int file_id = 1;
  431. ASSERT_OK(GenerateAndAddExternalFile(
  432. options, {1, 2, 3, 4, 5, 6},
  433. {ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue,
  434. ValueType::kTypeMerge, ValueType::kTypeValue, ValueType::kTypeMerge},
  435. file_id++, write_global_seqno, verify_checksums_before_ingest,
  436. &true_data));
  437. // File doesn't overwrite any keys, no seqno needed
  438. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
  439. ASSERT_OK(GenerateAndAddExternalFile(
  440. options, {10, 11, 12, 13},
  441. {ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue,
  442. ValueType::kTypeMerge},
  443. file_id++, write_global_seqno, verify_checksums_before_ingest,
  444. &true_data));
  445. // File doesn't overwrite any keys, no seqno needed
  446. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
  447. ASSERT_OK(GenerateAndAddExternalFile(
  448. options, {1, 4, 6},
  449. {ValueType::kTypeDeletion, ValueType::kTypeValue,
  450. ValueType::kTypeMerge},
  451. file_id++, write_global_seqno, verify_checksums_before_ingest,
  452. &true_data));
  453. // File overwrites some keys, a seqno will be assigned
  454. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
  455. ASSERT_OK(GenerateAndAddExternalFile(
  456. options, {11, 15, 19},
  457. {ValueType::kTypeDeletion, ValueType::kTypeMerge,
  458. ValueType::kTypeValue},
  459. file_id++, write_global_seqno, verify_checksums_before_ingest,
  460. &true_data));
  461. // File overwrites some keys, a seqno will be assigned
  462. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
  463. ASSERT_OK(GenerateAndAddExternalFile(
  464. options, {120, 130}, {ValueType::kTypeValue, ValueType::kTypeMerge},
  465. file_id++, write_global_seqno, verify_checksums_before_ingest,
  466. &true_data));
  467. // File doesn't overwrite any keys, no seqno needed
  468. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
  469. ASSERT_OK(GenerateAndAddExternalFile(
  470. options, {1, 130}, {ValueType::kTypeMerge, ValueType::kTypeDeletion},
  471. file_id++, write_global_seqno, verify_checksums_before_ingest,
  472. &true_data));
  473. // File overwrites some keys, a seqno will be assigned
  474. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
  475. ASSERT_OK(GenerateAndAddExternalFile(
  476. options, {150, 151, 152},
  477. {ValueType::kTypeValue, ValueType::kTypeMerge,
  478. ValueType::kTypeDeletion},
  479. {{150, 160}, {180, 190}}, file_id++, write_global_seqno,
  480. verify_checksums_before_ingest, &true_data));
  481. // File doesn't overwrite any keys, no seqno needed
  482. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
  483. ASSERT_OK(GenerateAndAddExternalFile(
  484. options, {150, 151, 152},
  485. {ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue},
  486. {{200, 250}}, file_id++, write_global_seqno,
  487. verify_checksums_before_ingest, &true_data));
  488. // File overwrites some keys, a seqno will be assigned
  489. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 4);
  490. ASSERT_OK(GenerateAndAddExternalFile(
  491. options, {300, 301, 302},
  492. {ValueType::kTypeValue, ValueType::kTypeMerge,
  493. ValueType::kTypeDeletion},
  494. {{1, 2}, {152, 154}}, file_id++, write_global_seqno,
  495. verify_checksums_before_ingest, &true_data));
  496. // File overwrites some keys, a seqno will be assigned
  497. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
  498. // Write some keys through normal write path
  499. for (int i = 0; i < 50; i++) {
  500. ASSERT_OK(Put(Key(i), "memtable"));
  501. true_data[Key(i)] = "memtable";
  502. }
  503. SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
  504. ASSERT_OK(GenerateAndAddExternalFile(
  505. options, {60, 61, 62},
  506. {ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue},
  507. file_id++, write_global_seqno, verify_checksums_before_ingest,
  508. &true_data));
  509. // File doesn't overwrite any keys, no seqno needed
  510. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
  511. ASSERT_OK(GenerateAndAddExternalFile(
  512. options, {40, 41, 42},
  513. {ValueType::kTypeValue, ValueType::kTypeDeletion,
  514. ValueType::kTypeDeletion},
  515. file_id++, write_global_seqno, verify_checksums_before_ingest,
  516. &true_data));
  517. // File overwrites some keys, a seqno will be assigned
  518. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
  519. ASSERT_OK(GenerateAndAddExternalFile(
  520. options, {20, 30, 40},
  521. {ValueType::kTypeDeletion, ValueType::kTypeDeletion,
  522. ValueType::kTypeDeletion},
  523. file_id++, write_global_seqno, verify_checksums_before_ingest,
  524. &true_data));
  525. // File overwrites some keys, a seqno will be assigned
  526. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
  527. const Snapshot* snapshot = db_->GetSnapshot();
  528. // We will need a seqno for the file regardless if the file overwrite
  529. // keys in the DB or not because we have a snapshot
  530. ASSERT_OK(GenerateAndAddExternalFile(
  531. options, {1000, 1002}, {ValueType::kTypeValue, ValueType::kTypeMerge},
  532. file_id++, write_global_seqno, verify_checksums_before_ingest,
  533. &true_data));
  534. // A global seqno will be assigned anyway because of the snapshot
  535. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
  536. ASSERT_OK(GenerateAndAddExternalFile(
  537. options, {2000, 3002}, {ValueType::kTypeValue, ValueType::kTypeMerge},
  538. file_id++, write_global_seqno, verify_checksums_before_ingest,
  539. &true_data));
  540. // A global seqno will be assigned anyway because of the snapshot
  541. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
  542. ASSERT_OK(GenerateAndAddExternalFile(
  543. options, {1, 20, 40, 100, 150},
  544. {ValueType::kTypeDeletion, ValueType::kTypeDeletion,
  545. ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeMerge},
  546. file_id++, write_global_seqno, verify_checksums_before_ingest,
  547. &true_data));
  548. // A global seqno will be assigned anyway because of the snapshot
  549. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
  550. db_->ReleaseSnapshot(snapshot);
  551. ASSERT_OK(GenerateAndAddExternalFile(
  552. options, {5000, 5001}, {ValueType::kTypeValue, ValueType::kTypeMerge},
  553. file_id++, write_global_seqno, verify_checksums_before_ingest,
  554. &true_data));
  555. // No snapshot anymore, no need to assign a seqno
  556. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
  557. size_t kcnt = 0;
  558. VerifyDBFromMap(true_data, &kcnt, false);
  559. } while (ChangeOptionsForFileIngestionTest());
  560. }
  561. TEST_F(ExternalSSTFileBasicTest, FadviseTrigger) {
  562. Options options = CurrentOptions();
  563. const int kNumKeys = 10000;
  564. size_t total_fadvised_bytes = 0;
  565. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  566. "SstFileWriter::Rep::InvalidatePageCache", [&](void* arg) {
  567. size_t fadvise_size = *(reinterpret_cast<size_t*>(arg));
  568. total_fadvised_bytes += fadvise_size;
  569. });
  570. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  571. std::unique_ptr<SstFileWriter> sst_file_writer;
  572. std::string sst_file_path = sst_files_dir_ + "file_fadvise_disable.sst";
  573. sst_file_writer.reset(
  574. new SstFileWriter(EnvOptions(), options, nullptr, false));
  575. ASSERT_OK(sst_file_writer->Open(sst_file_path));
  576. for (int i = 0; i < kNumKeys; i++) {
  577. ASSERT_OK(sst_file_writer->Put(Key(i), Key(i)));
  578. }
  579. ASSERT_OK(sst_file_writer->Finish());
  580. // fadvise disabled
  581. ASSERT_EQ(total_fadvised_bytes, 0);
  582. sst_file_path = sst_files_dir_ + "file_fadvise_enable.sst";
  583. sst_file_writer.reset(
  584. new SstFileWriter(EnvOptions(), options, nullptr, true));
  585. ASSERT_OK(sst_file_writer->Open(sst_file_path));
  586. for (int i = 0; i < kNumKeys; i++) {
  587. ASSERT_OK(sst_file_writer->Put(Key(i), Key(i)));
  588. }
  589. ASSERT_OK(sst_file_writer->Finish());
  590. // fadvise enabled
  591. ASSERT_EQ(total_fadvised_bytes, sst_file_writer->FileSize());
  592. ASSERT_GT(total_fadvised_bytes, 0);
  593. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  594. }
  595. TEST_F(ExternalSSTFileBasicTest, SyncFailure) {
  596. Options options;
  597. options.create_if_missing = true;
  598. options.env = fault_injection_test_env_.get();
  599. std::vector<std::pair<std::string, std::string>> test_cases = {
  600. {"ExternalSstFileIngestionJob::BeforeSyncIngestedFile",
  601. "ExternalSstFileIngestionJob::AfterSyncIngestedFile"},
  602. {"ExternalSstFileIngestionJob::BeforeSyncDir",
  603. "ExternalSstFileIngestionJob::AfterSyncDir"},
  604. {"ExternalSstFileIngestionJob::BeforeSyncGlobalSeqno",
  605. "ExternalSstFileIngestionJob::AfterSyncGlobalSeqno"}};
  606. for (size_t i = 0; i < test_cases.size(); i++) {
  607. SyncPoint::GetInstance()->SetCallBack(test_cases[i].first, [&](void*) {
  608. fault_injection_test_env_->SetFilesystemActive(false);
  609. });
  610. SyncPoint::GetInstance()->SetCallBack(test_cases[i].second, [&](void*) {
  611. fault_injection_test_env_->SetFilesystemActive(true);
  612. });
  613. SyncPoint::GetInstance()->EnableProcessing();
  614. DestroyAndReopen(options);
  615. if (i == 2) {
  616. ASSERT_OK(Put("foo", "v1"));
  617. }
  618. Options sst_file_writer_options;
  619. std::unique_ptr<SstFileWriter> sst_file_writer(
  620. new SstFileWriter(EnvOptions(), sst_file_writer_options));
  621. std::string file_name =
  622. sst_files_dir_ + "sync_failure_test_" + ToString(i) + ".sst";
  623. ASSERT_OK(sst_file_writer->Open(file_name));
  624. ASSERT_OK(sst_file_writer->Put("bar", "v2"));
  625. ASSERT_OK(sst_file_writer->Finish());
  626. IngestExternalFileOptions ingest_opt;
  627. if (i == 0) {
  628. ingest_opt.move_files = true;
  629. }
  630. const Snapshot* snapshot = db_->GetSnapshot();
  631. if (i == 2) {
  632. ingest_opt.write_global_seqno = true;
  633. }
  634. ASSERT_FALSE(db_->IngestExternalFile({file_name}, ingest_opt).ok());
  635. db_->ReleaseSnapshot(snapshot);
  636. SyncPoint::GetInstance()->DisableProcessing();
  637. SyncPoint::GetInstance()->ClearAllCallBacks();
  638. Destroy(options);
  639. }
  640. }
  641. TEST_F(ExternalSSTFileBasicTest, VerifyChecksumReadahead) {
  642. Options options;
  643. options.create_if_missing = true;
  644. SpecialEnv senv(Env::Default());
  645. options.env = &senv;
  646. DestroyAndReopen(options);
  647. Options sst_file_writer_options;
  648. std::unique_ptr<SstFileWriter> sst_file_writer(
  649. new SstFileWriter(EnvOptions(), sst_file_writer_options));
  650. std::string file_name = sst_files_dir_ + "verify_checksum_readahead_test.sst";
  651. ASSERT_OK(sst_file_writer->Open(file_name));
  652. Random rnd(301);
  653. std::string value = DBTestBase::RandomString(&rnd, 4000);
  654. for (int i = 0; i < 5000; i++) {
  655. ASSERT_OK(sst_file_writer->Put(DBTestBase::Key(i), value));
  656. }
  657. ASSERT_OK(sst_file_writer->Finish());
  658. // Ingest it once without verifying checksums to see the baseline
  659. // preads.
  660. IngestExternalFileOptions ingest_opt;
  661. ingest_opt.move_files = false;
  662. senv.count_random_reads_ = true;
  663. senv.random_read_bytes_counter_ = 0;
  664. ASSERT_OK(db_->IngestExternalFile({file_name}, ingest_opt));
  665. auto base_num_reads = senv.random_read_counter_.Read();
  666. // Make sure the counter is enabled.
  667. ASSERT_GT(base_num_reads, 0);
  668. // Ingest again and observe the reads made for for readahead.
  669. ingest_opt.move_files = false;
  670. ingest_opt.verify_checksums_before_ingest = true;
  671. ingest_opt.verify_checksums_readahead_size = size_t{2 * 1024 * 1024};
  672. senv.count_random_reads_ = true;
  673. senv.random_read_bytes_counter_ = 0;
  674. ASSERT_OK(db_->IngestExternalFile({file_name}, ingest_opt));
  675. // Make sure the counter is enabled.
  676. ASSERT_GT(senv.random_read_counter_.Read() - base_num_reads, 0);
  677. // The SST file is about 20MB. Readahead size is 2MB.
  678. // Give a conservative 15 reads for metadata blocks, the number
  679. // of random reads should be within 20 MB / 2MB + 15 = 25.
  680. ASSERT_LE(senv.random_read_counter_.Read() - base_num_reads, 40);
  681. Destroy(options);
  682. }
  683. TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
  684. int kNumLevels = 7;
  685. Options options = CurrentOptions();
  686. options.disable_auto_compactions = true;
  687. options.num_levels = kNumLevels;
  688. Reopen(options);
  689. std::map<std::string, std::string> true_data;
  690. int file_id = 1;
  691. // prevent range deletions from being dropped due to becoming obsolete.
  692. const Snapshot* snapshot = db_->GetSnapshot();
  693. // range del [0, 50) in L6 file, [50, 100) in L0 file, [100, 150) in memtable
  694. for (int i = 0; i < 3; i++) {
  695. if (i != 0) {
  696. db_->Flush(FlushOptions());
  697. if (i == 1) {
  698. MoveFilesToLevel(kNumLevels - 1);
  699. }
  700. }
  701. ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
  702. Key(50 * i), Key(50 * (i + 1))));
  703. }
  704. ASSERT_EQ(1, NumTableFilesAtLevel(0));
  705. ASSERT_EQ(0, NumTableFilesAtLevel(kNumLevels - 2));
  706. ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 1));
  707. bool write_global_seqno = std::get<0>(GetParam());
  708. bool verify_checksums_before_ingest = std::get<1>(GetParam());
  709. // overlaps with L0 file but not memtable, so flush is skipped and file is
  710. // ingested into L0
  711. SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
  712. ASSERT_OK(GenerateAndAddExternalFile(
  713. options, {60, 90}, {ValueType::kTypeValue, ValueType::kTypeValue},
  714. {{65, 70}, {70, 85}}, file_id++, write_global_seqno,
  715. verify_checksums_before_ingest, &true_data));
  716. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
  717. ASSERT_EQ(2, NumTableFilesAtLevel(0));
  718. ASSERT_EQ(0, NumTableFilesAtLevel(kNumLevels - 2));
  719. ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
  720. // overlaps with L6 file but not memtable or L0 file, so flush is skipped and
  721. // file is ingested into L5
  722. ASSERT_OK(GenerateAndAddExternalFile(
  723. options, {10, 40}, {ValueType::kTypeValue, ValueType::kTypeValue},
  724. file_id++, write_global_seqno, verify_checksums_before_ingest,
  725. &true_data));
  726. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
  727. ASSERT_EQ(2, NumTableFilesAtLevel(0));
  728. ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
  729. ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
  730. // overlaps with L5 file but not memtable or L0 file, so flush is skipped and
  731. // file is ingested into L4
  732. ASSERT_OK(GenerateAndAddExternalFile(
  733. options, {}, {}, {{5, 15}}, file_id++, write_global_seqno,
  734. verify_checksums_before_ingest, &true_data));
  735. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
  736. ASSERT_EQ(2, NumTableFilesAtLevel(0));
  737. ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
  738. ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 2));
  739. ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
  740. // ingested file overlaps with memtable, so flush is triggered before the file
  741. // is ingested such that the ingested data is considered newest. So L0 file
  742. // count increases by two.
  743. ASSERT_OK(GenerateAndAddExternalFile(
  744. options, {100, 140}, {ValueType::kTypeValue, ValueType::kTypeValue},
  745. file_id++, write_global_seqno, verify_checksums_before_ingest,
  746. &true_data));
  747. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
  748. ASSERT_EQ(4, NumTableFilesAtLevel(0));
  749. ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
  750. ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
  751. // snapshot unneeded now that all range deletions are persisted
  752. db_->ReleaseSnapshot(snapshot);
  753. // overlaps with nothing, so places at bottom level and skips incrementing
  754. // seqnum.
  755. ASSERT_OK(GenerateAndAddExternalFile(
  756. options, {151, 175}, {ValueType::kTypeValue, ValueType::kTypeValue},
  757. {{160, 200}}, file_id++, write_global_seqno,
  758. verify_checksums_before_ingest, &true_data));
  759. ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
  760. ASSERT_EQ(4, NumTableFilesAtLevel(0));
  761. ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
  762. ASSERT_EQ(2, NumTableFilesAtLevel(options.num_levels - 1));
  763. }
  764. TEST_F(ExternalSSTFileBasicTest, AdjacentRangeDeletionTombstones) {
  765. Options options = CurrentOptions();
  766. SstFileWriter sst_file_writer(EnvOptions(), options);
  767. // file8.sst (delete 300 => 400)
  768. std::string file8 = sst_files_dir_ + "file8.sst";
  769. ASSERT_OK(sst_file_writer.Open(file8));
  770. ASSERT_OK(sst_file_writer.DeleteRange(Key(300), Key(400)));
  771. ExternalSstFileInfo file8_info;
  772. Status s = sst_file_writer.Finish(&file8_info);
  773. ASSERT_TRUE(s.ok()) << s.ToString();
  774. ASSERT_EQ(file8_info.file_path, file8);
  775. ASSERT_EQ(file8_info.num_entries, 0);
  776. ASSERT_EQ(file8_info.smallest_key, "");
  777. ASSERT_EQ(file8_info.largest_key, "");
  778. ASSERT_EQ(file8_info.num_range_del_entries, 1);
  779. ASSERT_EQ(file8_info.smallest_range_del_key, Key(300));
  780. ASSERT_EQ(file8_info.largest_range_del_key, Key(400));
  781. // file9.sst (delete 400 => 500)
  782. std::string file9 = sst_files_dir_ + "file9.sst";
  783. ASSERT_OK(sst_file_writer.Open(file9));
  784. ASSERT_OK(sst_file_writer.DeleteRange(Key(400), Key(500)));
  785. ExternalSstFileInfo file9_info;
  786. s = sst_file_writer.Finish(&file9_info);
  787. ASSERT_TRUE(s.ok()) << s.ToString();
  788. ASSERT_EQ(file9_info.file_path, file9);
  789. ASSERT_EQ(file9_info.num_entries, 0);
  790. ASSERT_EQ(file9_info.smallest_key, "");
  791. ASSERT_EQ(file9_info.largest_key, "");
  792. ASSERT_EQ(file9_info.num_range_del_entries, 1);
  793. ASSERT_EQ(file9_info.smallest_range_del_key, Key(400));
  794. ASSERT_EQ(file9_info.largest_range_del_key, Key(500));
  795. // Range deletion tombstones are exclusive on their end key, so these SSTs
  796. // should not be considered as overlapping.
  797. s = DeprecatedAddFile({file8, file9});
  798. ASSERT_TRUE(s.ok()) << s.ToString();
  799. ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
  800. DestroyAndRecreateExternalSSTFilesDir();
  801. }
  802. TEST_P(ExternalSSTFileBasicTest, IngestFileWithBadBlockChecksum) {
  803. bool change_checksum_called = false;
  804. const auto& change_checksum = [&](void* arg) {
  805. if (!change_checksum_called) {
  806. char* buf = reinterpret_cast<char*>(arg);
  807. assert(nullptr != buf);
  808. buf[0] ^= 0x1;
  809. change_checksum_called = true;
  810. }
  811. };
  812. SyncPoint::GetInstance()->DisableProcessing();
  813. SyncPoint::GetInstance()->ClearAllCallBacks();
  814. SyncPoint::GetInstance()->SetCallBack(
  815. "BlockBasedTableBuilder::WriteRawBlock:TamperWithChecksum",
  816. change_checksum);
  817. SyncPoint::GetInstance()->EnableProcessing();
  818. int file_id = 0;
  819. bool write_global_seqno = std::get<0>(GetParam());
  820. bool verify_checksums_before_ingest = std::get<1>(GetParam());
  821. do {
  822. Options options = CurrentOptions();
  823. DestroyAndReopen(options);
  824. std::map<std::string, std::string> true_data;
  825. Status s = GenerateAndAddExternalFile(
  826. options, {1, 2, 3, 4, 5, 6}, ValueType::kTypeValue, file_id++,
  827. write_global_seqno, verify_checksums_before_ingest, &true_data);
  828. if (verify_checksums_before_ingest) {
  829. ASSERT_NOK(s);
  830. } else {
  831. ASSERT_OK(s);
  832. }
  833. change_checksum_called = false;
  834. } while (ChangeOptionsForFileIngestionTest());
  835. }
  836. TEST_P(ExternalSSTFileBasicTest, IngestFileWithFirstByteTampered) {
  837. SyncPoint::GetInstance()->DisableProcessing();
  838. int file_id = 0;
  839. EnvOptions env_options;
  840. do {
  841. Options options = CurrentOptions();
  842. std::string file_path = sst_files_dir_ + ToString(file_id++);
  843. SstFileWriter sst_file_writer(env_options, options);
  844. Status s = sst_file_writer.Open(file_path);
  845. ASSERT_OK(s);
  846. for (int i = 0; i != 100; ++i) {
  847. std::string key = Key(i);
  848. std::string value = Key(i) + ToString(0);
  849. ASSERT_OK(sst_file_writer.Put(key, value));
  850. }
  851. ASSERT_OK(sst_file_writer.Finish());
  852. {
  853. // Get file size
  854. uint64_t file_size = 0;
  855. ASSERT_OK(env_->GetFileSize(file_path, &file_size));
  856. ASSERT_GT(file_size, 8);
  857. std::unique_ptr<RandomRWFile> rwfile;
  858. ASSERT_OK(env_->NewRandomRWFile(file_path, &rwfile, EnvOptions()));
  859. // Manually corrupt the file
  860. // We deterministically corrupt the first byte because we currently
  861. // cannot choose a random offset. The reason for this limitation is that
  862. // we do not checksum property block at present.
  863. const uint64_t offset = 0;
  864. char scratch[8] = {0};
  865. Slice buf;
  866. ASSERT_OK(rwfile->Read(offset, sizeof(scratch), &buf, scratch));
  867. scratch[0] ^= 0xff; // flip one bit
  868. ASSERT_OK(rwfile->Write(offset, buf));
  869. }
  870. // Ingest file.
  871. IngestExternalFileOptions ifo;
  872. ifo.write_global_seqno = std::get<0>(GetParam());
  873. ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
  874. s = db_->IngestExternalFile({file_path}, ifo);
  875. if (ifo.verify_checksums_before_ingest) {
  876. ASSERT_NOK(s);
  877. } else {
  878. ASSERT_OK(s);
  879. }
  880. } while (ChangeOptionsForFileIngestionTest());
  881. }
  882. TEST_P(ExternalSSTFileBasicTest, IngestExternalFileWithCorruptedPropsBlock) {
  883. bool verify_checksums_before_ingest = std::get<1>(GetParam());
  884. if (!verify_checksums_before_ingest) {
  885. return;
  886. }
  887. uint64_t props_block_offset = 0;
  888. size_t props_block_size = 0;
  889. const auto& get_props_block_offset = [&](void* arg) {
  890. props_block_offset = *reinterpret_cast<uint64_t*>(arg);
  891. };
  892. const auto& get_props_block_size = [&](void* arg) {
  893. props_block_size = *reinterpret_cast<uint64_t*>(arg);
  894. };
  895. SyncPoint::GetInstance()->DisableProcessing();
  896. SyncPoint::GetInstance()->ClearAllCallBacks();
  897. SyncPoint::GetInstance()->SetCallBack(
  898. "BlockBasedTableBuilder::WritePropertiesBlock:GetPropsBlockOffset",
  899. get_props_block_offset);
  900. SyncPoint::GetInstance()->SetCallBack(
  901. "BlockBasedTableBuilder::WritePropertiesBlock:GetPropsBlockSize",
  902. get_props_block_size);
  903. SyncPoint::GetInstance()->EnableProcessing();
  904. int file_id = 0;
  905. Random64 rand(time(nullptr));
  906. do {
  907. std::string file_path = sst_files_dir_ + ToString(file_id++);
  908. Options options = CurrentOptions();
  909. SstFileWriter sst_file_writer(EnvOptions(), options);
  910. Status s = sst_file_writer.Open(file_path);
  911. ASSERT_OK(s);
  912. for (int i = 0; i != 100; ++i) {
  913. std::string key = Key(i);
  914. std::string value = Key(i) + ToString(0);
  915. ASSERT_OK(sst_file_writer.Put(key, value));
  916. }
  917. ASSERT_OK(sst_file_writer.Finish());
  918. {
  919. std::unique_ptr<RandomRWFile> rwfile;
  920. ASSERT_OK(env_->NewRandomRWFile(file_path, &rwfile, EnvOptions()));
  921. // Manually corrupt the file
  922. ASSERT_GT(props_block_size, 8);
  923. uint64_t offset =
  924. props_block_offset + rand.Next() % (props_block_size - 8);
  925. char scratch[8] = {0};
  926. Slice buf;
  927. ASSERT_OK(rwfile->Read(offset, sizeof(scratch), &buf, scratch));
  928. scratch[0] ^= 0xff; // flip one bit
  929. ASSERT_OK(rwfile->Write(offset, buf));
  930. }
  931. // Ingest file.
  932. IngestExternalFileOptions ifo;
  933. ifo.write_global_seqno = std::get<0>(GetParam());
  934. ifo.verify_checksums_before_ingest = true;
  935. s = db_->IngestExternalFile({file_path}, ifo);
  936. ASSERT_NOK(s);
  937. } while (ChangeOptionsForFileIngestionTest());
  938. }
  939. TEST_F(ExternalSSTFileBasicTest, OverlappingFiles) {
  940. Options options = CurrentOptions();
  941. std::vector<std::string> files;
  942. {
  943. SstFileWriter sst_file_writer(EnvOptions(), options);
  944. std::string file1 = sst_files_dir_ + "file1.sst";
  945. ASSERT_OK(sst_file_writer.Open(file1));
  946. ASSERT_OK(sst_file_writer.Put("a", "z"));
  947. ASSERT_OK(sst_file_writer.Put("i", "m"));
  948. ExternalSstFileInfo file1_info;
  949. ASSERT_OK(sst_file_writer.Finish(&file1_info));
  950. files.push_back(std::move(file1));
  951. }
  952. {
  953. SstFileWriter sst_file_writer(EnvOptions(), options);
  954. std::string file2 = sst_files_dir_ + "file2.sst";
  955. ASSERT_OK(sst_file_writer.Open(file2));
  956. ASSERT_OK(sst_file_writer.Put("i", "k"));
  957. ExternalSstFileInfo file2_info;
  958. ASSERT_OK(sst_file_writer.Finish(&file2_info));
  959. files.push_back(std::move(file2));
  960. }
  961. IngestExternalFileOptions ifo;
  962. ASSERT_OK(db_->IngestExternalFile(files, ifo));
  963. ASSERT_EQ(Get("a"), "z");
  964. ASSERT_EQ(Get("i"), "k");
  965. int total_keys = 0;
  966. Iterator* iter = db_->NewIterator(ReadOptions());
  967. for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
  968. ASSERT_OK(iter->status());
  969. total_keys++;
  970. }
  971. delete iter;
  972. ASSERT_EQ(total_keys, 2);
  973. ASSERT_EQ(2, NumTableFilesAtLevel(0));
  974. }
  975. INSTANTIATE_TEST_CASE_P(ExternalSSTFileBasicTest, ExternalSSTFileBasicTest,
  976. testing::Values(std::make_tuple(true, true),
  977. std::make_tuple(true, false),
  978. std::make_tuple(false, true),
  979. std::make_tuple(false, false)));
  980. #endif // ROCKSDB_LITE
  981. } // namespace ROCKSDB_NAMESPACE
  982. int main(int argc, char** argv) {
  983. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  984. ::testing::InitGoogleTest(&argc, argv);
  985. return RUN_ALL_TESTS();
  986. }