#include <functional>
#include "db/db_test_util.h"
+#include "db/version_edit.h"
#include "port/port.h"
#include "port/stack_trace.h"
#include "rocksdb/sst_file_writer.h"
-#include "test_util/fault_injection_test_env.h"
#include "test_util/testutil.h"
+#include "util/random.h"
+#include "utilities/fault_injection_env.h"
namespace ROCKSDB_NAMESPACE {
: public DBTestBase,
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
public:
- ExternalSSTFileBasicTest() : DBTestBase("/external_sst_file_basic_test") {
+ ExternalSSTFileBasicTest()
+ : DBTestBase("/external_sst_file_basic_test", /*env_do_fsync=*/true) {
sst_files_dir_ = dbname_ + "/sst_files/";
- fault_injection_test_env_.reset(new FaultInjectionTestEnv(Env::Default()));
+ fault_injection_test_env_.reset(new FaultInjectionTestEnv(env_));
DestroyAndRecreateExternalSSTFilesDir();
}
void DestroyAndRecreateExternalSSTFilesDir() {
- test::DestroyDir(env_, sst_files_dir_);
+ DestroyDir(env_, sst_files_dir_);
env_->CreateDir(sst_files_dir_);
}
return db_->IngestExternalFile(files, opts);
}
+ Status AddFileWithFileChecksum(
+ const std::vector<std::string>& files,
+ const std::vector<std::string>& files_checksums,
+ const std::vector<std::string>& files_checksum_func_names,
+ bool verify_file_checksum = true, bool move_files = false,
+ bool skip_snapshot_check = false, bool write_global_seqno = true) {
+ IngestExternalFileOptions opts;
+ opts.move_files = move_files;
+ opts.snapshot_consistency = !skip_snapshot_check;
+ opts.allow_global_seqno = false;
+ opts.allow_blocking_flush = false;
+ opts.write_global_seqno = write_global_seqno;
+ opts.verify_file_checksum = verify_file_checksum;
+
+ IngestExternalFileArg arg;
+ arg.column_family = db_->DefaultColumnFamily();
+ arg.external_files = files;
+ arg.options = opts;
+ arg.files_checksums = files_checksums;
+ arg.files_checksum_func_names = files_checksum_func_names;
+ return db_->IngestExternalFiles({arg});
+ }
+
Status GenerateAndAddExternalFile(
const Options options, std::vector<int> keys,
const std::vector<ValueType>& value_types,
write_global_seqno, verify_checksums_before_ingest, true_data);
}
- ~ExternalSSTFileBasicTest() override {
- test::DestroyDir(env_, sst_files_dir_);
- }
+ ~ExternalSSTFileBasicTest() override { DestroyDir(env_, sst_files_dir_); }
protected:
std::string sst_files_dir_;
ASSERT_EQ(file1_info.num_range_del_entries, 0);
ASSERT_EQ(file1_info.smallest_range_del_key, "");
ASSERT_EQ(file1_info.largest_range_del_key, "");
+ ASSERT_EQ(file1_info.file_checksum, kUnknownFileChecksum);
+ ASSERT_EQ(file1_info.file_checksum_func_name, kUnknownFileChecksumFuncName);
// sst_file_writer already finished, cannot add this value
s = sst_file_writer.Put(Key(100), "bad_val");
ASSERT_FALSE(s.ok()) << s.ToString();
DestroyAndRecreateExternalSSTFilesDir();
}
+class ChecksumVerifyHelper {
+ private:
+ Options options_;
+
+ public:
+ ChecksumVerifyHelper(Options& options) : options_(options) {}
+ ~ChecksumVerifyHelper() {}
+
+ Status GetSingleFileChecksumAndFuncName(
+ const std::string& file_path, std::string* file_checksum,
+ std::string* file_checksum_func_name) {
+ Status s;
+ EnvOptions soptions;
+ std::unique_ptr<SequentialFile> file_reader;
+ s = options_.env->NewSequentialFile(file_path, &file_reader, soptions);
+ if (!s.ok()) {
+ return s;
+ }
+ std::unique_ptr<char[]> scratch(new char[2048]);
+ Slice result;
+ FileChecksumGenFactory* file_checksum_gen_factory =
+ options_.file_checksum_gen_factory.get();
+ if (file_checksum_gen_factory == nullptr) {
+ *file_checksum = kUnknownFileChecksum;
+ *file_checksum_func_name = kUnknownFileChecksumFuncName;
+ return Status::OK();
+ } else {
+ FileChecksumGenContext gen_context;
+ std::unique_ptr<FileChecksumGenerator> file_checksum_gen =
+ file_checksum_gen_factory->CreateFileChecksumGenerator(gen_context);
+ *file_checksum_func_name = file_checksum_gen->Name();
+ s = file_reader->Read(2048, &result, scratch.get());
+ if (!s.ok()) {
+ return s;
+ }
+ while (result.size() != 0) {
+ file_checksum_gen->Update(scratch.get(), result.size());
+ s = file_reader->Read(2048, &result, scratch.get());
+ if (!s.ok()) {
+ return s;
+ }
+ }
+ file_checksum_gen->Finalize();
+ *file_checksum = file_checksum_gen->GetChecksum();
+ }
+ return Status::OK();
+ }
+};
+
+TEST_F(ExternalSSTFileBasicTest, BasicWithFileChecksumCrc32c) {
+ Options options = CurrentOptions();
+ options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
+ ChecksumVerifyHelper checksum_helper(options);
+
+ SstFileWriter sst_file_writer(EnvOptions(), options);
+
+ // Current file size should be 0 after sst_file_writer init and before open a
+ // file.
+ ASSERT_EQ(sst_file_writer.FileSize(), 0);
+
+ // file1.sst (0 => 99)
+ std::string file1 = sst_files_dir_ + "file1.sst";
+ ASSERT_OK(sst_file_writer.Open(file1));
+ for (int k = 0; k < 100; k++) {
+ ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
+ }
+ ExternalSstFileInfo file1_info;
+ Status s = sst_file_writer.Finish(&file1_info);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ std::string file_checksum, file_checksum_func_name;
+ ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
+ file1, &file_checksum, &file_checksum_func_name));
+
+ // Current file size should be non-zero after success write.
+ ASSERT_GT(sst_file_writer.FileSize(), 0);
+
+ ASSERT_EQ(file1_info.file_path, file1);
+ ASSERT_EQ(file1_info.num_entries, 100);
+ ASSERT_EQ(file1_info.smallest_key, Key(0));
+ ASSERT_EQ(file1_info.largest_key, Key(99));
+ ASSERT_EQ(file1_info.num_range_del_entries, 0);
+ ASSERT_EQ(file1_info.smallest_range_del_key, "");
+ ASSERT_EQ(file1_info.largest_range_del_key, "");
+ ASSERT_EQ(file1_info.file_checksum, file_checksum);
+ ASSERT_EQ(file1_info.file_checksum_func_name, file_checksum_func_name);
+ // sst_file_writer already finished, cannot add this value
+ s = sst_file_writer.Put(Key(100), "bad_val");
+ ASSERT_FALSE(s.ok()) << s.ToString();
+ s = sst_file_writer.DeleteRange(Key(100), Key(200));
+ ASSERT_FALSE(s.ok()) << s.ToString();
+
+ DestroyAndReopen(options);
+ // Add file using file path
+ s = DeprecatedAddFile({file1});
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
+ for (int k = 0; k < 100; k++) {
+ ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
+ }
+
+ DestroyAndRecreateExternalSSTFilesDir();
+}
+
+TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
+ Options old_options = CurrentOptions();
+ Options options = CurrentOptions();
+ options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
+ const ImmutableCFOptions ioptions(options);
+ ChecksumVerifyHelper checksum_helper(options);
+
+ SstFileWriter sst_file_writer(EnvOptions(), options);
+
+ // file01.sst (1000 => 1099)
+ std::string file1 = sst_files_dir_ + "file01.sst";
+ ASSERT_OK(sst_file_writer.Open(file1));
+ for (int k = 1000; k < 1100; k++) {
+ ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
+ }
+ ExternalSstFileInfo file1_info;
+ Status s = sst_file_writer.Finish(&file1_info);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ ASSERT_EQ(file1_info.file_path, file1);
+ ASSERT_EQ(file1_info.num_entries, 100);
+ ASSERT_EQ(file1_info.smallest_key, Key(1000));
+ ASSERT_EQ(file1_info.largest_key, Key(1099));
+ std::string file_checksum1, file_checksum_func_name1;
+ ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
+ file1, &file_checksum1, &file_checksum_func_name1));
+ ASSERT_EQ(file1_info.file_checksum, file_checksum1);
+ ASSERT_EQ(file1_info.file_checksum_func_name, file_checksum_func_name1);
+
+ // file02.sst (1100 => 1299)
+ std::string file2 = sst_files_dir_ + "file02.sst";
+ ASSERT_OK(sst_file_writer.Open(file2));
+ for (int k = 1100; k < 1300; k++) {
+ ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
+ }
+ ExternalSstFileInfo file2_info;
+ s = sst_file_writer.Finish(&file2_info);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ ASSERT_EQ(file2_info.file_path, file2);
+ ASSERT_EQ(file2_info.num_entries, 200);
+ ASSERT_EQ(file2_info.smallest_key, Key(1100));
+ ASSERT_EQ(file2_info.largest_key, Key(1299));
+ std::string file_checksum2, file_checksum_func_name2;
+ ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
+ file2, &file_checksum2, &file_checksum_func_name2));
+ ASSERT_EQ(file2_info.file_checksum, file_checksum2);
+ ASSERT_EQ(file2_info.file_checksum_func_name, file_checksum_func_name2);
+
+ // file03.sst (1300 => 1499)
+ std::string file3 = sst_files_dir_ + "file03.sst";
+ ASSERT_OK(sst_file_writer.Open(file3));
+ for (int k = 1300; k < 1500; k++) {
+ ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val_overlap"));
+ }
+ ExternalSstFileInfo file3_info;
+ s = sst_file_writer.Finish(&file3_info);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ ASSERT_EQ(file3_info.file_path, file3);
+ ASSERT_EQ(file3_info.num_entries, 200);
+ ASSERT_EQ(file3_info.smallest_key, Key(1300));
+ ASSERT_EQ(file3_info.largest_key, Key(1499));
+ std::string file_checksum3, file_checksum_func_name3;
+ ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
+ file3, &file_checksum3, &file_checksum_func_name3));
+ ASSERT_EQ(file3_info.file_checksum, file_checksum3);
+ ASSERT_EQ(file3_info.file_checksum_func_name, file_checksum_func_name3);
+
+ // file04.sst (1500 => 1799)
+ std::string file4 = sst_files_dir_ + "file04.sst";
+ ASSERT_OK(sst_file_writer.Open(file4));
+ for (int k = 1500; k < 1800; k++) {
+ ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val_overlap"));
+ }
+ ExternalSstFileInfo file4_info;
+ s = sst_file_writer.Finish(&file4_info);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ ASSERT_EQ(file4_info.file_path, file4);
+ ASSERT_EQ(file4_info.num_entries, 300);
+ ASSERT_EQ(file4_info.smallest_key, Key(1500));
+ ASSERT_EQ(file4_info.largest_key, Key(1799));
+ std::string file_checksum4, file_checksum_func_name4;
+ ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
+ file4, &file_checksum4, &file_checksum_func_name4));
+ ASSERT_EQ(file4_info.file_checksum, file_checksum4);
+ ASSERT_EQ(file4_info.file_checksum_func_name, file_checksum_func_name4);
+
+ // file05.sst (1800 => 1899)
+ std::string file5 = sst_files_dir_ + "file05.sst";
+ ASSERT_OK(sst_file_writer.Open(file5));
+ for (int k = 1800; k < 2000; k++) {
+ ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val_overlap"));
+ }
+ ExternalSstFileInfo file5_info;
+ s = sst_file_writer.Finish(&file5_info);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ ASSERT_EQ(file5_info.file_path, file5);
+ ASSERT_EQ(file5_info.num_entries, 200);
+ ASSERT_EQ(file5_info.smallest_key, Key(1800));
+ ASSERT_EQ(file5_info.largest_key, Key(1999));
+ std::string file_checksum5, file_checksum_func_name5;
+ ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
+ file5, &file_checksum5, &file_checksum_func_name5));
+ ASSERT_EQ(file5_info.file_checksum, file_checksum5);
+ ASSERT_EQ(file5_info.file_checksum_func_name, file_checksum_func_name5);
+
+ // file06.sst (2000 => 2199)
+ std::string file6 = sst_files_dir_ + "file06.sst";
+ ASSERT_OK(sst_file_writer.Open(file6));
+ for (int k = 2000; k < 2200; k++) {
+ ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val_overlap"));
+ }
+ ExternalSstFileInfo file6_info;
+ s = sst_file_writer.Finish(&file6_info);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ ASSERT_EQ(file6_info.file_path, file6);
+ ASSERT_EQ(file6_info.num_entries, 200);
+ ASSERT_EQ(file6_info.smallest_key, Key(2000));
+ ASSERT_EQ(file6_info.largest_key, Key(2199));
+ std::string file_checksum6, file_checksum_func_name6;
+ ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
+ file6, &file_checksum6, &file_checksum_func_name6));
+ ASSERT_EQ(file6_info.file_checksum, file_checksum6);
+ ASSERT_EQ(file6_info.file_checksum_func_name, file_checksum_func_name6);
+
+ s = AddFileWithFileChecksum({file1}, {file_checksum1, "xyz"},
+ {file_checksum1}, true, false, false, false);
+ // does not care the checksum input since db does not enable file checksum
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ ASSERT_OK(env_->FileExists(file1));
+ std::vector<LiveFileMetaData> live_files;
+ dbfull()->GetLiveFilesMetaData(&live_files);
+ std::set<std::string> set1;
+ for (auto f : live_files) {
+ set1.insert(f.name);
+ ASSERT_EQ(f.file_checksum, kUnknownFileChecksum);
+ ASSERT_EQ(f.file_checksum_func_name, kUnknownFileChecksumFuncName);
+ }
+
+ // Reopen Db with checksum enabled
+ Reopen(options);
+ // Enable verify_file_checksum option
+ // The checksum vector does not match, fail the ingestion
+ s = AddFileWithFileChecksum({file2}, {file_checksum2, "xyz"},
+ {file_checksum_func_name2}, true, false, false,
+ false);
+ ASSERT_FALSE(s.ok()) << s.ToString();
+
+ // Enable verify_file_checksum option
+ // The checksum name does not match, fail the ingestion
+ s = AddFileWithFileChecksum({file2}, {file_checksum2}, {"xyz"}, true, false,
+ false, false);
+ ASSERT_FALSE(s.ok()) << s.ToString();
+
+ // Enable verify_file_checksum option
+ // The checksum itself does not match, fail the ingestion
+ s = AddFileWithFileChecksum({file2}, {"xyz"}, {file_checksum_func_name2},
+ true, false, false, false);
+ ASSERT_FALSE(s.ok()) << s.ToString();
+
+ // Enable verify_file_checksum option
+ // All matches, ingestion is successful
+ s = AddFileWithFileChecksum({file2}, {file_checksum2},
+ {file_checksum_func_name2}, true, false, false,
+ false);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ std::vector<LiveFileMetaData> live_files1;
+ dbfull()->GetLiveFilesMetaData(&live_files1);
+ for (auto f : live_files1) {
+ if (set1.find(f.name) == set1.end()) {
+ ASSERT_EQ(f.file_checksum, file_checksum2);
+ ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name2);
+ set1.insert(f.name);
+ }
+ }
+ ASSERT_OK(env_->FileExists(file2));
+
+ // Enable verify_file_checksum option
+ // No checksum information is provided, generate it when ingesting
+ std::vector<std::string> checksum, checksum_func;
+ s = AddFileWithFileChecksum({file3}, checksum, checksum_func, true, false,
+ false, false);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ std::vector<LiveFileMetaData> live_files2;
+ dbfull()->GetLiveFilesMetaData(&live_files2);
+ for (auto f : live_files2) {
+ if (set1.find(f.name) == set1.end()) {
+ ASSERT_EQ(f.file_checksum, file_checksum3);
+ ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name3);
+ set1.insert(f.name);
+ }
+ }
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ ASSERT_OK(env_->FileExists(file3));
+
+ // Does not enable verify_file_checksum options
+ // The checksum name does not match, fail the ingestion
+ s = AddFileWithFileChecksum({file4}, {file_checksum4}, {"xyz"}, false, false,
+ false, false);
+ ASSERT_FALSE(s.ok()) << s.ToString();
+
+ // Does not enable verify_file_checksum options
+ // Checksum function name matches, store the checksum being ingested.
+ s = AddFileWithFileChecksum({file4}, {"asd"}, {file_checksum_func_name4},
+ false, false, false, false);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ std::vector<LiveFileMetaData> live_files3;
+ dbfull()->GetLiveFilesMetaData(&live_files3);
+ for (auto f : live_files3) {
+ if (set1.find(f.name) == set1.end()) {
+ ASSERT_FALSE(f.file_checksum == file_checksum4);
+ ASSERT_EQ(f.file_checksum, "asd");
+ ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name4);
+ set1.insert(f.name);
+ }
+ }
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ ASSERT_OK(env_->FileExists(file4));
+
+ // enable verify_file_checksum options, DB enable checksum, and enable
+ // write_global_seq. So the checksum stored is different from the one
+ // ingested due to the sequence number changes.
+ s = AddFileWithFileChecksum({file5}, {file_checksum5},
+ {file_checksum_func_name5}, true, false, false,
+ true);
+ ASSERT_OK(s);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ std::vector<LiveFileMetaData> live_files4;
+ dbfull()->GetLiveFilesMetaData(&live_files4);
+ for (auto f : live_files4) {
+ if (set1.find(f.name) == set1.end()) {
+ std::string cur_checksum5, cur_checksum_func_name5;
+ ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
+ dbname_ + f.name, &cur_checksum5, &cur_checksum_func_name5));
+ ASSERT_EQ(f.file_checksum, cur_checksum5);
+ ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name5);
+ set1.insert(f.name);
+ }
+ }
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ ASSERT_OK(env_->FileExists(file5));
+
+ // Does not enable verify_file_checksum options and also the ingested file
+ // checksum information is empty. DB will generate and store the checksum
+ // in Manifest.
+ std::vector<std::string> files_c6, files_name6;
+ s = AddFileWithFileChecksum({file6}, files_c6, files_name6, false, false,
+ false, false);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ std::vector<LiveFileMetaData> live_files6;
+ dbfull()->GetLiveFilesMetaData(&live_files6);
+ for (auto f : live_files6) {
+ if (set1.find(f.name) == set1.end()) {
+ ASSERT_EQ(f.file_checksum, file_checksum6);
+ ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name6);
+ set1.insert(f.name);
+ }
+ }
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ ASSERT_OK(env_->FileExists(file6));
+}
+
TEST_F(ExternalSSTFileBasicTest, NoCopy) {
Options options = CurrentOptions();
const ImmutableCFOptions ioptions(options);
}
Options sst_file_writer_options;
+ sst_file_writer_options.env = env_;
std::unique_ptr<SstFileWriter> sst_file_writer(
new SstFileWriter(EnvOptions(), sst_file_writer_options));
std::string file_name =
TEST_F(ExternalSSTFileBasicTest, VerifyChecksumReadahead) {
Options options;
options.create_if_missing = true;
- SpecialEnv senv(Env::Default());
+ SpecialEnv senv(env_);
options.env = &senv;
DestroyAndReopen(options);
Options sst_file_writer_options;
+ sst_file_writer_options.env = env_;
std::unique_ptr<SstFileWriter> sst_file_writer(
new SstFileWriter(EnvOptions(), sst_file_writer_options));
std::string file_name = sst_files_dir_ + "verify_checksum_readahead_test.sst";
ASSERT_OK(sst_file_writer->Open(file_name));
Random rnd(301);
- std::string value = DBTestBase::RandomString(&rnd, 4000);
+ std::string value = rnd.RandomString(4000);
for (int i = 0; i < 5000; i++) {
ASSERT_OK(sst_file_writer->Put(DBTestBase::Key(i), value));
}
Destroy(options);
}
+TEST_F(ExternalSSTFileBasicTest, IngestRangeDeletionTombstoneWithGlobalSeqno) {
+ for (int i = 5; i < 25; i++) {
+ ASSERT_OK(db_->Put(WriteOptions(), db_->DefaultColumnFamily(), Key(i),
+ Key(i) + "_val"));
+ }
+
+ Options options = CurrentOptions();
+ options.disable_auto_compactions = true;
+ Reopen(options);
+ SstFileWriter sst_file_writer(EnvOptions(), options);
+
+ // file.sst (delete 0 => 30)
+ std::string file = sst_files_dir_ + "file.sst";
+ ASSERT_OK(sst_file_writer.Open(file));
+ ASSERT_OK(sst_file_writer.DeleteRange(Key(0), Key(30)));
+ ExternalSstFileInfo file_info;
+ ASSERT_OK(sst_file_writer.Finish(&file_info));
+ ASSERT_EQ(file_info.file_path, file);
+ ASSERT_EQ(file_info.num_entries, 0);
+ ASSERT_EQ(file_info.smallest_key, "");
+ ASSERT_EQ(file_info.largest_key, "");
+ ASSERT_EQ(file_info.num_range_del_entries, 1);
+ ASSERT_EQ(file_info.smallest_range_del_key, Key(0));
+ ASSERT_EQ(file_info.largest_range_del_key, Key(30));
+
+ IngestExternalFileOptions ifo;
+ ifo.move_files = true;
+ ifo.snapshot_consistency = true;
+ ifo.allow_global_seqno = true;
+ ifo.write_global_seqno = true;
+ ifo.verify_checksums_before_ingest = false;
+ ASSERT_OK(db_->IngestExternalFile({file}, ifo));
+
+ for (int i = 5; i < 25; i++) {
+ std::string res;
+ ASSERT_TRUE(db_->Get(ReadOptions(), Key(i), &res).IsNotFound());
+ }
+}
+
TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
int kNumLevels = 7;
Options options = CurrentOptions();