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