|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
|
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
|
|
// (found in the LICENSE.Apache file in the root directory).
|
|
|
|
|
|
|
|
#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/testharness.h"
|
|
|
|
#include "test_util/testutil.h"
|
|
|
|
#include "util/random.h"
|
|
|
|
#include "utilities/fault_injection_env.h"
|
|
|
|
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
|
|
|
|
|
|
#ifndef ROCKSDB_LITE
|
|
|
|
class ExternalSSTFileBasicTest
|
|
|
|
: public DBTestBase,
|
|
|
|
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
|
|
|
|
public:
|
|
|
|
ExternalSSTFileBasicTest()
|
|
|
|
: DBTestBase("external_sst_file_basic_test", /*env_do_fsync=*/true) {
|
|
|
|
sst_files_dir_ = dbname_ + "_sst_files/";
|
Fix many tests to run with MEM_ENV and ENCRYPTED_ENV; Introduce a MemoryFileSystem class (#7566)
Summary:
This PR does a few things:
1. The MockFileSystem class was split out from the MockEnv. This change would theoretically allow a MockFileSystem to be used by other Environments as well (if we created a means of constructing one). The MockFileSystem implements a FileSystem in its entirety and does not rely on any Wrapper implementation.
2. Make the RocksDB test suite work when MOCK_ENV=1 and ENCRYPTED_ENV=1 are set. To accomplish this, a few things were needed:
- The tests that tried to use the "wrong" environment (Env::Default() instead of env_) were updated
- The MockFileSystem was changed to support the features it was missing or mishandled (such as recursively deleting files in a directory or supporting renaming of a directory).
3. Updated the test framework to have a ROCKSDB_GTEST_SKIP macro. This can be used to flag tests that are skipped. Currently, this defaults to doing nothing (marks the test as SUCCESS) but will mark the tests as SKIPPED when RocksDB is upgraded to a version of gtest that supports this (gtest-1.10).
I have run a full "make check" with MEM_ENV, ENCRYPTED_ENV, both, and neither under both MacOS and RedHat. A few tests were disabled/skipped for the MEM/ENCRYPTED cases. The error_handler_fs_test fails/hangs for MEM_ENV (presumably a timing problem) and I will introduce another PR/issue to track that problem. (I will also push a change to disable those tests soon). There is one more test in DBTest2 that also fails which I need to investigate or skip before this PR is merged.
Theoretically, this PR should also allow the test suite to run against an Env loaded from the registry, though I do not have one to try it with currently.
Finally, once this is accepted, it would be nice if there was a CircleCI job to run these tests on a checkin so this effort does not become stale. I do not know how to do that, so if someone could write that job, it would be appreciated :)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/7566
Reviewed By: zhichao-cao
Differential Revision: D24408980
Pulled By: jay-zhuang
fbshipit-source-id: 911b1554a4d0da06fd51feca0c090a4abdcb4a5f
4 years ago
|
|
|
fault_injection_test_env_.reset(new FaultInjectionTestEnv(env_));
|
|
|
|
DestroyAndRecreateExternalSSTFilesDir();
|
|
|
|
|
|
|
|
// Check if the Env supports RandomRWFile
|
|
|
|
std::string file_path = sst_files_dir_ + "test_random_rw_file";
|
|
|
|
std::unique_ptr<WritableFile> wfile;
|
|
|
|
assert(env_->NewWritableFile(file_path, &wfile, EnvOptions()).ok());
|
|
|
|
wfile.reset();
|
|
|
|
std::unique_ptr<RandomRWFile> rwfile;
|
|
|
|
Status s = env_->NewRandomRWFile(file_path, &rwfile, EnvOptions());
|
|
|
|
if (s.IsNotSupported()) {
|
|
|
|
random_rwfile_supported_ = false;
|
|
|
|
} else {
|
|
|
|
EXPECT_OK(s);
|
|
|
|
random_rwfile_supported_ = true;
|
|
|
|
}
|
|
|
|
rwfile.reset();
|
|
|
|
EXPECT_OK(env_->DeleteFile(file_path));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DestroyAndRecreateExternalSSTFilesDir() {
|
|
|
|
ASSERT_OK(DestroyDir(env_, sst_files_dir_));
|
|
|
|
ASSERT_OK(env_->CreateDir(sst_files_dir_));
|
|
|
|
}
|
|
|
|
|
|
|
|
Status DeprecatedAddFile(const std::vector<std::string>& files,
|
|
|
|
bool move_files = false,
|
|
|
|
bool skip_snapshot_check = false) {
|
|
|
|
IngestExternalFileOptions opts;
|
|
|
|
opts.move_files = move_files;
|
|
|
|
opts.snapshot_consistency = !skip_snapshot_check;
|
|
|
|
opts.allow_global_seqno = false;
|
|
|
|
opts.allow_blocking_flush = false;
|
|
|
|
return db_->IngestExternalFile(files, opts);
|
|
|
|
}
|
|
|
|
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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,
|
|
|
|
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) {
|
|
|
|
assert(value_types.size() == 1 || keys.size() == value_types.size());
|
|
|
|
std::string file_path = sst_files_dir_ + std::to_string(file_id);
|
|
|
|
SstFileWriter sst_file_writer(EnvOptions(), options);
|
|
|
|
|
|
|
|
Status s = sst_file_writer.Open(file_path);
|
|
|
|
if (!s.ok()) {
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
for (size_t i = 0; i < range_deletions.size(); i++) {
|
|
|
|
// Account for the effect of range deletions on true_data before
|
|
|
|
// all point operators, even though sst_file_writer.DeleteRange
|
|
|
|
// must be called before other sst_file_writer methods. This is
|
|
|
|
// because point writes take precedence over range deletions
|
|
|
|
// in the same ingested sst.
|
|
|
|
std::string start_key = Key(range_deletions[i].first);
|
|
|
|
std::string end_key = Key(range_deletions[i].second);
|
|
|
|
s = sst_file_writer.DeleteRange(start_key, end_key);
|
|
|
|
if (!s.ok()) {
|
|
|
|
sst_file_writer.Finish();
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
auto start_key_it = true_data->find(start_key);
|
|
|
|
if (start_key_it == true_data->end()) {
|
|
|
|
start_key_it = true_data->upper_bound(start_key);
|
|
|
|
}
|
|
|
|
auto end_key_it = true_data->find(end_key);
|
|
|
|
if (end_key_it == true_data->end()) {
|
|
|
|
end_key_it = true_data->upper_bound(end_key);
|
|
|
|
}
|
|
|
|
true_data->erase(start_key_it, end_key_it);
|
|
|
|
}
|
|
|
|
for (size_t i = 0; i < keys.size(); i++) {
|
|
|
|
std::string key = Key(keys[i]);
|
|
|
|
std::string value = Key(keys[i]) + std::to_string(file_id);
|
|
|
|
ValueType value_type =
|
|
|
|
(value_types.size() == 1 ? value_types[0] : value_types[i]);
|
|
|
|
switch (value_type) {
|
|
|
|
case ValueType::kTypeValue:
|
|
|
|
s = sst_file_writer.Put(key, value);
|
|
|
|
(*true_data)[key] = value;
|
|
|
|
break;
|
|
|
|
case ValueType::kTypeMerge:
|
|
|
|
s = sst_file_writer.Merge(key, value);
|
|
|
|
// we only use TestPutOperator in this test
|
|
|
|
(*true_data)[key] = value;
|
|
|
|
break;
|
|
|
|
case ValueType::kTypeDeletion:
|
|
|
|
s = sst_file_writer.Delete(key);
|
|
|
|
true_data->erase(key);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return Status::InvalidArgument("Value type is not supported");
|
|
|
|
}
|
|
|
|
if (!s.ok()) {
|
|
|
|
sst_file_writer.Finish();
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s = sst_file_writer.Finish();
|
|
|
|
|
|
|
|
if (s.ok()) {
|
|
|
|
IngestExternalFileOptions ifo;
|
|
|
|
ifo.allow_global_seqno = true;
|
|
|
|
ifo.write_global_seqno = write_global_seqno;
|
|
|
|
ifo.verify_checksums_before_ingest = verify_checksums_before_ingest;
|
|
|
|
s = db_->IngestExternalFile({file_path}, ifo);
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status 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) {
|
|
|
|
return GenerateAndAddExternalFile(
|
|
|
|
options, keys, value_types, {}, file_id, write_global_seqno,
|
|
|
|
verify_checksums_before_ingest, true_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status 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) {
|
|
|
|
return GenerateAndAddExternalFile(
|
|
|
|
options, keys, std::vector<ValueType>(1, value_type), file_id,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, true_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
~ExternalSSTFileBasicTest() override {
|
|
|
|
DestroyDir(env_, sst_files_dir_).PermitUncheckedError();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
std::string sst_files_dir_;
|
|
|
|
std::unique_ptr<FaultInjectionTestEnv> fault_injection_test_env_;
|
|
|
|
bool random_rwfile_supported_;
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, Basic) {
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
|
|
|
|
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_OK(s) << s.ToString();
|
|
|
|
|
|
|
|
// 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, 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_NOK(s) << s.ToString();
|
|
|
|
s = sst_file_writer.DeleteRange(Key(100), Key(200));
|
|
|
|
ASSERT_NOK(s) << s.ToString();
|
|
|
|
|
|
|
|
DestroyAndReopen(options);
|
|
|
|
// Add file using file path
|
|
|
|
s = DeprecatedAddFile({file1});
|
|
|
|
ASSERT_OK(s) << s.ToString();
|
|
|
|
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
|
|
|
for (int k = 0; k < 100; k++) {
|
|
|
|
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
|
|
|
}
|
|
|
|
|
|
|
|
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_OK(s) << 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_NOK(s) << s.ToString();
|
|
|
|
s = sst_file_writer.DeleteRange(Key(100), Key(200));
|
|
|
|
ASSERT_NOK(s) << s.ToString();
|
|
|
|
|
|
|
|
DestroyAndReopen(options);
|
|
|
|
// Add file using file path
|
|
|
|
s = DeprecatedAddFile({file1});
|
|
|
|
ASSERT_OK(s) << s.ToString();
|
|
|
|
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
|
|
|
for (int k = 0; k < 100; k++) {
|
|
|
|
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
|
|
|
}
|
|
|
|
|
|
|
|
DestroyAndRecreateExternalSSTFilesDir();
|
|
|
|
}
|
|
|
|
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check the temperature of the file being ingested
|
|
|
|
ColumnFamilyMetaData metadata;
|
|
|
|
db_->GetColumnFamilyMetaData(&metadata);
|
|
|
|
ASSERT_EQ(1, metadata.file_count);
|
|
|
|
ASSERT_EQ(Temperature::kUnknown, metadata.levels[6].files[0].temperature);
|
|
|
|
auto size = GetSstSizeHelper(Temperature::kUnknown);
|
|
|
|
ASSERT_GT(size, 0);
|
|
|
|
size = GetSstSizeHelper(Temperature::kWarm);
|
|
|
|
ASSERT_EQ(size, 0);
|
|
|
|
size = GetSstSizeHelper(Temperature::kHot);
|
|
|
|
ASSERT_EQ(size, 0);
|
|
|
|
size = GetSstSizeHelper(Temperature::kCold);
|
|
|
|
ASSERT_EQ(size, 0);
|
|
|
|
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
// 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_NOK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
|
|
|
|
// 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_NOK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
|
|
|
|
// 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_NOK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
|
|
|
|
// Enable verify_file_checksum option
|
|
|
|
// All matches, ingestion is successful
|
|
|
|
s = AddFileWithFileChecksum({file2}, {file_checksum2},
|
|
|
|
{file_checksum_func_name2}, true, false, false,
|
|
|
|
false);
|
|
|
|
ASSERT_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_NOK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
|
|
|
|
// 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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
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_OK(s) << s.ToString();
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
ASSERT_OK(env_->FileExists(file6));
|
|
|
|
db_->GetColumnFamilyMetaData(&metadata);
|
|
|
|
size = GetSstSizeHelper(Temperature::kUnknown);
|
|
|
|
ASSERT_GT(size, 0);
|
|
|
|
size = GetSstSizeHelper(Temperature::kWarm);
|
|
|
|
ASSERT_EQ(size, 0);
|
|
|
|
size = GetSstSizeHelper(Temperature::kHot);
|
|
|
|
ASSERT_EQ(size, 0);
|
|
|
|
size = GetSstSizeHelper(Temperature::kCold);
|
|
|
|
ASSERT_EQ(size, 0);
|
Ingest SST files with checksum information (#6891)
Summary:
Application can ingest SST files with file checksum information, such that during ingestion, DB is able to check data integrity and identify of the SST file. The PR introduces generate_and_verify_file_checksum to IngestExternalFileOption to control if the ingested checksum information should be verified with the generated checksum.
1. If generate_and_verify_file_checksum options is *FALSE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enables the SST file checksum and the checksum function name matches the checksum function name in DB, we trust the ingested checksum, store it in Manifest. If the checksum function name does not match, we treat that as an error and fail the IngestExternalFile() call.
2. If generate_and_verify_file_checksum options is *TRUE*: *1)* if DB does not enable SST file checksum, the checksum information ingested will be ignored; *2)* if DB enable the SST file checksum, we will use the checksum generator from DB to calculate the checksum for each ingested SST files after they are copied or moved. Then, compare the checksum results with the ingested checksum information: _A)_ if the checksum function name does not match, _verification always report true_ and we store the DB generated checksum information in Manifest. _B)_ if the checksum function name mach, and checksum match, ingestion continues and stores the checksum information in the Manifest. Otherwise, terminate file ingestion and report file corruption.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6891
Test Plan: added unit test, pass make asan_check
Reviewed By: pdillinger
Differential Revision: D21935988
Pulled By: zhichao-cao
fbshipit-source-id: 7b55f486632db467e76d72602218d0658aa7f6ed
4 years ago
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, NoCopy) {
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
const ImmutableCFOptions ioptions(options);
|
|
|
|
|
|
|
|
SstFileWriter sst_file_writer(EnvOptions(), options);
|
|
|
|
|
|
|
|
// 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_OK(s) << s.ToString();
|
|
|
|
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));
|
|
|
|
|
|
|
|
// file2.sst (100 => 299)
|
|
|
|
std::string file2 = sst_files_dir_ + "file2.sst";
|
|
|
|
ASSERT_OK(sst_file_writer.Open(file2));
|
|
|
|
for (int k = 100; k < 300; k++) {
|
|
|
|
ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
|
|
|
|
}
|
|
|
|
ExternalSstFileInfo file2_info;
|
|
|
|
s = sst_file_writer.Finish(&file2_info);
|
|
|
|
ASSERT_OK(s) << s.ToString();
|
|
|
|
ASSERT_EQ(file2_info.file_path, file2);
|
|
|
|
ASSERT_EQ(file2_info.num_entries, 200);
|
|
|
|
ASSERT_EQ(file2_info.smallest_key, Key(100));
|
|
|
|
ASSERT_EQ(file2_info.largest_key, Key(299));
|
|
|
|
|
|
|
|
// file3.sst (110 => 124) .. overlap with file2.sst
|
|
|
|
std::string file3 = sst_files_dir_ + "file3.sst";
|
|
|
|
ASSERT_OK(sst_file_writer.Open(file3));
|
|
|
|
for (int k = 110; k < 125; k++) {
|
|
|
|
ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val_overlap"));
|
|
|
|
}
|
|
|
|
ExternalSstFileInfo file3_info;
|
|
|
|
s = sst_file_writer.Finish(&file3_info);
|
|
|
|
ASSERT_OK(s) << s.ToString();
|
|
|
|
ASSERT_EQ(file3_info.file_path, file3);
|
|
|
|
ASSERT_EQ(file3_info.num_entries, 15);
|
|
|
|
ASSERT_EQ(file3_info.smallest_key, Key(110));
|
|
|
|
ASSERT_EQ(file3_info.largest_key, Key(124));
|
|
|
|
|
|
|
|
s = DeprecatedAddFile({file1}, true /* move file */);
|
|
|
|
ASSERT_OK(s) << s.ToString();
|
|
|
|
ASSERT_EQ(Status::NotFound(), env_->FileExists(file1));
|
|
|
|
|
|
|
|
s = DeprecatedAddFile({file2}, false /* copy file */);
|
|
|
|
ASSERT_OK(s) << s.ToString();
|
|
|
|
ASSERT_OK(env_->FileExists(file2));
|
|
|
|
|
|
|
|
// This file has overlapping values with the existing data
|
|
|
|
s = DeprecatedAddFile({file3}, true /* move file */);
|
|
|
|
ASSERT_NOK(s) << s.ToString();
|
|
|
|
ASSERT_OK(env_->FileExists(file3));
|
|
|
|
|
|
|
|
for (int k = 0; k < 300; k++) {
|
|
|
|
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
|
|
|
|
bool write_global_seqno = std::get<0>(GetParam());
|
|
|
|
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
|
|
|
do {
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
DestroyAndReopen(options);
|
|
|
|
std::map<std::string, std::string> true_data;
|
|
|
|
|
|
|
|
int file_id = 1;
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1, 2, 3, 4, 5, 6}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {10, 11, 12, 13}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1, 4, 6}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {11, 15, 19}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {120, 130}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1, 130}, ValueType::kTypeValue, file_id++, write_global_seqno,
|
|
|
|
verify_checksums_before_ingest, &true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
|
|
|
|
|
|
|
// Write some keys through normal write path
|
|
|
|
for (int i = 0; i < 50; i++) {
|
|
|
|
ASSERT_OK(Put(Key(i), "memtable"));
|
|
|
|
true_data[Key(i)] = "memtable";
|
|
|
|
}
|
|
|
|
SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {60, 61, 62}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {40, 41, 42}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {20, 30, 40}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
|
|
|
|
|
|
|
|
const Snapshot* snapshot = db_->GetSnapshot();
|
|
|
|
|
|
|
|
// We will need a seqno for the file regardless if the file overwrite
|
|
|
|
// keys in the DB or not because we have a snapshot
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1000, 1002}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// A global seqno will be assigned anyway because of the snapshot
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {2000, 3002}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// A global seqno will be assigned anyway because of the snapshot
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1, 20, 40, 100, 150}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// A global seqno will be assigned anyway because of the snapshot
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
|
|
|
|
|
|
|
db_->ReleaseSnapshot(snapshot);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {5000, 5001}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// No snapshot anymore, no need to assign a seqno
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
|
|
|
|
|
|
|
size_t kcnt = 0;
|
|
|
|
VerifyDBFromMap(true_data, &kcnt, false);
|
|
|
|
} while (ChangeOptionsForFileIngestionTest());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
|
|
|
|
bool write_global_seqno = std::get<0>(GetParam());
|
|
|
|
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
|
|
|
do {
|
|
|
|
Options options = CurrentOptions();
|
Add missing range conflict check between file ingestion and RefitLevel() (#10988)
Summary:
**Context:**
File ingestion never checks whether the key range it acts on overlaps with an ongoing RefitLevel() (used in `CompactRange()` with `change_level=true`). That's because RefitLevel() doesn't register and make its key range known to file ingestion. Though it checks overlapping with other compactions by https://github.com/facebook/rocksdb/blob/7.8.fb/db/external_sst_file_ingestion_job.cc#L998.
RefitLevel() (used in `CompactRange()` with `change_level=true`) doesn't check whether the key range it acts on overlaps with an ongoing file ingestion. That's because file ingestion does not register and make its key range known to other compactions.
- Note that non-refitlevel-compaction (e.g, manual compaction w/o RefitLevel() or general compaction) also does not check key range overlap with ongoing file ingestion for the same reason.
- But it's fine. Credited to cbi42's discovery, `WaitForIngestFile` was called by background and foreground compactions. They were introduced in https://github.com/facebook/rocksdb/commit/0f88160f67d36ea30e3aca3a3cef924c3a009be6, https://github.com/facebook/rocksdb/commit/5c64fb67d2fc198f1a73ff3ae543749a6a41f513 and https://github.com/facebook/rocksdb/commit/87dfc1d23e0e16ff73e15f63c6fa0fb3b3fc8c8c.
- Regardless, this PR registers file ingestion like a compaction is a general approach that will also add range conflict check between file ingestion and non-refitlevel-compaction, though it has not been the issue motivated this PR.
Above are bugs resulting in two bad consequences:
- If file ingestion and RefitLevel() creates files in the same level, then range-overlapped files will be created at that level and caught as corruption by `force_consistency_checks=true`
- If file ingestion and RefitLevel() creates file in different levels, then with one further compaction on the ingested file, it can result in two same keys both with seqno 0 in two different levels. Then with iterator's [optimization](https://github.com/facebook/rocksdb/blame/c62f3221698fd273b673d4f7e54eabb8329a4369/db/db_iter.cc#L342-L343) that assumes no two same keys both with seqno 0, it will either break this assertion in debug build or, even worst, return value of this same key for the key after it, which is the wrong value to return, in release build.
Therefore we decide to introduce range conflict check for file ingestion and RefitLevel() inspired from the existing range conflict check among compactions.
**Summary:**
- Treat file ingestion job and RefitLevel() as `Compaction` of new compaction reasons: `CompactionReason::kExternalSstIngestion` and `CompactionReason::kRefitLevel` and register/unregister them. File ingestion is treated as compaction from L0 to different levels and RefitLevel() as compaction from source level to target level.
- Check for `RangeOverlapWithCompaction` with other ongoing compactions, `RegisterCompaction()` on this "compaction" before changing the LSM state in `VersionStorageInfo`, and `UnregisterCompaction()` after changing.
- Replace scattered fixes (https://github.com/facebook/rocksdb/commit/0f88160f67d36ea30e3aca3a3cef924c3a009be6, https://github.com/facebook/rocksdb/commit/5c64fb67d2fc198f1a73ff3ae543749a6a41f513 and https://github.com/facebook/rocksdb/commit/87dfc1d23e0e16ff73e15f63c6fa0fb3b3fc8c8c.) that prevents overlapping between file ingestion and non-refit-level compaction with this fix cuz those practices are easy to overlook.
- Misc: logic cleanup, see PR comments
Pull Request resolved: https://github.com/facebook/rocksdb/pull/10988
Test Plan:
- New unit test `DBCompactionTestWithOngoingFileIngestionParam*` that failed pre-fix and passed afterwards.
- Made compatible with existing tests, see PR comments
- make check
- [Ongoing] Stress test rehearsal with normal value and aggressive CI value https://github.com/facebook/rocksdb/pull/10761
Reviewed By: cbi42
Differential Revision: D41535685
Pulled By: hx235
fbshipit-source-id: 549833a577ba1496d20a870583d4caa737da1258
2 years ago
|
|
|
options.disable_auto_compactions = true;
|
|
|
|
options.merge_operator.reset(new TestPutOperator());
|
|
|
|
DestroyAndReopen(options);
|
|
|
|
std::map<std::string, std::string> true_data;
|
|
|
|
|
|
|
|
int file_id = 1;
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1, 2, 3, 4, 5, 6}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {10, 11, 12, 13}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1, 4, 6}, ValueType::kTypeMerge, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {11, 15, 19}, ValueType::kTypeDeletion, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {120, 130}, ValueType::kTypeMerge, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1, 130}, ValueType::kTypeDeletion, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {120}, {ValueType::kTypeValue}, {{120, 135}}, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 4);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {}, {}, {{110, 120}}, file_id++, write_global_seqno,
|
|
|
|
verify_checksums_before_ingest, &true_data));
|
|
|
|
// The range deletion ends on a key, but it doesn't actually delete
|
|
|
|
// this key because the largest key in the range is exclusive. Still,
|
|
|
|
// it counts as an overlap so a new seqno will be assigned.
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {}, {}, {{100, 109}}, file_id++, write_global_seqno,
|
|
|
|
verify_checksums_before_ingest, &true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
|
|
|
|
|
|
|
|
// Write some keys through normal write path
|
|
|
|
for (int i = 0; i < 50; i++) {
|
|
|
|
ASSERT_OK(Put(Key(i), "memtable"));
|
|
|
|
true_data[Key(i)] = "memtable";
|
|
|
|
}
|
|
|
|
SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {60, 61, 62}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {40, 41, 42}, ValueType::kTypeMerge, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {20, 30, 40}, ValueType::kTypeDeletion, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
|
|
|
|
|
|
|
|
const Snapshot* snapshot = db_->GetSnapshot();
|
|
|
|
|
|
|
|
// We will need a seqno for the file regardless if the file overwrite
|
|
|
|
// keys in the DB or not because we have a snapshot
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1000, 1002}, ValueType::kTypeMerge, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// A global seqno will be assigned anyway because of the snapshot
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {2000, 3002}, ValueType::kTypeMerge, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// A global seqno will be assigned anyway because of the snapshot
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1, 20, 40, 100, 150}, ValueType::kTypeMerge, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// A global seqno will be assigned anyway because of the snapshot
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
|
|
|
|
|
|
|
db_->ReleaseSnapshot(snapshot);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {5000, 5001}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
|
|
|
// No snapshot anymore, no need to assign a seqno
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
|
|
|
|
|
|
|
size_t kcnt = 0;
|
|
|
|
VerifyDBFromMap(true_data, &kcnt, false);
|
|
|
|
} while (ChangeOptionsForFileIngestionTest());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
|
|
|
bool write_global_seqno = std::get<0>(GetParam());
|
|
|
|
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
|
|
|
do {
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
options.merge_operator.reset(new TestPutOperator());
|
|
|
|
DestroyAndReopen(options);
|
|
|
|
std::map<std::string, std::string> true_data;
|
|
|
|
|
|
|
|
int file_id = 1;
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1, 2, 3, 4, 5, 6},
|
|
|
|
{ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue,
|
|
|
|
ValueType::kTypeMerge, ValueType::kTypeValue, ValueType::kTypeMerge},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {10, 11, 12, 13},
|
|
|
|
{ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue,
|
|
|
|
ValueType::kTypeMerge},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1, 4, 6},
|
|
|
|
{ValueType::kTypeDeletion, ValueType::kTypeValue,
|
|
|
|
ValueType::kTypeMerge},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {11, 15, 19},
|
|
|
|
{ValueType::kTypeDeletion, ValueType::kTypeMerge,
|
|
|
|
ValueType::kTypeValue},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {120, 130}, {ValueType::kTypeValue, ValueType::kTypeMerge},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1, 130}, {ValueType::kTypeMerge, ValueType::kTypeDeletion},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {150, 151, 152},
|
|
|
|
{ValueType::kTypeValue, ValueType::kTypeMerge,
|
|
|
|
ValueType::kTypeDeletion},
|
|
|
|
{{150, 160}, {180, 190}}, file_id++, write_global_seqno,
|
|
|
|
verify_checksums_before_ingest, &true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {150, 151, 152},
|
|
|
|
{ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue},
|
|
|
|
{{200, 250}}, file_id++, write_global_seqno,
|
|
|
|
verify_checksums_before_ingest, &true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 4);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {300, 301, 302},
|
|
|
|
{ValueType::kTypeValue, ValueType::kTypeMerge,
|
|
|
|
ValueType::kTypeDeletion},
|
|
|
|
{{1, 2}, {152, 154}}, file_id++, write_global_seqno,
|
|
|
|
verify_checksums_before_ingest, &true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
|
|
|
|
|
|
|
|
// Write some keys through normal write path
|
|
|
|
for (int i = 0; i < 50; i++) {
|
|
|
|
ASSERT_OK(Put(Key(i), "memtable"));
|
|
|
|
true_data[Key(i)] = "memtable";
|
|
|
|
}
|
|
|
|
SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {60, 61, 62},
|
|
|
|
{ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
// File doesn't overwrite any keys, no seqno needed
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {40, 41, 42},
|
|
|
|
{ValueType::kTypeValue, ValueType::kTypeDeletion,
|
|
|
|
ValueType::kTypeDeletion},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {20, 30, 40},
|
|
|
|
{ValueType::kTypeDeletion, ValueType::kTypeDeletion,
|
|
|
|
ValueType::kTypeDeletion},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
// File overwrites some keys, a seqno will be assigned
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
|
|
|
|
|
|
|
|
const Snapshot* snapshot = db_->GetSnapshot();
|
|
|
|
|
|
|
|
// We will need a seqno for the file regardless if the file overwrite
|
|
|
|
// keys in the DB or not because we have a snapshot
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1000, 1002}, {ValueType::kTypeValue, ValueType::kTypeMerge},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
// A global seqno will be assigned anyway because of the snapshot
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {2000, 3002}, {ValueType::kTypeValue, ValueType::kTypeMerge},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
// A global seqno will be assigned anyway because of the snapshot
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {1, 20, 40, 100, 150},
|
|
|
|
{ValueType::kTypeDeletion, ValueType::kTypeDeletion,
|
|
|
|
ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeMerge},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
// A global seqno will be assigned anyway because of the snapshot
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
|
|
|
|
|
|
|
db_->ReleaseSnapshot(snapshot);
|
|
|
|
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {5000, 5001}, {ValueType::kTypeValue, ValueType::kTypeMerge},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
// No snapshot anymore, no need to assign a seqno
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
|
|
|
|
|
|
|
size_t kcnt = 0;
|
|
|
|
VerifyDBFromMap(true_data, &kcnt, false);
|
|
|
|
} while (ChangeOptionsForFileIngestionTest());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, FadviseTrigger) {
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
const int kNumKeys = 10000;
|
|
|
|
|
|
|
|
size_t total_fadvised_bytes = 0;
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
|
|
|
"SstFileWriter::Rep::InvalidatePageCache", [&](void* arg) {
|
|
|
|
size_t fadvise_size = *(reinterpret_cast<size_t*>(arg));
|
|
|
|
total_fadvised_bytes += fadvise_size;
|
|
|
|
});
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
|
|
|
|
|
|
|
std::unique_ptr<SstFileWriter> sst_file_writer;
|
|
|
|
|
|
|
|
std::string sst_file_path = sst_files_dir_ + "file_fadvise_disable.sst";
|
|
|
|
sst_file_writer.reset(
|
|
|
|
new SstFileWriter(EnvOptions(), options, nullptr, false));
|
|
|
|
ASSERT_OK(sst_file_writer->Open(sst_file_path));
|
|
|
|
for (int i = 0; i < kNumKeys; i++) {
|
|
|
|
ASSERT_OK(sst_file_writer->Put(Key(i), Key(i)));
|
|
|
|
}
|
|
|
|
ASSERT_OK(sst_file_writer->Finish());
|
|
|
|
// fadvise disabled
|
|
|
|
ASSERT_EQ(total_fadvised_bytes, 0);
|
|
|
|
|
|
|
|
sst_file_path = sst_files_dir_ + "file_fadvise_enable.sst";
|
|
|
|
sst_file_writer.reset(
|
|
|
|
new SstFileWriter(EnvOptions(), options, nullptr, true));
|
|
|
|
ASSERT_OK(sst_file_writer->Open(sst_file_path));
|
|
|
|
for (int i = 0; i < kNumKeys; i++) {
|
|
|
|
ASSERT_OK(sst_file_writer->Put(Key(i), Key(i)));
|
|
|
|
}
|
|
|
|
ASSERT_OK(sst_file_writer->Finish());
|
|
|
|
// fadvise enabled
|
|
|
|
ASSERT_EQ(total_fadvised_bytes, sst_file_writer->FileSize());
|
|
|
|
ASSERT_GT(total_fadvised_bytes, 0);
|
|
|
|
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, SyncFailure) {
|
|
|
|
Options options;
|
|
|
|
options.create_if_missing = true;
|
|
|
|
options.env = fault_injection_test_env_.get();
|
|
|
|
|
|
|
|
std::vector<std::pair<std::string, std::string>> test_cases = {
|
|
|
|
{"ExternalSstFileIngestionJob::BeforeSyncIngestedFile",
|
|
|
|
"ExternalSstFileIngestionJob::AfterSyncIngestedFile"},
|
|
|
|
{"ExternalSstFileIngestionJob::BeforeSyncDir",
|
|
|
|
"ExternalSstFileIngestionJob::AfterSyncDir"},
|
|
|
|
{"ExternalSstFileIngestionJob::BeforeSyncGlobalSeqno",
|
|
|
|
"ExternalSstFileIngestionJob::AfterSyncGlobalSeqno"}};
|
|
|
|
|
|
|
|
for (size_t i = 0; i < test_cases.size(); i++) {
|
|
|
|
bool no_sync = false;
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(test_cases[i].first, [&](void*) {
|
|
|
|
fault_injection_test_env_->SetFilesystemActive(false);
|
|
|
|
});
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(test_cases[i].second, [&](void*) {
|
|
|
|
fault_injection_test_env_->SetFilesystemActive(true);
|
|
|
|
});
|
|
|
|
if (i == 0) {
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(
|
|
|
|
"ExternalSstFileIngestionJob::Prepare:Reopen", [&](void* s) {
|
|
|
|
Status* status = static_cast<Status*>(s);
|
|
|
|
if (status->IsNotSupported()) {
|
|
|
|
no_sync = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (i == 2) {
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(
|
|
|
|
"ExternalSstFileIngestionJob::NewRandomRWFile", [&](void* s) {
|
|
|
|
Status* status = static_cast<Status*>(s);
|
|
|
|
if (status->IsNotSupported()) {
|
|
|
|
no_sync = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
SyncPoint::GetInstance()->EnableProcessing();
|
|
|
|
|
|
|
|
DestroyAndReopen(options);
|
|
|
|
if (i == 2) {
|
|
|
|
ASSERT_OK(Put("foo", "v1"));
|
|
|
|
}
|
|
|
|
|
|
|
|
Options sst_file_writer_options;
|
|
|
|
sst_file_writer_options.env = fault_injection_test_env_.get();
|
|
|
|
std::unique_ptr<SstFileWriter> sst_file_writer(
|
|
|
|
new SstFileWriter(EnvOptions(), sst_file_writer_options));
|
|
|
|
std::string file_name =
|
|
|
|
sst_files_dir_ + "sync_failure_test_" + std::to_string(i) + ".sst";
|
|
|
|
ASSERT_OK(sst_file_writer->Open(file_name));
|
|
|
|
ASSERT_OK(sst_file_writer->Put("bar", "v2"));
|
|
|
|
ASSERT_OK(sst_file_writer->Finish());
|
|
|
|
|
|
|
|
IngestExternalFileOptions ingest_opt;
|
|
|
|
if (i == 0) {
|
|
|
|
ingest_opt.move_files = true;
|
|
|
|
}
|
|
|
|
const Snapshot* snapshot = db_->GetSnapshot();
|
|
|
|
if (i == 2) {
|
|
|
|
ingest_opt.write_global_seqno = true;
|
|
|
|
}
|
|
|
|
Status s = db_->IngestExternalFile({file_name}, ingest_opt);
|
|
|
|
if (no_sync) {
|
|
|
|
ASSERT_OK(s);
|
|
|
|
} else {
|
|
|
|
ASSERT_NOK(s);
|
|
|
|
}
|
|
|
|
db_->ReleaseSnapshot(snapshot);
|
|
|
|
|
|
|
|
SyncPoint::GetInstance()->DisableProcessing();
|
|
|
|
SyncPoint::GetInstance()->ClearAllCallBacks();
|
|
|
|
Destroy(options);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, ReopenNotSupported) {
|
|
|
|
Options options;
|
|
|
|
options.create_if_missing = true;
|
|
|
|
options.env = env_;
|
|
|
|
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(
|
|
|
|
"ExternalSstFileIngestionJob::Prepare:Reopen", [&](void* arg) {
|
|
|
|
Status* s = static_cast<Status*>(arg);
|
|
|
|
*s = Status::NotSupported();
|
|
|
|
});
|
|
|
|
SyncPoint::GetInstance()->EnableProcessing();
|
|
|
|
|
|
|
|
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_ + "reopen_not_supported_test_" + ".sst";
|
|
|
|
ASSERT_OK(sst_file_writer->Open(file_name));
|
|
|
|
ASSERT_OK(sst_file_writer->Put("bar", "v2"));
|
|
|
|
ASSERT_OK(sst_file_writer->Finish());
|
|
|
|
|
|
|
|
IngestExternalFileOptions ingest_opt;
|
|
|
|
ingest_opt.move_files = true;
|
|
|
|
const Snapshot* snapshot = db_->GetSnapshot();
|
|
|
|
ASSERT_OK(db_->IngestExternalFile({file_name}, ingest_opt));
|
|
|
|
db_->ReleaseSnapshot(snapshot);
|
|
|
|
|
|
|
|
SyncPoint::GetInstance()->DisableProcessing();
|
|
|
|
SyncPoint::GetInstance()->ClearAllCallBacks();
|
|
|
|
Destroy(options);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, VerifyChecksumReadahead) {
|
|
|
|
Options options;
|
|
|
|
options.create_if_missing = true;
|
Fix many tests to run with MEM_ENV and ENCRYPTED_ENV; Introduce a MemoryFileSystem class (#7566)
Summary:
This PR does a few things:
1. The MockFileSystem class was split out from the MockEnv. This change would theoretically allow a MockFileSystem to be used by other Environments as well (if we created a means of constructing one). The MockFileSystem implements a FileSystem in its entirety and does not rely on any Wrapper implementation.
2. Make the RocksDB test suite work when MOCK_ENV=1 and ENCRYPTED_ENV=1 are set. To accomplish this, a few things were needed:
- The tests that tried to use the "wrong" environment (Env::Default() instead of env_) were updated
- The MockFileSystem was changed to support the features it was missing or mishandled (such as recursively deleting files in a directory or supporting renaming of a directory).
3. Updated the test framework to have a ROCKSDB_GTEST_SKIP macro. This can be used to flag tests that are skipped. Currently, this defaults to doing nothing (marks the test as SUCCESS) but will mark the tests as SKIPPED when RocksDB is upgraded to a version of gtest that supports this (gtest-1.10).
I have run a full "make check" with MEM_ENV, ENCRYPTED_ENV, both, and neither under both MacOS and RedHat. A few tests were disabled/skipped for the MEM/ENCRYPTED cases. The error_handler_fs_test fails/hangs for MEM_ENV (presumably a timing problem) and I will introduce another PR/issue to track that problem. (I will also push a change to disable those tests soon). There is one more test in DBTest2 that also fails which I need to investigate or skip before this PR is merged.
Theoretically, this PR should also allow the test suite to run against an Env loaded from the registry, though I do not have one to try it with currently.
Finally, once this is accepted, it would be nice if there was a CircleCI job to run these tests on a checkin so this effort does not become stale. I do not know how to do that, so if someone could write that job, it would be appreciated :)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/7566
Reviewed By: zhichao-cao
Differential Revision: D24408980
Pulled By: jay-zhuang
fbshipit-source-id: 911b1554a4d0da06fd51feca0c090a4abdcb4a5f
4 years ago
|
|
|
SpecialEnv senv(env_);
|
|
|
|
options.env = &senv;
|
|
|
|
DestroyAndReopen(options);
|
|
|
|
|
|
|
|
Options sst_file_writer_options;
|
Fix many tests to run with MEM_ENV and ENCRYPTED_ENV; Introduce a MemoryFileSystem class (#7566)
Summary:
This PR does a few things:
1. The MockFileSystem class was split out from the MockEnv. This change would theoretically allow a MockFileSystem to be used by other Environments as well (if we created a means of constructing one). The MockFileSystem implements a FileSystem in its entirety and does not rely on any Wrapper implementation.
2. Make the RocksDB test suite work when MOCK_ENV=1 and ENCRYPTED_ENV=1 are set. To accomplish this, a few things were needed:
- The tests that tried to use the "wrong" environment (Env::Default() instead of env_) were updated
- The MockFileSystem was changed to support the features it was missing or mishandled (such as recursively deleting files in a directory or supporting renaming of a directory).
3. Updated the test framework to have a ROCKSDB_GTEST_SKIP macro. This can be used to flag tests that are skipped. Currently, this defaults to doing nothing (marks the test as SUCCESS) but will mark the tests as SKIPPED when RocksDB is upgraded to a version of gtest that supports this (gtest-1.10).
I have run a full "make check" with MEM_ENV, ENCRYPTED_ENV, both, and neither under both MacOS and RedHat. A few tests were disabled/skipped for the MEM/ENCRYPTED cases. The error_handler_fs_test fails/hangs for MEM_ENV (presumably a timing problem) and I will introduce another PR/issue to track that problem. (I will also push a change to disable those tests soon). There is one more test in DBTest2 that also fails which I need to investigate or skip before this PR is merged.
Theoretically, this PR should also allow the test suite to run against an Env loaded from the registry, though I do not have one to try it with currently.
Finally, once this is accepted, it would be nice if there was a CircleCI job to run these tests on a checkin so this effort does not become stale. I do not know how to do that, so if someone could write that job, it would be appreciated :)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/7566
Reviewed By: zhichao-cao
Differential Revision: D24408980
Pulled By: jay-zhuang
fbshipit-source-id: 911b1554a4d0da06fd51feca0c090a4abdcb4a5f
4 years ago
|
|
|
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 = rnd.RandomString(4000);
|
|
|
|
for (int i = 0; i < 5000; i++) {
|
|
|
|
ASSERT_OK(sst_file_writer->Put(DBTestBase::Key(i), value));
|
|
|
|
}
|
|
|
|
ASSERT_OK(sst_file_writer->Finish());
|
|
|
|
|
|
|
|
// Ingest it once without verifying checksums to see the baseline
|
|
|
|
// preads.
|
|
|
|
IngestExternalFileOptions ingest_opt;
|
|
|
|
ingest_opt.move_files = false;
|
|
|
|
senv.count_random_reads_ = true;
|
|
|
|
senv.random_read_bytes_counter_ = 0;
|
|
|
|
ASSERT_OK(db_->IngestExternalFile({file_name}, ingest_opt));
|
|
|
|
|
|
|
|
auto base_num_reads = senv.random_read_counter_.Read();
|
|
|
|
// Make sure the counter is enabled.
|
|
|
|
ASSERT_GT(base_num_reads, 0);
|
|
|
|
|
|
|
|
// Ingest again and observe the reads made for for readahead.
|
|
|
|
ingest_opt.move_files = false;
|
|
|
|
ingest_opt.verify_checksums_before_ingest = true;
|
|
|
|
ingest_opt.verify_checksums_readahead_size = size_t{2 * 1024 * 1024};
|
|
|
|
|
|
|
|
senv.count_random_reads_ = true;
|
|
|
|
senv.random_read_bytes_counter_ = 0;
|
|
|
|
ASSERT_OK(db_->IngestExternalFile({file_name}, ingest_opt));
|
|
|
|
|
|
|
|
// Make sure the counter is enabled.
|
|
|
|
ASSERT_GT(senv.random_read_counter_.Read() - base_num_reads, 0);
|
|
|
|
|
|
|
|
// The SST file is about 20MB. Readahead size is 2MB.
|
|
|
|
// Give a conservative 15 reads for metadata blocks, the number
|
|
|
|
// of random reads should be within 20 MB / 2MB + 15 = 25.
|
|
|
|
ASSERT_LE(senv.random_read_counter_.Read() - base_num_reads, 40);
|
|
|
|
|
|
|
|
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();
|
|
|
|
options.disable_auto_compactions = true;
|
|
|
|
options.num_levels = kNumLevels;
|
|
|
|
Reopen(options);
|
|
|
|
|
|
|
|
std::map<std::string, std::string> true_data;
|
|
|
|
int file_id = 1;
|
|
|
|
// prevent range deletions from being dropped due to becoming obsolete.
|
|
|
|
const Snapshot* snapshot = db_->GetSnapshot();
|
|
|
|
|
|
|
|
// range del [0, 50) in L6 file, [50, 100) in L0 file, [100, 150) in memtable
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
if (i != 0) {
|
|
|
|
db_->Flush(FlushOptions());
|
|
|
|
if (i == 1) {
|
|
|
|
MoveFilesToLevel(kNumLevels - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
|
|
|
Key(50 * i), Key(50 * (i + 1))));
|
|
|
|
}
|
|
|
|
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
|
|
|
ASSERT_EQ(0, NumTableFilesAtLevel(kNumLevels - 2));
|
|
|
|
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 1));
|
|
|
|
|
|
|
|
bool write_global_seqno = std::get<0>(GetParam());
|
|
|
|
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
|
|
|
// overlaps with L0 file but not memtable, so flush is skipped and file is
|
|
|
|
// ingested into L0
|
|
|
|
SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {60, 90}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
|
|
|
{{65, 70}, {70, 85}}, file_id++, write_global_seqno,
|
|
|
|
verify_checksums_before_ingest, &true_data));
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
|
|
|
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
|
|
|
ASSERT_EQ(0, NumTableFilesAtLevel(kNumLevels - 2));
|
|
|
|
ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
|
|
|
|
|
|
|
|
// overlaps with L6 file but not memtable or L0 file, so flush is skipped and
|
|
|
|
// file is ingested into L5
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {10, 40}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
|
|
|
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
|
|
|
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
|
|
|
ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
|
|
|
|
|
|
|
|
// overlaps with L5 file but not memtable or L0 file, so flush is skipped and
|
|
|
|
// file is ingested into L4
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {}, {}, {{5, 15}}, file_id++, write_global_seqno,
|
|
|
|
verify_checksums_before_ingest, &true_data));
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
|
|
|
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
|
|
|
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
|
|
|
ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 2));
|
|
|
|
ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
|
|
|
|
|
|
|
|
// ingested file overlaps with memtable, so flush is triggered before the file
|
|
|
|
// is ingested such that the ingested data is considered newest. So L0 file
|
|
|
|
// count increases by two.
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {100, 140}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
|
|
|
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
|
|
|
&true_data));
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
|
|
|
ASSERT_EQ(4, NumTableFilesAtLevel(0));
|
|
|
|
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
|
|
|
ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
|
|
|
|
|
|
|
|
// snapshot unneeded now that all range deletions are persisted
|
|
|
|
db_->ReleaseSnapshot(snapshot);
|
|
|
|
|
|
|
|
// overlaps with nothing, so places at bottom level and skips incrementing
|
|
|
|
// seqnum.
|
|
|
|
ASSERT_OK(GenerateAndAddExternalFile(
|
|
|
|
options, {151, 175}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
|
|
|
{{160, 200}}, file_id++, write_global_seqno,
|
|
|
|
verify_checksums_before_ingest, &true_data));
|
|
|
|
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
|
|
|
ASSERT_EQ(4, NumTableFilesAtLevel(0));
|
|
|
|
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
|
|
|
ASSERT_EQ(2, NumTableFilesAtLevel(options.num_levels - 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, AdjacentRangeDeletionTombstones) {
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
SstFileWriter sst_file_writer(EnvOptions(), options);
|
|
|
|
|
|
|
|
// file8.sst (delete 300 => 400)
|
|
|
|
std::string file8 = sst_files_dir_ + "file8.sst";
|
|
|
|
ASSERT_OK(sst_file_writer.Open(file8));
|
|
|
|
ASSERT_OK(sst_file_writer.DeleteRange(Key(300), Key(400)));
|
|
|
|
ExternalSstFileInfo file8_info;
|
|
|
|
Status s = sst_file_writer.Finish(&file8_info);
|
|
|
|
ASSERT_OK(s) << s.ToString();
|
|
|
|
ASSERT_EQ(file8_info.file_path, file8);
|
|
|
|
ASSERT_EQ(file8_info.num_entries, 0);
|
|
|
|
ASSERT_EQ(file8_info.smallest_key, "");
|
|
|
|
ASSERT_EQ(file8_info.largest_key, "");
|
|
|
|
ASSERT_EQ(file8_info.num_range_del_entries, 1);
|
|
|
|
ASSERT_EQ(file8_info.smallest_range_del_key, Key(300));
|
|
|
|
ASSERT_EQ(file8_info.largest_range_del_key, Key(400));
|
|
|
|
|
|
|
|
// file9.sst (delete 400 => 500)
|
|
|
|
std::string file9 = sst_files_dir_ + "file9.sst";
|
|
|
|
ASSERT_OK(sst_file_writer.Open(file9));
|
|
|
|
ASSERT_OK(sst_file_writer.DeleteRange(Key(400), Key(500)));
|
|
|
|
ExternalSstFileInfo file9_info;
|
|
|
|
s = sst_file_writer.Finish(&file9_info);
|
|
|
|
ASSERT_OK(s) << s.ToString();
|
|
|
|
ASSERT_EQ(file9_info.file_path, file9);
|
|
|
|
ASSERT_EQ(file9_info.num_entries, 0);
|
|
|
|
ASSERT_EQ(file9_info.smallest_key, "");
|
|
|
|
ASSERT_EQ(file9_info.largest_key, "");
|
|
|
|
ASSERT_EQ(file9_info.num_range_del_entries, 1);
|
|
|
|
ASSERT_EQ(file9_info.smallest_range_del_key, Key(400));
|
|
|
|
ASSERT_EQ(file9_info.largest_range_del_key, Key(500));
|
|
|
|
|
|
|
|
// Range deletion tombstones are exclusive on their end key, so these SSTs
|
|
|
|
// should not be considered as overlapping.
|
|
|
|
s = DeprecatedAddFile({file8, file9});
|
|
|
|
ASSERT_OK(s) << s.ToString();
|
|
|
|
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
|
|
|
DestroyAndRecreateExternalSSTFilesDir();
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(ExternalSSTFileBasicTest, IngestFileWithBadBlockChecksum) {
|
|
|
|
bool change_checksum_called = false;
|
|
|
|
const auto& change_checksum = [&](void* arg) {
|
|
|
|
if (!change_checksum_called) {
|
|
|
|
char* buf = reinterpret_cast<char*>(arg);
|
|
|
|
assert(nullptr != buf);
|
|
|
|
buf[0] ^= 0x1;
|
|
|
|
change_checksum_called = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
SyncPoint::GetInstance()->DisableProcessing();
|
|
|
|
SyncPoint::GetInstance()->ClearAllCallBacks();
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(
|
Refactor to avoid confusing "raw block" (#10408)
Summary:
We have a lot of confusing code because of mixed, sometimes
completely opposite uses of of the term "raw block" or "raw contents",
sometimes within the same source file. For example, in `BlockBasedTableBuilder`,
`raw_block_contents` and `raw_size` generally referred to uncompressed block
contents and size, while `WriteRawBlock` referred to writing a block that
is already compressed if it is going to be. Meanwhile, in
`BlockBasedTable`, `raw_block_contents` either referred to a (maybe
compressed) block with trailer, or a maybe compressed block maybe
without trailer. (Note: left as follow-up work to use C++ typing to
better sort out the various kinds of BlockContents.)
This change primarily tries to apply some consistent terminology around
the kinds of block representations, avoiding the unclear "raw". (Any
meaning of "raw" assumes some bias toward the storage layer or toward
the logical data layer.) Preferred terminology:
* **Serialized block** - bytes that go into storage. For block-based table
(usually the case) this includes the block trailer. WART: block `size` may or
may not include the trailer; need to be clear about whether it does or not.
* **Maybe compressed block** - like a serialized block, but without the
trailer (or no promise of including a trailer). Must be accompanied by a
CompressionType.
* **Uncompressed block** - "payload" bytes that are either stored with no
compression, used as input to compression function, or result of
decompression function.
* **Parsed block** - an in-memory form of a block in block cache, as it is
used by the table reader. Different C++ types are used depending on the
block type (see block_like_traits.h).
Other refactorings:
* Misc corrections/improvements of internal API comments
* Remove a few misleading / unhelpful / redundant comments.
* Use move semantics in some places to simplify contracts
* Use better parameter names to indicate which parameters are used for
outputs
* Remove some extraneous `extern`
* Various clean-ups to `CacheDumperImpl` (mostly unnecessary code)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/10408
Test Plan: existing tests
Reviewed By: akankshamahajan15
Differential Revision: D38172617
Pulled By: pdillinger
fbshipit-source-id: ccb99299f324ac5ca46996d34c5089621a4f260c
2 years ago
|
|
|
"BlockBasedTableBuilder::WriteMaybeCompressedBlock:TamperWithChecksum",
|
|
|
|
change_checksum);
|
|
|
|
SyncPoint::GetInstance()->EnableProcessing();
|
|
|
|
int file_id = 0;
|
|
|
|
bool write_global_seqno = std::get<0>(GetParam());
|
|
|
|
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
|
|
|
do {
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
DestroyAndReopen(options);
|
|
|
|
std::map<std::string, std::string> true_data;
|
|
|
|
Status s = GenerateAndAddExternalFile(
|
|
|
|
options, {1, 2, 3, 4, 5, 6}, ValueType::kTypeValue, file_id++,
|
|
|
|
write_global_seqno, verify_checksums_before_ingest, &true_data);
|
|
|
|
if (verify_checksums_before_ingest) {
|
|
|
|
ASSERT_NOK(s);
|
|
|
|
} else {
|
|
|
|
ASSERT_OK(s);
|
|
|
|
}
|
|
|
|
change_checksum_called = false;
|
|
|
|
} while (ChangeOptionsForFileIngestionTest());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(ExternalSSTFileBasicTest, IngestFileWithFirstByteTampered) {
|
|
|
|
if (!random_rwfile_supported_) {
|
|
|
|
ROCKSDB_GTEST_SKIP("Test requires NewRandomRWFile support");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SyncPoint::GetInstance()->DisableProcessing();
|
|
|
|
int file_id = 0;
|
|
|
|
EnvOptions env_options;
|
|
|
|
do {
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
std::string file_path = sst_files_dir_ + std::to_string(file_id++);
|
|
|
|
SstFileWriter sst_file_writer(env_options, options);
|
|
|
|
Status s = sst_file_writer.Open(file_path);
|
|
|
|
ASSERT_OK(s);
|
|
|
|
for (int i = 0; i != 100; ++i) {
|
|
|
|
std::string key = Key(i);
|
|
|
|
std::string value = Key(i) + std::to_string(0);
|
|
|
|
ASSERT_OK(sst_file_writer.Put(key, value));
|
|
|
|
}
|
|
|
|
ASSERT_OK(sst_file_writer.Finish());
|
|
|
|
{
|
|
|
|
// Get file size
|
|
|
|
uint64_t file_size = 0;
|
|
|
|
ASSERT_OK(env_->GetFileSize(file_path, &file_size));
|
|
|
|
ASSERT_GT(file_size, 8);
|
|
|
|
std::unique_ptr<RandomRWFile> rwfile;
|
|
|
|
ASSERT_OK(env_->NewRandomRWFile(file_path, &rwfile, EnvOptions()));
|
|
|
|
// Manually corrupt the file
|
|
|
|
// We deterministically corrupt the first byte because we currently
|
|
|
|
// cannot choose a random offset. The reason for this limitation is that
|
|
|
|
// we do not checksum property block at present.
|
|
|
|
const uint64_t offset = 0;
|
|
|
|
char scratch[8] = {0};
|
|
|
|
Slice buf;
|
|
|
|
ASSERT_OK(rwfile->Read(offset, sizeof(scratch), &buf, scratch));
|
|
|
|
scratch[0] ^= 0xff; // flip one bit
|
|
|
|
ASSERT_OK(rwfile->Write(offset, buf));
|
|
|
|
}
|
|
|
|
// Ingest file.
|
|
|
|
IngestExternalFileOptions ifo;
|
|
|
|
ifo.write_global_seqno = std::get<0>(GetParam());
|
|
|
|
ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
|
|
|
|
s = db_->IngestExternalFile({file_path}, ifo);
|
|
|
|
if (ifo.verify_checksums_before_ingest) {
|
|
|
|
ASSERT_NOK(s);
|
|
|
|
} else {
|
|
|
|
ASSERT_OK(s);
|
|
|
|
}
|
|
|
|
} while (ChangeOptionsForFileIngestionTest());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(ExternalSSTFileBasicTest, IngestExternalFileWithCorruptedPropsBlock) {
|
|
|
|
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
|
|
|
if (!verify_checksums_before_ingest) {
|
|
|
|
ROCKSDB_GTEST_BYPASS("Bypassing test when !verify_checksums_before_ingest");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!random_rwfile_supported_) {
|
|
|
|
ROCKSDB_GTEST_SKIP("Test requires NewRandomRWFile support");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
uint64_t props_block_offset = 0;
|
|
|
|
size_t props_block_size = 0;
|
|
|
|
const auto& get_props_block_offset = [&](void* arg) {
|
|
|
|
props_block_offset = *reinterpret_cast<uint64_t*>(arg);
|
|
|
|
};
|
|
|
|
const auto& get_props_block_size = [&](void* arg) {
|
|
|
|
props_block_size = *reinterpret_cast<uint64_t*>(arg);
|
|
|
|
};
|
|
|
|
SyncPoint::GetInstance()->DisableProcessing();
|
|
|
|
SyncPoint::GetInstance()->ClearAllCallBacks();
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(
|
|
|
|
"BlockBasedTableBuilder::WritePropertiesBlock:GetPropsBlockOffset",
|
|
|
|
get_props_block_offset);
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(
|
|
|
|
"BlockBasedTableBuilder::WritePropertiesBlock:GetPropsBlockSize",
|
|
|
|
get_props_block_size);
|
|
|
|
SyncPoint::GetInstance()->EnableProcessing();
|
|
|
|
int file_id = 0;
|
|
|
|
Random64 rand(time(nullptr));
|
|
|
|
do {
|
|
|
|
std::string file_path = sst_files_dir_ + std::to_string(file_id++);
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
SstFileWriter sst_file_writer(EnvOptions(), options);
|
|
|
|
Status s = sst_file_writer.Open(file_path);
|
|
|
|
ASSERT_OK(s);
|
|
|
|
for (int i = 0; i != 100; ++i) {
|
|
|
|
std::string key = Key(i);
|
|
|
|
std::string value = Key(i) + std::to_string(0);
|
|
|
|
ASSERT_OK(sst_file_writer.Put(key, value));
|
|
|
|
}
|
|
|
|
ASSERT_OK(sst_file_writer.Finish());
|
|
|
|
|
|
|
|
{
|
|
|
|
std::unique_ptr<RandomRWFile> rwfile;
|
|
|
|
ASSERT_OK(env_->NewRandomRWFile(file_path, &rwfile, EnvOptions()));
|
|
|
|
// Manually corrupt the file
|
|
|
|
ASSERT_GT(props_block_size, 8);
|
|
|
|
uint64_t offset =
|
|
|
|
props_block_offset + rand.Next() % (props_block_size - 8);
|
|
|
|
char scratch[8] = {0};
|
|
|
|
Slice buf;
|
|
|
|
ASSERT_OK(rwfile->Read(offset, sizeof(scratch), &buf, scratch));
|
|
|
|
scratch[0] ^= 0xff; // flip one bit
|
|
|
|
ASSERT_OK(rwfile->Write(offset, buf));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ingest file.
|
|
|
|
IngestExternalFileOptions ifo;
|
|
|
|
ifo.write_global_seqno = std::get<0>(GetParam());
|
|
|
|
ifo.verify_checksums_before_ingest = true;
|
|
|
|
s = db_->IngestExternalFile({file_path}, ifo);
|
|
|
|
ASSERT_NOK(s);
|
|
|
|
} while (ChangeOptionsForFileIngestionTest());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, OverlappingFiles) {
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
|
|
|
|
std::vector<std::string> files;
|
|
|
|
{
|
|
|
|
SstFileWriter sst_file_writer(EnvOptions(), options);
|
|
|
|
std::string file1 = sst_files_dir_ + "file1.sst";
|
|
|
|
ASSERT_OK(sst_file_writer.Open(file1));
|
|
|
|
ASSERT_OK(sst_file_writer.Put("a", "z"));
|
|
|
|
ASSERT_OK(sst_file_writer.Put("i", "m"));
|
|
|
|
ExternalSstFileInfo file1_info;
|
|
|
|
ASSERT_OK(sst_file_writer.Finish(&file1_info));
|
|
|
|
files.push_back(std::move(file1));
|
|
|
|
}
|
|
|
|
{
|
|
|
|
SstFileWriter sst_file_writer(EnvOptions(), options);
|
|
|
|
std::string file2 = sst_files_dir_ + "file2.sst";
|
|
|
|
ASSERT_OK(sst_file_writer.Open(file2));
|
|
|
|
ASSERT_OK(sst_file_writer.Put("i", "k"));
|
|
|
|
ExternalSstFileInfo file2_info;
|
|
|
|
ASSERT_OK(sst_file_writer.Finish(&file2_info));
|
|
|
|
files.push_back(std::move(file2));
|
|
|
|
}
|
|
|
|
|
|
|
|
IngestExternalFileOptions ifo;
|
|
|
|
ASSERT_OK(db_->IngestExternalFile(files, ifo));
|
|
|
|
ASSERT_EQ(Get("a"), "z");
|
|
|
|
ASSERT_EQ(Get("i"), "k");
|
|
|
|
|
|
|
|
int total_keys = 0;
|
|
|
|
Iterator* iter = db_->NewIterator(ReadOptions());
|
|
|
|
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
|
|
|
ASSERT_OK(iter->status());
|
|
|
|
total_keys++;
|
|
|
|
}
|
|
|
|
delete iter;
|
|
|
|
ASSERT_EQ(total_keys, 2);
|
|
|
|
|
|
|
|
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, IngestFileAfterDBPut) {
|
|
|
|
// Repro https://github.com/facebook/rocksdb/issues/6245.
|
|
|
|
// Flush three files to L0. Ingest one more file to trigger L0->L1 compaction
|
|
|
|
// via trivial move. The bug happened when L1 files were incorrectly sorted
|
|
|
|
// resulting in an old value for "k" returned by `Get()`.
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
|
|
|
|
ASSERT_OK(Put("k", "a"));
|
|
|
|
Flush();
|
|
|
|
ASSERT_OK(Put("k", "a"));
|
|
|
|
Flush();
|
|
|
|
ASSERT_OK(Put("k", "a"));
|
|
|
|
Flush();
|
|
|
|
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);
|
|
|
|
|
|
|
|
std::string file1 = sst_files_dir_ + "file1.sst";
|
|
|
|
ASSERT_OK(sst_file_writer.Open(file1));
|
|
|
|
ASSERT_OK(sst_file_writer.Put("k", "b"));
|
|
|
|
|
|
|
|
ExternalSstFileInfo file1_info;
|
|
|
|
Status s = sst_file_writer.Finish(&file1_info);
|
|
|
|
ASSERT_OK(s) << s.ToString();
|
|
|
|
|
|
|
|
// Current file size should be non-zero after success write.
|
|
|
|
ASSERT_GT(sst_file_writer.FileSize(), 0);
|
|
|
|
|
|
|
|
IngestExternalFileOptions ifo;
|
|
|
|
s = db_->IngestExternalFile({file1}, ifo);
|
|
|
|
ASSERT_OK(s);
|
|
|
|
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
|
|
|
|
|
|
|
ASSERT_EQ(Get("k"), "b");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, IngestWithTemperature) {
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
const ImmutableCFOptions ioptions(options);
|
|
|
|
options.bottommost_temperature = Temperature::kWarm;
|
|
|
|
SstFileWriter sst_file_writer(EnvOptions(), options);
|
|
|
|
options.level0_file_num_compaction_trigger = 2;
|
|
|
|
Reopen(options);
|
|
|
|
|
|
|
|
auto size = GetSstSizeHelper(Temperature::kUnknown);
|
|
|
|
ASSERT_EQ(size, 0);
|
|
|
|
size = GetSstSizeHelper(Temperature::kWarm);
|
|
|
|
ASSERT_EQ(size, 0);
|
|
|
|
size = GetSstSizeHelper(Temperature::kHot);
|
|
|
|
ASSERT_EQ(size, 0);
|
|
|
|
|
|
|
|
// create file01.sst (1000 => 1099) and ingest it
|
|
|
|
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_OK(s);
|
|
|
|
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::vector<std::string> files;
|
|
|
|
std::vector<std::string> files_checksums;
|
|
|
|
std::vector<std::string> files_checksum_func_names;
|
|
|
|
Temperature file_temperature = Temperature::kWarm;
|
|
|
|
|
|
|
|
files.push_back(file1);
|
|
|
|
IngestExternalFileOptions in_opts;
|
|
|
|
in_opts.move_files = false;
|
|
|
|
in_opts.snapshot_consistency = true;
|
|
|
|
in_opts.allow_global_seqno = false;
|
|
|
|
in_opts.allow_blocking_flush = false;
|
|
|
|
in_opts.write_global_seqno = true;
|
|
|
|
in_opts.verify_file_checksum = false;
|
|
|
|
IngestExternalFileArg arg;
|
|
|
|
arg.column_family = db_->DefaultColumnFamily();
|
|
|
|
arg.external_files = files;
|
|
|
|
arg.options = in_opts;
|
|
|
|
arg.files_checksums = files_checksums;
|
|
|
|
arg.files_checksum_func_names = files_checksum_func_names;
|
|
|
|
arg.file_temperature = file_temperature;
|
|
|
|
s = db_->IngestExternalFiles({arg});
|
|
|
|
ASSERT_OK(s);
|
|
|
|
|
|
|
|
// check the temperature of the file being ingested
|
|
|
|
ColumnFamilyMetaData metadata;
|
|
|
|
db_->GetColumnFamilyMetaData(&metadata);
|
|
|
|
ASSERT_EQ(1, metadata.file_count);
|
|
|
|
ASSERT_EQ(Temperature::kWarm, metadata.levels[6].files[0].temperature);
|
|
|
|
size = GetSstSizeHelper(Temperature::kUnknown);
|
|
|
|
ASSERT_EQ(size, 0);
|
|
|
|
size = GetSstSizeHelper(Temperature::kWarm);
|
|
|
|
ASSERT_GT(size, 1);
|
|
|
|
|
|
|
|
// non-bottommost file still has unknown temperature
|
|
|
|
ASSERT_OK(Put("foo", "bar"));
|
|
|
|
ASSERT_OK(Put("bar", "bar"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
db_->GetColumnFamilyMetaData(&metadata);
|
|
|
|
ASSERT_EQ(2, metadata.file_count);
|
|
|
|
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[0].temperature);
|
|
|
|
size = GetSstSizeHelper(Temperature::kUnknown);
|
|
|
|
ASSERT_GT(size, 0);
|
|
|
|
size = GetSstSizeHelper(Temperature::kWarm);
|
|
|
|
ASSERT_GT(size, 0);
|
|
|
|
|
|
|
|
// reopen and check the information is persisted
|
|
|
|
Reopen(options);
|
|
|
|
db_->GetColumnFamilyMetaData(&metadata);
|
|
|
|
ASSERT_EQ(2, metadata.file_count);
|
|
|
|
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[0].temperature);
|
|
|
|
ASSERT_EQ(Temperature::kWarm, metadata.levels[6].files[0].temperature);
|
|
|
|
size = GetSstSizeHelper(Temperature::kUnknown);
|
|
|
|
ASSERT_GT(size, 0);
|
|
|
|
size = GetSstSizeHelper(Temperature::kWarm);
|
|
|
|
ASSERT_GT(size, 0);
|
|
|
|
|
|
|
|
// check other non-exist temperatures
|
|
|
|
size = GetSstSizeHelper(Temperature::kHot);
|
|
|
|
ASSERT_EQ(size, 0);
|
|
|
|
size = GetSstSizeHelper(Temperature::kCold);
|
|
|
|
ASSERT_EQ(size, 0);
|
|
|
|
std::string prop;
|
|
|
|
ASSERT_TRUE(dbfull()->GetProperty(
|
|
|
|
DB::Properties::kLiveSstFilesSizeAtTemperature + std::to_string(22),
|
|
|
|
&prop));
|
|
|
|
ASSERT_EQ(std::atoi(prop.c_str()), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, FailIfNotBottommostLevel) {
|
|
|
|
Options options = GetDefaultOptions();
|
|
|
|
|
|
|
|
std::string file_path = sst_files_dir_ + std::to_string(1);
|
|
|
|
SstFileWriter sfw(EnvOptions(), options);
|
|
|
|
|
|
|
|
ASSERT_OK(sfw.Open(file_path));
|
|
|
|
ASSERT_OK(sfw.Put("b", "dontcare"));
|
|
|
|
ASSERT_OK(sfw.Finish());
|
|
|
|
|
|
|
|
// Test universal compaction + ingest with snapshot consistency
|
|
|
|
options.create_if_missing = true;
|
|
|
|
options.compaction_style = CompactionStyle::kCompactionStyleUniversal;
|
|
|
|
DestroyAndReopen(options);
|
|
|
|
{
|
|
|
|
const Snapshot* snapshot = db_->GetSnapshot();
|
|
|
|
ManagedSnapshot snapshot_guard(db_, snapshot);
|
|
|
|
IngestExternalFileOptions ifo;
|
|
|
|
ifo.fail_if_not_bottommost_level = true;
|
|
|
|
ifo.snapshot_consistency = true;
|
|
|
|
const Status s = db_->IngestExternalFile({file_path}, ifo);
|
|
|
|
ASSERT_TRUE(s.IsTryAgain());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test level compaction
|
|
|
|
options.compaction_style = CompactionStyle::kCompactionStyleLevel;
|
|
|
|
options.num_levels = 2;
|
|
|
|
DestroyAndReopen(options);
|
|
|
|
ASSERT_OK(db_->Put(WriteOptions(), "a", "dontcare"));
|
|
|
|
ASSERT_OK(db_->Put(WriteOptions(), "c", "dontcare"));
|
|
|
|
ASSERT_OK(db_->Flush(FlushOptions()));
|
|
|
|
|
|
|
|
ASSERT_OK(db_->Put(WriteOptions(), "b", "dontcare"));
|
|
|
|
ASSERT_OK(db_->Put(WriteOptions(), "d", "dontcare"));
|
|
|
|
ASSERT_OK(db_->Flush(FlushOptions()));
|
|
|
|
|
|
|
|
{
|
|
|
|
CompactRangeOptions cro;
|
|
|
|
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
|
|
|
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
|
|
|
|
|
|
|
IngestExternalFileOptions ifo;
|
|
|
|
ifo.fail_if_not_bottommost_level = true;
|
|
|
|
const Status s = db_->IngestExternalFile({file_path}, ifo);
|
|
|
|
ASSERT_TRUE(s.IsTryAgain());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, VerifyChecksum) {
|
|
|
|
const std::string kPutVal = "put_val";
|
|
|
|
const std::string kIngestedVal = "ingested_val";
|
|
|
|
|
|
|
|
ASSERT_OK(Put("k", kPutVal, WriteOptions()));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
|
|
|
|
std::string external_file = sst_files_dir_ + "/file_to_ingest.sst";
|
|
|
|
{
|
|
|
|
SstFileWriter sst_file_writer{EnvOptions(), CurrentOptions()};
|
|
|
|
|
|
|
|
ASSERT_OK(sst_file_writer.Open(external_file));
|
|
|
|
ASSERT_OK(sst_file_writer.Put("k", kIngestedVal));
|
|
|
|
ASSERT_OK(sst_file_writer.Finish());
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT_OK(db_->IngestExternalFile(db_->DefaultColumnFamily(), {external_file},
|
|
|
|
IngestExternalFileOptions()));
|
|
|
|
|
|
|
|
ASSERT_OK(db_->VerifyChecksum());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, VerifySstUniqueId) {
|
|
|
|
const std::string kPutVal = "put_val";
|
|
|
|
const std::string kIngestedVal = "ingested_val";
|
|
|
|
|
|
|
|
ASSERT_OK(Put("k", kPutVal, WriteOptions()));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
|
|
|
|
std::string external_file = sst_files_dir_ + "/file_to_ingest.sst";
|
|
|
|
{
|
|
|
|
SstFileWriter sst_file_writer{EnvOptions(), CurrentOptions()};
|
|
|
|
|
|
|
|
ASSERT_OK(sst_file_writer.Open(external_file));
|
|
|
|
ASSERT_OK(sst_file_writer.Put("k", kIngestedVal));
|
|
|
|
ASSERT_OK(sst_file_writer.Finish());
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT_OK(db_->IngestExternalFile(db_->DefaultColumnFamily(), {external_file},
|
|
|
|
IngestExternalFileOptions()));
|
|
|
|
|
|
|
|
// Test ingest file without session_id and db_id (for example generated by an
|
|
|
|
// older version of sst_writer)
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(
|
|
|
|
"PropertyBlockBuilder::AddTableProperty:Start", [&](void* props_vs) {
|
|
|
|
auto props = static_cast<TableProperties*>(props_vs);
|
|
|
|
// update table property session_id to a different one
|
|
|
|
props->db_session_id = "";
|
|
|
|
props->db_id = "";
|
|
|
|
});
|
Always verify SST unique IDs on SST file open (#10532)
Summary:
Although we've been tracking SST unique IDs in the DB manifest
unconditionally, checking has been opt-in and with an extra pass at DB::Open
time. This changes the behavior of `verify_sst_unique_id_in_manifest` to
check unique ID against manifest every time an SST file is opened through
table cache (normal DB operations), replacing the explicit pass over files
at DB::Open time. This change also enables the option by default and
removes the "EXPERIMENTAL" designation.
One possible criticism is that the option no longer ensures the integrity
of a DB at Open time. This is far from an all-or-nothing issue. Verifying
the IDs of all SST files hardly ensures all the data in the DB is readable.
(VerifyChecksum is supposed to do that.) Also, with
max_open_files=-1 (default, extremely common), all SST files are
opened at DB::Open time anyway.
Implementation details:
* `VerifySstUniqueIdInManifest()` functions are the extra/explicit pass
that is now removed.
* Unit tests that manipulate/corrupt table properties have to opt out of
this check, because that corrupts the "actual" unique id. (And even for
testing we don't currently have a mechanism to set "no unique id"
in the in-memory file metadata for new files.)
* A lot of other unit test churn relates to (a) default checking on, and
(b) checking on SST open even without DB::Open (e.g. on flush)
* Use `FileMetaData` for more `TableCache` operations (in place of
`FileDescriptor`) so that we have access to the unique_id whenever
we might need to open an SST file. **There is the possibility of
performance impact because we can no longer use the more
localized `fd` part of an `FdWithKeyRange` but instead follow the
`file_metadata` pointer. However, this change (possible regression)
is only done for `GetMemoryUsageByTableReaders`.**
* Removed a completely unnecessary constructor overload of
`TableReaderOptions`
Possible follow-up:
* Verification only happens when opening through table cache. Are there
more places where this should happen?
* Improve error message when there is a file size mismatch vs. manifest
(FIXME added in the appropriate place).
* I'm not sure there's a justification for `FileDescriptor` to be distinct from
`FileMetaData`.
* I'm skeptical that `FdWithKeyRange` really still makes sense for
optimizing some data locality by duplicating some data in memory, but I
could be wrong.
* An unnecessary overload of NewTableReader was recently added, in
the public API nonetheless (though unusable there). It should be cleaned
up to put most things under `TableReaderOptions`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/10532
Test Plan:
updated unit tests
Performance test showing no significant difference (just noise I think):
`./db_bench -benchmarks=readwhilewriting[-X10] -num=3000000 -disable_wal=1 -bloom_bits=8 -write_buffer_size=1000000 -target_file_size_base=1000000`
Before: readwhilewriting [AVG 10 runs] : 68702 (± 6932) ops/sec
After: readwhilewriting [AVG 10 runs] : 68239 (± 7198) ops/sec
Reviewed By: jay-zhuang
Differential Revision: D38765551
Pulled By: pdillinger
fbshipit-source-id: a827a708155f12344ab2a5c16e7701c7636da4c2
2 years ago
|
|
|
std::atomic_int skipped = 0, passed = 0;
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(
|
|
|
|
"BlockBasedTable::Open::SkippedVerifyUniqueId",
|
|
|
|
[&](void* /*arg*/) { skipped++; });
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(
|
|
|
|
"BlockBasedTable::Open::PassedVerifyUniqueId",
|
|
|
|
[&](void* /*arg*/) { passed++; });
|
|
|
|
SyncPoint::GetInstance()->EnableProcessing();
|
|
|
|
|
Always verify SST unique IDs on SST file open (#10532)
Summary:
Although we've been tracking SST unique IDs in the DB manifest
unconditionally, checking has been opt-in and with an extra pass at DB::Open
time. This changes the behavior of `verify_sst_unique_id_in_manifest` to
check unique ID against manifest every time an SST file is opened through
table cache (normal DB operations), replacing the explicit pass over files
at DB::Open time. This change also enables the option by default and
removes the "EXPERIMENTAL" designation.
One possible criticism is that the option no longer ensures the integrity
of a DB at Open time. This is far from an all-or-nothing issue. Verifying
the IDs of all SST files hardly ensures all the data in the DB is readable.
(VerifyChecksum is supposed to do that.) Also, with
max_open_files=-1 (default, extremely common), all SST files are
opened at DB::Open time anyway.
Implementation details:
* `VerifySstUniqueIdInManifest()` functions are the extra/explicit pass
that is now removed.
* Unit tests that manipulate/corrupt table properties have to opt out of
this check, because that corrupts the "actual" unique id. (And even for
testing we don't currently have a mechanism to set "no unique id"
in the in-memory file metadata for new files.)
* A lot of other unit test churn relates to (a) default checking on, and
(b) checking on SST open even without DB::Open (e.g. on flush)
* Use `FileMetaData` for more `TableCache` operations (in place of
`FileDescriptor`) so that we have access to the unique_id whenever
we might need to open an SST file. **There is the possibility of
performance impact because we can no longer use the more
localized `fd` part of an `FdWithKeyRange` but instead follow the
`file_metadata` pointer. However, this change (possible regression)
is only done for `GetMemoryUsageByTableReaders`.**
* Removed a completely unnecessary constructor overload of
`TableReaderOptions`
Possible follow-up:
* Verification only happens when opening through table cache. Are there
more places where this should happen?
* Improve error message when there is a file size mismatch vs. manifest
(FIXME added in the appropriate place).
* I'm not sure there's a justification for `FileDescriptor` to be distinct from
`FileMetaData`.
* I'm skeptical that `FdWithKeyRange` really still makes sense for
optimizing some data locality by duplicating some data in memory, but I
could be wrong.
* An unnecessary overload of NewTableReader was recently added, in
the public API nonetheless (though unusable there). It should be cleaned
up to put most things under `TableReaderOptions`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/10532
Test Plan:
updated unit tests
Performance test showing no significant difference (just noise I think):
`./db_bench -benchmarks=readwhilewriting[-X10] -num=3000000 -disable_wal=1 -bloom_bits=8 -write_buffer_size=1000000 -target_file_size_base=1000000`
Before: readwhilewriting [AVG 10 runs] : 68702 (± 6932) ops/sec
After: readwhilewriting [AVG 10 runs] : 68239 (± 7198) ops/sec
Reviewed By: jay-zhuang
Differential Revision: D38765551
Pulled By: pdillinger
fbshipit-source-id: a827a708155f12344ab2a5c16e7701c7636da4c2
2 years ago
|
|
|
auto options = CurrentOptions();
|
|
|
|
ASSERT_TRUE(options.verify_sst_unique_id_in_manifest);
|
|
|
|
Reopen(options);
|
|
|
|
ASSERT_EQ(skipped, 0);
|
|
|
|
ASSERT_EQ(passed, 2); // one flushed + one ingested
|
|
|
|
|
|
|
|
external_file = sst_files_dir_ + "/file_to_ingest2.sst";
|
|
|
|
{
|
|
|
|
SstFileWriter sst_file_writer{EnvOptions(), CurrentOptions()};
|
|
|
|
|
|
|
|
ASSERT_OK(sst_file_writer.Open(external_file));
|
|
|
|
ASSERT_OK(sst_file_writer.Put("k", kIngestedVal));
|
|
|
|
ASSERT_OK(sst_file_writer.Finish());
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT_OK(db_->IngestExternalFile(db_->DefaultColumnFamily(), {external_file},
|
|
|
|
IngestExternalFileOptions()));
|
|
|
|
|
Always verify SST unique IDs on SST file open (#10532)
Summary:
Although we've been tracking SST unique IDs in the DB manifest
unconditionally, checking has been opt-in and with an extra pass at DB::Open
time. This changes the behavior of `verify_sst_unique_id_in_manifest` to
check unique ID against manifest every time an SST file is opened through
table cache (normal DB operations), replacing the explicit pass over files
at DB::Open time. This change also enables the option by default and
removes the "EXPERIMENTAL" designation.
One possible criticism is that the option no longer ensures the integrity
of a DB at Open time. This is far from an all-or-nothing issue. Verifying
the IDs of all SST files hardly ensures all the data in the DB is readable.
(VerifyChecksum is supposed to do that.) Also, with
max_open_files=-1 (default, extremely common), all SST files are
opened at DB::Open time anyway.
Implementation details:
* `VerifySstUniqueIdInManifest()` functions are the extra/explicit pass
that is now removed.
* Unit tests that manipulate/corrupt table properties have to opt out of
this check, because that corrupts the "actual" unique id. (And even for
testing we don't currently have a mechanism to set "no unique id"
in the in-memory file metadata for new files.)
* A lot of other unit test churn relates to (a) default checking on, and
(b) checking on SST open even without DB::Open (e.g. on flush)
* Use `FileMetaData` for more `TableCache` operations (in place of
`FileDescriptor`) so that we have access to the unique_id whenever
we might need to open an SST file. **There is the possibility of
performance impact because we can no longer use the more
localized `fd` part of an `FdWithKeyRange` but instead follow the
`file_metadata` pointer. However, this change (possible regression)
is only done for `GetMemoryUsageByTableReaders`.**
* Removed a completely unnecessary constructor overload of
`TableReaderOptions`
Possible follow-up:
* Verification only happens when opening through table cache. Are there
more places where this should happen?
* Improve error message when there is a file size mismatch vs. manifest
(FIXME added in the appropriate place).
* I'm not sure there's a justification for `FileDescriptor` to be distinct from
`FileMetaData`.
* I'm skeptical that `FdWithKeyRange` really still makes sense for
optimizing some data locality by duplicating some data in memory, but I
could be wrong.
* An unnecessary overload of NewTableReader was recently added, in
the public API nonetheless (though unusable there). It should be cleaned
up to put most things under `TableReaderOptions`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/10532
Test Plan:
updated unit tests
Performance test showing no significant difference (just noise I think):
`./db_bench -benchmarks=readwhilewriting[-X10] -num=3000000 -disable_wal=1 -bloom_bits=8 -write_buffer_size=1000000 -target_file_size_base=1000000`
Before: readwhilewriting [AVG 10 runs] : 68702 (± 6932) ops/sec
After: readwhilewriting [AVG 10 runs] : 68239 (± 7198) ops/sec
Reviewed By: jay-zhuang
Differential Revision: D38765551
Pulled By: pdillinger
fbshipit-source-id: a827a708155f12344ab2a5c16e7701c7636da4c2
2 years ago
|
|
|
// Two table file opens skipping verification:
|
|
|
|
// * ExternalSstFileIngestionJob::GetIngestedFileInfo
|
|
|
|
// * TableCache::GetTableReader
|
|
|
|
ASSERT_EQ(skipped, 2);
|
|
|
|
ASSERT_EQ(passed, 2);
|
|
|
|
|
|
|
|
// Check same after re-open (except no GetIngestedFileInfo)
|
|
|
|
skipped = 0;
|
|
|
|
passed = 0;
|
|
|
|
Reopen(options);
|
|
|
|
ASSERT_EQ(skipped, 1);
|
Always verify SST unique IDs on SST file open (#10532)
Summary:
Although we've been tracking SST unique IDs in the DB manifest
unconditionally, checking has been opt-in and with an extra pass at DB::Open
time. This changes the behavior of `verify_sst_unique_id_in_manifest` to
check unique ID against manifest every time an SST file is opened through
table cache (normal DB operations), replacing the explicit pass over files
at DB::Open time. This change also enables the option by default and
removes the "EXPERIMENTAL" designation.
One possible criticism is that the option no longer ensures the integrity
of a DB at Open time. This is far from an all-or-nothing issue. Verifying
the IDs of all SST files hardly ensures all the data in the DB is readable.
(VerifyChecksum is supposed to do that.) Also, with
max_open_files=-1 (default, extremely common), all SST files are
opened at DB::Open time anyway.
Implementation details:
* `VerifySstUniqueIdInManifest()` functions are the extra/explicit pass
that is now removed.
* Unit tests that manipulate/corrupt table properties have to opt out of
this check, because that corrupts the "actual" unique id. (And even for
testing we don't currently have a mechanism to set "no unique id"
in the in-memory file metadata for new files.)
* A lot of other unit test churn relates to (a) default checking on, and
(b) checking on SST open even without DB::Open (e.g. on flush)
* Use `FileMetaData` for more `TableCache` operations (in place of
`FileDescriptor`) so that we have access to the unique_id whenever
we might need to open an SST file. **There is the possibility of
performance impact because we can no longer use the more
localized `fd` part of an `FdWithKeyRange` but instead follow the
`file_metadata` pointer. However, this change (possible regression)
is only done for `GetMemoryUsageByTableReaders`.**
* Removed a completely unnecessary constructor overload of
`TableReaderOptions`
Possible follow-up:
* Verification only happens when opening through table cache. Are there
more places where this should happen?
* Improve error message when there is a file size mismatch vs. manifest
(FIXME added in the appropriate place).
* I'm not sure there's a justification for `FileDescriptor` to be distinct from
`FileMetaData`.
* I'm skeptical that `FdWithKeyRange` really still makes sense for
optimizing some data locality by duplicating some data in memory, but I
could be wrong.
* An unnecessary overload of NewTableReader was recently added, in
the public API nonetheless (though unusable there). It should be cleaned
up to put most things under `TableReaderOptions`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/10532
Test Plan:
updated unit tests
Performance test showing no significant difference (just noise I think):
`./db_bench -benchmarks=readwhilewriting[-X10] -num=3000000 -disable_wal=1 -bloom_bits=8 -write_buffer_size=1000000 -target_file_size_base=1000000`
Before: readwhilewriting [AVG 10 runs] : 68702 (± 6932) ops/sec
After: readwhilewriting [AVG 10 runs] : 68239 (± 7198) ops/sec
Reviewed By: jay-zhuang
Differential Revision: D38765551
Pulled By: pdillinger
fbshipit-source-id: a827a708155f12344ab2a5c16e7701c7636da4c2
2 years ago
|
|
|
ASSERT_EQ(passed, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExternalSSTFileBasicTest, StableSnapshotWhileLoggingToManifest) {
|
|
|
|
const std::string kPutVal = "put_val";
|
|
|
|
const std::string kIngestedVal = "ingested_val";
|
|
|
|
|
|
|
|
ASSERT_OK(Put("k", kPutVal, WriteOptions()));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
|
|
|
|
std::string external_file = sst_files_dir_ + "/file_to_ingest.sst";
|
|
|
|
{
|
|
|
|
SstFileWriter sst_file_writer{EnvOptions(), CurrentOptions()};
|
|
|
|
ASSERT_OK(sst_file_writer.Open(external_file));
|
|
|
|
ASSERT_OK(sst_file_writer.Put("k", kIngestedVal));
|
|
|
|
ASSERT_OK(sst_file_writer.Finish());
|
|
|
|
}
|
|
|
|
|
|
|
|
const Snapshot* snapshot = nullptr;
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
|
|
|
"VersionSet::LogAndApply:WriteManifest", [&](void* /* arg */) {
|
|
|
|
// prevent background compaction job to call this callback
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
|
|
|
snapshot = db_->GetSnapshot();
|
|
|
|
ReadOptions read_opts;
|
|
|
|
read_opts.snapshot = snapshot;
|
|
|
|
std::string value;
|
|
|
|
ASSERT_OK(db_->Get(read_opts, "k", &value));
|
|
|
|
ASSERT_EQ(kPutVal, value);
|
|
|
|
});
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
|
|
|
|
|
|
|
ASSERT_OK(db_->IngestExternalFile(db_->DefaultColumnFamily(), {external_file},
|
|
|
|
IngestExternalFileOptions()));
|
|
|
|
auto ingested_file_seqno = db_->GetLatestSequenceNumber();
|
|
|
|
ASSERT_NE(nullptr, snapshot);
|
|
|
|
// snapshot is taken before SST ingestion is done
|
|
|
|
ASSERT_EQ(ingested_file_seqno, snapshot->GetSequenceNumber() + 1);
|
|
|
|
|
|
|
|
ReadOptions read_opts;
|
|
|
|
read_opts.snapshot = snapshot;
|
|
|
|
std::string value;
|
|
|
|
ASSERT_OK(db_->Get(read_opts, "k", &value));
|
|
|
|
ASSERT_EQ(kPutVal, value);
|
|
|
|
db_->ReleaseSnapshot(snapshot);
|
|
|
|
|
|
|
|
// After reopen, sequence number should be up current such that
|
|
|
|
// ingested value is read
|
|
|
|
Reopen(CurrentOptions());
|
|
|
|
ASSERT_OK(db_->Get(ReadOptions(), "k", &value));
|
|
|
|
ASSERT_EQ(kIngestedVal, value);
|
|
|
|
|
|
|
|
// New write should get higher seqno compared to ingested file
|
|
|
|
ASSERT_OK(Put("k", kPutVal, WriteOptions()));
|
|
|
|
ASSERT_EQ(db_->GetLatestSequenceNumber(), ingested_file_seqno + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_CASE_P(ExternalSSTFileBasicTest, ExternalSSTFileBasicTest,
|
|
|
|
testing::Values(std::make_tuple(true, true),
|
|
|
|
std::make_tuple(true, false),
|
|
|
|
std::make_tuple(false, true),
|
|
|
|
std::make_tuple(false, false)));
|
|
|
|
|
|
|
|
#endif // ROCKSDB_LITE
|
|
|
|
|
|
|
|
} // namespace ROCKSDB_NAMESPACE
|
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
|
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
|
|
RegisterCustomObjects(argc, argv);
|
|
|
|
return RUN_ALL_TESTS();
|
|
|
|
}
|