2016-03-18 22:18:42 +00:00
|
|
|
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
|
2017-07-15 23:03:42 +00:00
|
|
|
// 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).
|
2016-03-18 22:18:42 +00:00
|
|
|
|
2021-07-28 23:43:16 +00:00
|
|
|
#include "rocksdb/options.h"
|
2017-02-15 19:16:34 +00:00
|
|
|
#ifndef ROCKSDB_LITE
|
|
|
|
|
2016-03-18 22:18:42 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
2019-05-31 18:52:59 +00:00
|
|
|
#include "db/db_impl/db_impl.h"
|
2016-03-18 22:18:42 +00:00
|
|
|
#include "db/db_test_util.h"
|
2019-05-30 03:44:08 +00:00
|
|
|
#include "file/file_util.h"
|
2016-06-24 23:29:43 +00:00
|
|
|
#include "rocksdb/comparator.h"
|
2016-03-18 22:18:42 +00:00
|
|
|
#include "rocksdb/db.h"
|
|
|
|
#include "rocksdb/transaction_log.h"
|
2022-05-24 16:00:06 +00:00
|
|
|
#include "table/unique_id_impl.h"
|
2016-06-24 20:12:13 +00:00
|
|
|
#include "util/string_util.h"
|
2016-03-18 22:18:42 +00:00
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
namespace ROCKSDB_NAMESPACE {
|
2016-03-18 22:18:42 +00:00
|
|
|
|
2017-02-16 18:25:06 +00:00
|
|
|
#ifndef ROCKSDB_LITE
|
2016-03-18 22:18:42 +00:00
|
|
|
class RepairTest : public DBTestBase {
|
|
|
|
public:
|
2021-07-23 15:37:27 +00:00
|
|
|
RepairTest() : DBTestBase("repair_test", /*env_do_fsync=*/true) {}
|
2016-03-18 22:18:42 +00:00
|
|
|
|
2020-09-29 23:28:42 +00:00
|
|
|
Status GetFirstSstPath(std::string* first_sst_path) {
|
|
|
|
assert(first_sst_path != nullptr);
|
|
|
|
first_sst_path->clear();
|
2016-03-18 22:18:42 +00:00
|
|
|
uint64_t manifest_size;
|
|
|
|
std::vector<std::string> files;
|
2020-09-29 23:28:42 +00:00
|
|
|
Status s = db_->GetLiveFiles(files, &manifest_size);
|
|
|
|
if (s.ok()) {
|
|
|
|
auto sst_iter =
|
|
|
|
std::find_if(files.begin(), files.end(), [](const std::string& file) {
|
|
|
|
uint64_t number;
|
|
|
|
FileType type;
|
|
|
|
bool ok = ParseFileName(file, &number, &type);
|
|
|
|
return ok && type == kTableFile;
|
|
|
|
});
|
|
|
|
*first_sst_path = sst_iter == files.end() ? "" : dbname_ + *sst_iter;
|
|
|
|
}
|
|
|
|
return s;
|
2016-03-18 22:18:42 +00:00
|
|
|
}
|
2022-05-19 18:04:21 +00:00
|
|
|
|
|
|
|
void ReopenWithSstIdVerify() {
|
|
|
|
std::atomic_int verify_passed{0};
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(
|
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
2022-09-08 05:52:42 +00:00
|
|
|
"BlockBasedTable::Open::PassedVerifyUniqueId", [&](void* arg) {
|
2022-05-19 18:04:21 +00:00
|
|
|
// override job status
|
2022-05-24 16:00:06 +00:00
|
|
|
auto id = static_cast<UniqueId64x2*>(arg);
|
|
|
|
assert(*id != kNullUniqueId64x2);
|
2022-05-19 18:04:21 +00:00
|
|
|
verify_passed++;
|
|
|
|
});
|
|
|
|
SyncPoint::GetInstance()->EnableProcessing();
|
|
|
|
auto options = CurrentOptions();
|
|
|
|
options.verify_sst_unique_id_in_manifest = true;
|
|
|
|
Reopen(options);
|
|
|
|
|
|
|
|
ASSERT_GT(verify_passed, 0);
|
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
2022-09-08 05:52:42 +00:00
|
|
|
SyncPoint::GetInstance()->DisableProcessing();
|
2022-05-19 18:04:21 +00:00
|
|
|
}
|
2016-03-18 22:18:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(RepairTest, LostManifest) {
|
|
|
|
// Add a couple SST files, delete the manifest, and verify RepairDB() saves
|
|
|
|
// the day.
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_OK(Put("key", "val"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
ASSERT_OK(Put("key2", "val2"));
|
|
|
|
ASSERT_OK(Flush());
|
2016-03-18 22:18:42 +00:00
|
|
|
// Need to get path before Close() deletes db_, but delete it after Close() to
|
|
|
|
// ensure Close() didn't change the manifest.
|
|
|
|
std::string manifest_path =
|
|
|
|
DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
|
|
|
|
|
|
|
|
Close();
|
|
|
|
ASSERT_OK(env_->FileExists(manifest_path));
|
|
|
|
ASSERT_OK(env_->DeleteFile(manifest_path));
|
2016-06-24 18:19:40 +00:00
|
|
|
ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
|
2022-05-19 18:04:21 +00:00
|
|
|
ReopenWithSstIdVerify();
|
2016-03-18 22:18:42 +00:00
|
|
|
|
|
|
|
ASSERT_EQ(Get("key"), "val");
|
|
|
|
ASSERT_EQ(Get("key2"), "val2");
|
|
|
|
}
|
|
|
|
|
2021-07-28 23:43:16 +00:00
|
|
|
TEST_F(RepairTest, LostManifestMoreDbFeatures) {
|
|
|
|
// Add a couple SST files, delete the manifest, and verify RepairDB() saves
|
|
|
|
// the day.
|
|
|
|
ASSERT_OK(Put("key", "val"));
|
|
|
|
ASSERT_OK(Put("key2", "val2"));
|
|
|
|
ASSERT_OK(Put("key3", "val3"));
|
|
|
|
ASSERT_OK(Put("key4", "val4"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
// Test an SST file containing only a range tombstone
|
|
|
|
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "key2",
|
|
|
|
"key3z"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
// Need to get path before Close() deletes db_, but delete it after Close() to
|
|
|
|
// ensure Close() didn't change the manifest.
|
|
|
|
std::string manifest_path =
|
|
|
|
DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
|
|
|
|
|
|
|
|
Close();
|
|
|
|
ASSERT_OK(env_->FileExists(manifest_path));
|
|
|
|
ASSERT_OK(env_->DeleteFile(manifest_path));
|
|
|
|
ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
|
2022-05-19 18:04:21 +00:00
|
|
|
|
|
|
|
// repair from sst should work with unique_id verification
|
|
|
|
ReopenWithSstIdVerify();
|
2021-07-28 23:43:16 +00:00
|
|
|
|
|
|
|
ASSERT_EQ(Get("key"), "val");
|
|
|
|
ASSERT_EQ(Get("key2"), "NOT_FOUND");
|
|
|
|
ASSERT_EQ(Get("key3"), "NOT_FOUND");
|
|
|
|
ASSERT_EQ(Get("key4"), "val4");
|
|
|
|
}
|
|
|
|
|
2016-03-18 22:18:42 +00:00
|
|
|
TEST_F(RepairTest, CorruptManifest) {
|
|
|
|
// Manifest is in an invalid format. Expect a full recovery.
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_OK(Put("key", "val"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
ASSERT_OK(Put("key2", "val2"));
|
|
|
|
ASSERT_OK(Flush());
|
2016-03-18 22:18:42 +00:00
|
|
|
// Need to get path before Close() deletes db_, but overwrite it after Close()
|
|
|
|
// to ensure Close() didn't change the manifest.
|
|
|
|
std::string manifest_path =
|
|
|
|
DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
|
|
|
|
|
|
|
|
Close();
|
|
|
|
ASSERT_OK(env_->FileExists(manifest_path));
|
Introduce a new storage specific Env API (#5761)
Summary:
The current Env API encompasses both storage/file operations, as well as OS related operations. Most of the APIs return a Status, which does not have enough metadata about an error, such as whether its retry-able or not, scope (i.e fault domain) of the error etc., that may be required in order to properly handle a storage error. The file APIs also do not provide enough control over the IO SLA, such as timeout, prioritization, hinting about placement and redundancy etc.
This PR separates out the file/storage APIs from Env into a new FileSystem class. The APIs are updated to return an IOStatus with metadata about the error, as well as to take an IOOptions structure as input in order to allow more control over the IO.
The user can set both ```options.env``` and ```options.file_system``` to specify that RocksDB should use the former for OS related operations and the latter for storage operations. Internally, a ```CompositeEnvWrapper``` has been introduced that inherits from ```Env``` and redirects individual methods to either an ```Env``` implementation or the ```FileSystem``` as appropriate. When options are sanitized during ```DB::Open```, ```options.env``` is replaced with a newly allocated ```CompositeEnvWrapper``` instance if both env and file_system have been specified. This way, the rest of the RocksDB code can continue to function as before.
This PR also ports PosixEnv to the new API by splitting it into two - PosixEnv and PosixFileSystem. PosixEnv is defined as a sub-class of CompositeEnvWrapper, and threading/time functions are overridden with Posix specific implementations in order to avoid an extra level of indirection.
The ```CompositeEnvWrapper``` translates ```IOStatus``` return code to ```Status```, and sets the severity to ```kSoftError``` if the io_status is retryable. The error handling code in RocksDB can then recover the DB automatically.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/5761
Differential Revision: D18868376
Pulled By: anand1976
fbshipit-source-id: 39efe18a162ea746fabac6360ff529baba48486f
2019-12-13 22:47:08 +00:00
|
|
|
|
2021-01-06 18:48:24 +00:00
|
|
|
ASSERT_OK(CreateFile(env_->GetFileSystem(), manifest_path, "blah",
|
|
|
|
false /* use_fsync */));
|
2016-06-24 18:19:40 +00:00
|
|
|
ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
|
2022-05-19 18:04:21 +00:00
|
|
|
|
|
|
|
ReopenWithSstIdVerify();
|
2016-03-18 22:18:42 +00:00
|
|
|
|
|
|
|
ASSERT_EQ(Get("key"), "val");
|
|
|
|
ASSERT_EQ(Get("key2"), "val2");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(RepairTest, IncompleteManifest) {
|
|
|
|
// In this case, the manifest is valid but does not reference all of the SST
|
|
|
|
// files. Expect a full recovery.
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_OK(Put("key", "val"));
|
|
|
|
ASSERT_OK(Flush());
|
2016-03-18 22:18:42 +00:00
|
|
|
std::string orig_manifest_path =
|
|
|
|
DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
|
|
|
|
CopyFile(orig_manifest_path, orig_manifest_path + ".tmp");
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_OK(Put("key2", "val2"));
|
|
|
|
ASSERT_OK(Flush());
|
2016-03-18 22:18:42 +00:00
|
|
|
// Need to get path before Close() deletes db_, but overwrite it after Close()
|
|
|
|
// to ensure Close() didn't change the manifest.
|
|
|
|
std::string new_manifest_path =
|
|
|
|
DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
|
|
|
|
|
|
|
|
Close();
|
|
|
|
ASSERT_OK(env_->FileExists(new_manifest_path));
|
|
|
|
// Replace the manifest with one that is only aware of the first SST file.
|
|
|
|
CopyFile(orig_manifest_path + ".tmp", new_manifest_path);
|
2016-06-24 18:19:40 +00:00
|
|
|
ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
|
2022-05-19 18:04:21 +00:00
|
|
|
|
|
|
|
ReopenWithSstIdVerify();
|
2016-03-18 22:18:42 +00:00
|
|
|
|
|
|
|
ASSERT_EQ(Get("key"), "val");
|
|
|
|
ASSERT_EQ(Get("key2"), "val2");
|
|
|
|
}
|
|
|
|
|
2017-10-10 20:07:00 +00:00
|
|
|
TEST_F(RepairTest, PostRepairSstFileNumbering) {
|
|
|
|
// Verify after a DB is repaired, new files will be assigned higher numbers
|
|
|
|
// than old files.
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_OK(Put("key", "val"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
ASSERT_OK(Put("key2", "val2"));
|
|
|
|
ASSERT_OK(Flush());
|
2017-10-10 20:07:00 +00:00
|
|
|
uint64_t pre_repair_file_num = dbfull()->TEST_Current_Next_FileNo();
|
|
|
|
Close();
|
|
|
|
|
|
|
|
ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
|
|
|
|
|
2022-05-19 18:04:21 +00:00
|
|
|
ReopenWithSstIdVerify();
|
|
|
|
|
2017-10-10 20:07:00 +00:00
|
|
|
uint64_t post_repair_file_num = dbfull()->TEST_Current_Next_FileNo();
|
|
|
|
ASSERT_GE(post_repair_file_num, pre_repair_file_num);
|
|
|
|
}
|
|
|
|
|
2016-03-18 22:18:42 +00:00
|
|
|
TEST_F(RepairTest, LostSst) {
|
|
|
|
// Delete one of the SST files but preserve the manifest that refers to it,
|
|
|
|
// then verify the DB is still usable for the intact SST.
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_OK(Put("key", "val"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
ASSERT_OK(Put("key2", "val2"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
std::string sst_path;
|
|
|
|
ASSERT_OK(GetFirstSstPath(&sst_path));
|
2016-03-18 22:18:42 +00:00
|
|
|
ASSERT_FALSE(sst_path.empty());
|
|
|
|
ASSERT_OK(env_->DeleteFile(sst_path));
|
|
|
|
|
|
|
|
Close();
|
2016-06-24 18:19:40 +00:00
|
|
|
ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
|
2022-05-19 18:04:21 +00:00
|
|
|
ReopenWithSstIdVerify();
|
2016-03-18 22:18:42 +00:00
|
|
|
|
|
|
|
// Exactly one of the key-value pairs should be in the DB now.
|
|
|
|
ASSERT_TRUE((Get("key") == "val") != (Get("key2") == "val2"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(RepairTest, CorruptSst) {
|
|
|
|
// Corrupt one of the SST files but preserve the manifest that refers to it,
|
|
|
|
// then verify the DB is still usable for the intact SST.
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_OK(Put("key", "val"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
ASSERT_OK(Put("key2", "val2"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
std::string sst_path;
|
|
|
|
ASSERT_OK(GetFirstSstPath(&sst_path));
|
2016-03-18 22:18:42 +00:00
|
|
|
ASSERT_FALSE(sst_path.empty());
|
Introduce a new storage specific Env API (#5761)
Summary:
The current Env API encompasses both storage/file operations, as well as OS related operations. Most of the APIs return a Status, which does not have enough metadata about an error, such as whether its retry-able or not, scope (i.e fault domain) of the error etc., that may be required in order to properly handle a storage error. The file APIs also do not provide enough control over the IO SLA, such as timeout, prioritization, hinting about placement and redundancy etc.
This PR separates out the file/storage APIs from Env into a new FileSystem class. The APIs are updated to return an IOStatus with metadata about the error, as well as to take an IOOptions structure as input in order to allow more control over the IO.
The user can set both ```options.env``` and ```options.file_system``` to specify that RocksDB should use the former for OS related operations and the latter for storage operations. Internally, a ```CompositeEnvWrapper``` has been introduced that inherits from ```Env``` and redirects individual methods to either an ```Env``` implementation or the ```FileSystem``` as appropriate. When options are sanitized during ```DB::Open```, ```options.env``` is replaced with a newly allocated ```CompositeEnvWrapper``` instance if both env and file_system have been specified. This way, the rest of the RocksDB code can continue to function as before.
This PR also ports PosixEnv to the new API by splitting it into two - PosixEnv and PosixFileSystem. PosixEnv is defined as a sub-class of CompositeEnvWrapper, and threading/time functions are overridden with Posix specific implementations in order to avoid an extra level of indirection.
The ```CompositeEnvWrapper``` translates ```IOStatus``` return code to ```Status```, and sets the severity to ```kSoftError``` if the io_status is retryable. The error handling code in RocksDB can then recover the DB automatically.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/5761
Differential Revision: D18868376
Pulled By: anand1976
fbshipit-source-id: 39efe18a162ea746fabac6360ff529baba48486f
2019-12-13 22:47:08 +00:00
|
|
|
|
2021-01-06 18:48:24 +00:00
|
|
|
ASSERT_OK(CreateFile(env_->GetFileSystem(), sst_path, "blah",
|
|
|
|
false /* use_fsync */));
|
2016-03-18 22:18:42 +00:00
|
|
|
|
|
|
|
Close();
|
2016-06-24 18:19:40 +00:00
|
|
|
ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
|
2022-05-19 18:04:21 +00:00
|
|
|
ReopenWithSstIdVerify();
|
2016-03-18 22:18:42 +00:00
|
|
|
|
|
|
|
// Exactly one of the key-value pairs should be in the DB now.
|
|
|
|
ASSERT_TRUE((Get("key") == "val") != (Get("key2") == "val2"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(RepairTest, UnflushedSst) {
|
|
|
|
// This test case invokes repair while some data is unflushed, then verifies
|
|
|
|
// that data is in the db.
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_OK(Put("key", "val"));
|
2016-03-18 22:18:42 +00:00
|
|
|
VectorLogPtr wal_files;
|
|
|
|
ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
|
|
|
|
ASSERT_EQ(wal_files.size(), 1);
|
2020-09-29 23:28:42 +00:00
|
|
|
{
|
|
|
|
uint64_t total_ssts_size;
|
|
|
|
std::unordered_map<std::string, uint64_t> sst_files;
|
2021-03-18 03:43:22 +00:00
|
|
|
ASSERT_OK(GetAllDataFiles(kTableFile, &sst_files, &total_ssts_size));
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_EQ(total_ssts_size, 0);
|
|
|
|
}
|
2016-03-18 22:18:42 +00:00
|
|
|
// Need to get path before Close() deletes db_, but delete it after Close() to
|
|
|
|
// ensure Close() didn't change the manifest.
|
|
|
|
std::string manifest_path =
|
|
|
|
DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
|
|
|
|
|
|
|
|
Close();
|
|
|
|
ASSERT_OK(env_->FileExists(manifest_path));
|
|
|
|
ASSERT_OK(env_->DeleteFile(manifest_path));
|
2016-06-24 18:19:40 +00:00
|
|
|
ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
|
2022-05-19 18:04:21 +00:00
|
|
|
ReopenWithSstIdVerify();
|
2016-03-18 22:18:42 +00:00
|
|
|
|
|
|
|
ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
|
|
|
|
ASSERT_EQ(wal_files.size(), 0);
|
2020-09-29 23:28:42 +00:00
|
|
|
{
|
|
|
|
uint64_t total_ssts_size;
|
|
|
|
std::unordered_map<std::string, uint64_t> sst_files;
|
2021-03-18 03:43:22 +00:00
|
|
|
ASSERT_OK(GetAllDataFiles(kTableFile, &sst_files, &total_ssts_size));
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_GT(total_ssts_size, 0);
|
|
|
|
}
|
2016-03-18 22:18:42 +00:00
|
|
|
ASSERT_EQ(Get("key"), "val");
|
|
|
|
}
|
|
|
|
|
2017-08-08 17:42:38 +00:00
|
|
|
TEST_F(RepairTest, SeparateWalDir) {
|
|
|
|
do {
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
DestroyAndReopen(options);
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_OK(Put("key", "val"));
|
|
|
|
ASSERT_OK(Put("foo", "bar"));
|
2017-08-08 17:42:38 +00:00
|
|
|
VectorLogPtr wal_files;
|
|
|
|
ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
|
|
|
|
ASSERT_EQ(wal_files.size(), 1);
|
2020-09-29 23:28:42 +00:00
|
|
|
{
|
|
|
|
uint64_t total_ssts_size;
|
|
|
|
std::unordered_map<std::string, uint64_t> sst_files;
|
2021-03-18 03:43:22 +00:00
|
|
|
ASSERT_OK(GetAllDataFiles(kTableFile, &sst_files, &total_ssts_size));
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_EQ(total_ssts_size, 0);
|
|
|
|
}
|
2017-08-08 17:42:38 +00:00
|
|
|
std::string manifest_path =
|
|
|
|
DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
|
|
|
|
|
|
|
|
Close();
|
|
|
|
ASSERT_OK(env_->FileExists(manifest_path));
|
|
|
|
ASSERT_OK(env_->DeleteFile(manifest_path));
|
|
|
|
ASSERT_OK(RepairDB(dbname_, options));
|
|
|
|
|
|
|
|
// make sure that all WALs are converted to SSTables.
|
|
|
|
options.wal_dir = "";
|
|
|
|
|
2022-05-19 18:04:21 +00:00
|
|
|
ReopenWithSstIdVerify();
|
2017-08-08 17:42:38 +00:00
|
|
|
ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
|
|
|
|
ASSERT_EQ(wal_files.size(), 0);
|
2020-09-29 23:28:42 +00:00
|
|
|
{
|
|
|
|
uint64_t total_ssts_size;
|
|
|
|
std::unordered_map<std::string, uint64_t> sst_files;
|
2021-03-18 03:43:22 +00:00
|
|
|
ASSERT_OK(GetAllDataFiles(kTableFile, &sst_files, &total_ssts_size));
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_GT(total_ssts_size, 0);
|
|
|
|
}
|
2017-08-08 17:42:38 +00:00
|
|
|
ASSERT_EQ(Get("key"), "val");
|
|
|
|
ASSERT_EQ(Get("foo"), "bar");
|
|
|
|
|
|
|
|
} while(ChangeWalOptions());
|
|
|
|
}
|
|
|
|
|
2016-06-24 20:12:13 +00:00
|
|
|
TEST_F(RepairTest, RepairMultipleColumnFamilies) {
|
|
|
|
// Verify repair logic associates SST files with their original column
|
|
|
|
// families.
|
|
|
|
const int kNumCfs = 3;
|
|
|
|
const int kEntriesPerCf = 2;
|
|
|
|
DestroyAndReopen(CurrentOptions());
|
|
|
|
CreateAndReopenWithCF({"pikachu1", "pikachu2"}, CurrentOptions());
|
|
|
|
for (int i = 0; i < kNumCfs; ++i) {
|
|
|
|
for (int j = 0; j < kEntriesPerCf; ++j) {
|
2022-05-06 20:03:58 +00:00
|
|
|
ASSERT_OK(Put(i, "key" + std::to_string(j), "val" + std::to_string(j)));
|
2016-06-24 20:12:13 +00:00
|
|
|
if (j == kEntriesPerCf - 1 && i == kNumCfs - 1) {
|
|
|
|
// Leave one unflushed so we can verify WAL entries are properly
|
|
|
|
// associated with column families.
|
|
|
|
continue;
|
|
|
|
}
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_OK(Flush(i));
|
2016-06-24 20:12:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Need to get path before Close() deletes db_, but delete it after Close() to
|
|
|
|
// ensure Close() doesn't re-create the manifest.
|
|
|
|
std::string manifest_path =
|
|
|
|
DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
|
|
|
|
Close();
|
|
|
|
ASSERT_OK(env_->FileExists(manifest_path));
|
|
|
|
ASSERT_OK(env_->DeleteFile(manifest_path));
|
|
|
|
|
|
|
|
ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
|
|
|
|
|
|
|
|
ReopenWithColumnFamilies({"default", "pikachu1", "pikachu2"},
|
|
|
|
CurrentOptions());
|
|
|
|
for (int i = 0; i < kNumCfs; ++i) {
|
|
|
|
for (int j = 0; j < kEntriesPerCf; ++j) {
|
2022-05-06 20:03:58 +00:00
|
|
|
ASSERT_EQ(Get(i, "key" + std::to_string(j)), "val" + std::to_string(j));
|
2016-06-24 20:12:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-24 23:29:43 +00:00
|
|
|
TEST_F(RepairTest, RepairColumnFamilyOptions) {
|
|
|
|
// Verify repair logic uses correct ColumnFamilyOptions when repairing a
|
|
|
|
// database with different options for column families.
|
|
|
|
const int kNumCfs = 2;
|
|
|
|
const int kEntriesPerCf = 2;
|
|
|
|
|
|
|
|
Options opts(CurrentOptions()), rev_opts(CurrentOptions());
|
|
|
|
opts.comparator = BytewiseComparator();
|
|
|
|
rev_opts.comparator = ReverseBytewiseComparator();
|
|
|
|
|
|
|
|
DestroyAndReopen(opts);
|
|
|
|
CreateColumnFamilies({"reverse"}, rev_opts);
|
|
|
|
ReopenWithColumnFamilies({"default", "reverse"},
|
|
|
|
std::vector<Options>{opts, rev_opts});
|
|
|
|
for (int i = 0; i < kNumCfs; ++i) {
|
|
|
|
for (int j = 0; j < kEntriesPerCf; ++j) {
|
2022-05-06 20:03:58 +00:00
|
|
|
ASSERT_OK(Put(i, "key" + std::to_string(j), "val" + std::to_string(j)));
|
2016-06-24 23:29:43 +00:00
|
|
|
if (i == kNumCfs - 1 && j == kEntriesPerCf - 1) {
|
|
|
|
// Leave one unflushed so we can verify RepairDB's flush logic
|
|
|
|
continue;
|
|
|
|
}
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_OK(Flush(i));
|
2016-06-24 23:29:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Close();
|
|
|
|
|
|
|
|
// RepairDB() records the comparator in the manifest, and DB::Open would fail
|
|
|
|
// if a different comparator were used.
|
|
|
|
ASSERT_OK(RepairDB(dbname_, opts, {{"default", opts}, {"reverse", rev_opts}},
|
|
|
|
opts /* unknown_cf_opts */));
|
|
|
|
ASSERT_OK(TryReopenWithColumnFamilies({"default", "reverse"},
|
|
|
|
std::vector<Options>{opts, rev_opts}));
|
|
|
|
for (int i = 0; i < kNumCfs; ++i) {
|
|
|
|
for (int j = 0; j < kEntriesPerCf; ++j) {
|
2022-05-06 20:03:58 +00:00
|
|
|
ASSERT_EQ(Get(i, "key" + std::to_string(j)), "val" + std::to_string(j));
|
2016-06-24 23:29:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Examine table properties to verify RepairDB() used the right options when
|
|
|
|
// converting WAL->SST
|
|
|
|
TablePropertiesCollection fname_to_props;
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_OK(db_->GetPropertiesOfAllTables(handles_[1], &fname_to_props));
|
2016-06-24 23:29:43 +00:00
|
|
|
ASSERT_EQ(fname_to_props.size(), 2U);
|
|
|
|
for (const auto& fname_and_props : fname_to_props) {
|
2022-07-12 20:30:35 +00:00
|
|
|
std::string comparator_name(rev_opts.comparator->Name());
|
2017-02-10 18:51:08 +00:00
|
|
|
ASSERT_EQ(comparator_name,
|
2016-06-24 23:29:43 +00:00
|
|
|
fname_and_props.second->comparator_name);
|
|
|
|
}
|
2018-10-12 17:40:06 +00:00
|
|
|
Close();
|
2016-06-24 23:29:43 +00:00
|
|
|
|
|
|
|
// Also check comparator when it's provided via "unknown" CF options
|
|
|
|
ASSERT_OK(RepairDB(dbname_, opts, {{"default", opts}},
|
|
|
|
rev_opts /* unknown_cf_opts */));
|
|
|
|
ASSERT_OK(TryReopenWithColumnFamilies({"default", "reverse"},
|
|
|
|
std::vector<Options>{opts, rev_opts}));
|
|
|
|
for (int i = 0; i < kNumCfs; ++i) {
|
|
|
|
for (int j = 0; j < kEntriesPerCf; ++j) {
|
2022-05-06 20:03:58 +00:00
|
|
|
ASSERT_EQ(Get(i, "key" + std::to_string(j)), "val" + std::to_string(j));
|
2016-06-24 23:29:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-16 18:25:06 +00:00
|
|
|
|
2017-09-22 19:37:59 +00:00
|
|
|
TEST_F(RepairTest, DbNameContainsTrailingSlash) {
|
|
|
|
{
|
|
|
|
bool tmp;
|
|
|
|
if (env_->AreFilesSame("", "", &tmp).IsNotSupported()) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"skipping RepairTest.DbNameContainsTrailingSlash due to "
|
|
|
|
"unsupported Env::AreFilesSame\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-29 23:28:42 +00:00
|
|
|
ASSERT_OK(Put("key", "val"));
|
|
|
|
ASSERT_OK(Flush());
|
2017-09-22 19:37:59 +00:00
|
|
|
Close();
|
|
|
|
|
|
|
|
ASSERT_OK(RepairDB(dbname_ + "/", CurrentOptions()));
|
2022-05-19 18:04:21 +00:00
|
|
|
ReopenWithSstIdVerify();
|
2017-09-22 19:37:59 +00:00
|
|
|
ASSERT_EQ(Get("key"), "val");
|
|
|
|
}
|
2017-02-16 18:25:06 +00:00
|
|
|
#endif // ROCKSDB_LITE
|
2020-02-20 20:07:53 +00:00
|
|
|
} // namespace ROCKSDB_NAMESPACE
|
2016-03-18 22:18:42 +00:00
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
|
|
return RUN_ALL_TESTS();
|
|
|
|
}
|
2017-02-15 19:16:34 +00:00
|
|
|
|
|
|
|
#else
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2018-04-16 00:19:57 +00:00
|
|
|
int main(int /*argc*/, char** /*argv*/) {
|
2017-02-15 19:16:34 +00:00
|
|
|
fprintf(stderr, "SKIPPED as RepairDB is not supported in ROCKSDB_LITE\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // ROCKSDB_LITE
|